.net 如何在视觉上连接两个圆圈?

slmsl1lt  于 2023-11-20  发布在  .NET
关注(0)|答案(3)|浏览(115)

我们知道2个圆的x和y中心位置,半径是相同的。我想直观地连接圆,而不是为连接2个圆中心的直线上的每个点循环绘制椭圆。
由此:


的数据
对此:



代码:

int radius = 75;

int x1 = 100;
int y1 = 200;

int x2 = 300;
int y2 = 100;

g.FillEllipse(Brushes.Blue, new Rectangle(x1 - radius / 2, y1 - radius / 2, radius, radius));
g.FillEllipse(Brushes.Blue, new Rectangle(x2 - radius / 2, y2 - radius / 2, radius, radius));

字符串

41zrol4v

41zrol4v1#

一个解决方案,当圆不具有相同的直径。
第一个需要的信息是两个圆的中心之间的距离。
为了计算它,我们使用应用于笛卡尔平面的Euclidean distance
x1c 0d1x的数据
其中(x1, y1)(x2, y2)是两个圆的圆心的坐标。
我们还需要知道方向(表示为正值或负值):计算出的[Distance]将始终为正值。
C# it中,它可以被编码为:

float Direction = (Circle1Center.X > Circle2Center.X) ? -1 : 1;
float Distance = (float)Math.Sqrt(Math.Pow(Circle1Center.X - Circle2Center.X, 2) + 
                                  Math.Pow(Circle1Center.Y - Circle2Center.Y, 2));
Distance *= Direction;

字符串
现在,我们有两个圆的中心之间的距离,它也表示方向。
我们还需要知道这条连接两个圆心的虚拟直线是如何相对于我们的绘图平面旋转的。在下图中,距离可以被看作是right triangleh = (A, B)的交叉使用。CAngular 由平行于轴的直线的交点确定,这些直线穿过圆心。
我们需要计算Angular θ (θ)
使用Pythagorean theorem,我们可以推导出Angular θ的正弦为Sinθ = b/h(如图所示)

使用圆的中心坐标,这可以在C#中编码为:
Distance是三角形的长度)

float SinTheta = (Math.Max(Circle1Center.Y, Circle2Center.Y) - 
                  Math.Min(Circle1Center.Y, Circle2Center.Y)) / Distance;


SinTheta表示Radians中的Angular 。我们需要Degrees中表示的Angular :Graphics对象使用此度量作为其世界变换函数。

float RotationAngle = (float)(Math.Asin(SinTheta) * (180 / Math.PI));


现在,我们需要构建一个 Connector,一个连接两个Circles的形状。我们需要一个Polygon;一个Rectangle不能有不同的边对(我们考虑的是直径不同的Circles)。
此多边形的长边=圆中心之间的距离,短边=圆直径。
要构建多边形,我们可以使用Graphics.DrawPolygonGraphicsPath.AddPolygon。我选择GraphicsPath方法,因为GraphicsPath可以容纳多个形状,并且这些形状可以在某种程度上相互作用。
要将2个考虑的圆与多边形连接起来,我们需要使用之前计算的RotationAngle旋转多边形。
执行旋转的一个简单方法是使用Graphics.TranslateTransform方法将世界坐标移动到其中一个圆的中心,然后使用Graphics.RotateTransform旋转新坐标。
我们需要绘制多边形,将其中一条短边(对应于圆的直径,即坐标变换的中心)定位在圆的中心。因此,当应用旋转时,它的短边将位于此变换的中间,锚定到中心。
这里,figure 3显示了多边形的位置(黄色形状)(好吧,它看起来像一个矩形,没关系);
figure 4中旋转后的同一多边形。


备注:

正如TaW指出的那样,这个绘图需要使用具有非透明颜色的SolidBrush来执行,这有点令人失望。
好吧,半透明的画笔是不禁止的,但重叠的形状将有不同的颜色,交叉点的透明颜色的总和。
然而,使用GraphicsPath的能力,使用应用于所有重叠部分的颜色来填充其形状,可以使用半透明的Brush绘制形状,而无需更改颜色。我们只需要更改默认值FillMode(请参见图中的示例),将其设置为FillMode.Winding

示例代码:

在这个例子中,两对圆被绘制在图形上下文中。然后它们被连接到一个多边形形状,使用GraphicsPath.AddPolygon()创建。
(Of当然,我们需要使用可绘制控件的Paint事件,这里是一个窗体)。
重载的helper函数接受Circles的中心位置,表示为PointFRectangleF结构,表示Circles边界。
这是使用全色和半透明画笔的视觉结果:

using System.Drawing;
using System.Drawing.Drawing2D;

private float Radius1 = 30f;
private float Radius2 = 50f;

private PointF Circle1Center = new PointF(220, 47);
private PointF Circle2Center = new PointF(72, 254);
private PointF Circle3Center = new PointF(52, 58);
private PointF Circle4Center = new PointF(217, 232);

private void form1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.CompositingQuality =  CompositingQuality.GammaCorrected;
    e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

    DrawLinkedCircles(Circle1Center, Circle2Center, Radius1, Radius2, Color.FromArgb(200, Color.YellowGreen), e.Graphics);
    DrawLinkedCircles(Circle3Center, Circle4Center, Radius1, Radius2, Color.FromArgb(200, Color.SteelBlue), e.Graphics);

    //OR, passing a RectangleF structure
    //RectangleF Circle1 = new RectangleF(Circle1Center.X - Radius1, Circle1Center.Y - Radius1, Radius1 * 2, Radius1 * 2);
    //RectangleF Circle2 = new RectangleF(Circle2Center.X - Radius2, Circle2Center.Y - Radius2, Radius2 * 2, Radius2 * 2);

    //DrawLinkedCircles(Circle1, Circle2, Color.FromArgb(200, Color.YellowGreen), e.Graphics);
}

辅助函数:

public void DrawLinkedCircles(RectangleF Circle1, RectangleF Circle2, Color FillColor, Graphics g)
{
    PointF Circle1Center = new PointF(Circle1.X + (Circle1.Width / 2), Circle1.Y + (Circle1.Height / 2));
    PointF Circle2Center = new PointF(Circle2.X + (Circle2.Width / 2), Circle2.Y + (Circle2.Height / 2));
    DrawLinkedCircles(Circle1Center, Circle2Center, Circle1.Width / 2, Circle2.Width / 2, FillColor, g);
}

public void DrawLinkedCircles(PointF Circle1Center, PointF Circle2Center, float Circle1Radius, float Circle2Radius, Color FillColor, Graphics g)
{
    float Direction = (Circle1Center.X > Circle2Center.X) ? -1 : 1;
    float Distance = (float)Math.Sqrt(Math.Pow(Circle1Center.X - Circle2Center.X, 2) +
                                      Math.Pow(Circle1Center.Y - Circle2Center.Y, 2));
    Distance *= Direction;

    float SinTheta = (Math.Max(Circle1Center.Y, Circle2Center.Y) -
                      Math.Min(Circle1Center.Y, Circle2Center.Y)) / Distance;

    float RotationDirection = (Circle1Center.Y > Circle2Center.Y) ? -1 : 1;
    float RotationAngle = (float)(Math.Asin(SinTheta) * (180 / Math.PI)) * RotationDirection;

    using (GraphicsPath path = new GraphicsPath(FillMode.Winding))
    {
        path.AddEllipse(new RectangleF(-Circle1Radius, -Circle1Radius, 2 * Circle1Radius, 2 * Circle1Radius));
        path.AddEllipse(new RectangleF(-Circle2Radius + (Math.Abs(Distance) * Direction),
                                       -Circle2Radius, 2 * Circle2Radius, 2 * Circle2Radius));
        path.AddPolygon(new[] {
            new PointF(0, -Circle1Radius),
            new PointF(0, Circle1Radius),
            new PointF(Distance, Circle2Radius),
            new PointF(Distance, -Circle2Radius),
        });
        path.AddEllipse(new RectangleF(-Circle1Radius, -Circle1Radius, 2 * Circle1Radius, 2 * Circle1Radius));
        path.AddEllipse(new RectangleF(-Circle2Radius + (Math.Abs(Distance) * Direction),
                                       -Circle2Radius, 2 * Circle2Radius, 2 * Circle2Radius));

        path.CloseAllFigures();

        g.TranslateTransform(Circle1Center.X, Circle1Center.Y);
        g.RotateTransform(RotationAngle);

        using (SolidBrush FillBrush = new SolidBrush(FillColor)) {
            g.FillPath(FillBrush, path);
        }
        g.ResetTransform();
    }
}

ogsagwnx

ogsagwnx2#

由于到目前为止的其他答案稍微错过了正确的解决方案,这里有一个连接两个相等大小的圆

using (Pen pen = new Pen(Color.Blue, radius)
 { EndCap = LineCap.Round, StartCap = LineCap.Round }  )
     g.DrawLine(pen, x1, y1, x2, y2);

字符串
x1c 0d1x的数据
备注:

  • 通常,将图形对象的平滑模式设置为抗锯齿是一个好主意。
  • 连接两个不同大小的圆需要一些数学来计算四个外部tangent points。从这些可以得到一个多边形来填充,或者,如果需要的话,可以创建一个GraphicsPath来填充,如果颜色的alpha < 1。
  • Jimi的评论指出了一个利用GDI+转换功能的不同解决方案。
  • 一些答案或评论将所需的形状称为oval。虽然这在普通话中是可以的,但在这里,特别是当提到几何书籍时,这是错误的,因为椭圆形不会有任何直线。
  • 正如Jimi所指出的,你所说的 radius 实际上是圆的 * 直径 *。我在代码中留下了错误的术语,但是你不应该
ne5o7dgx

ne5o7dgx3#

伪风格:

circle1x;
    circle1y;

circle2x;
circle2y;

midx=circle1x-circle2x;
midy=circle2x-circle2x;

draw circle at midx midy;

字符串
重复midx midy,在两个方向上。添加另一个圆。老实说,伙计,这不值得,为了使它平滑,你需要几个圆。你需要用两个圆的中心作为椭圆的两个中心来画一个椭圆。

相关问题