如何在视觉上连接2个圆圈?

Ger*_*ina 1 .net c# gdi+ winforms

我们知道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));
Run Code Online (Sandbox Code Playgroud)

Jim*_*imi 6

圆圈不具有相同直径时的解决方案.

所需的第一个信息是两个圆圈中心之间的距离.
为了计算它,我们使用应用于笛卡尔平面的欧几里德距离:

欧几里德距离

(x1, y1)(x2, y2)两个圆的中心的坐标.
我们还需要知道方向(表示为正值或负值):计算的值[Distance]始终为正值.

C#其中,它可以编码为:

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;
Run Code Online (Sandbox Code Playgroud)

现在,我们有两个圆圈中心之间的距离,它也表达了一个方向.
我们还需要知道这条连接两个中心的虚拟线是如何相对于我们的绘图平面旋转的.在下面的图中,距离可以被视为一个的斜边直角三角形 h = (A, B).该C角由直线的交点确定的,平行于所述轴线,其穿过圆的中心.

我们需要计算角度Theta (?).
使用毕达哥拉斯定理,我们可以推导出角度Theta的正弦 Sin? = b/h(如图所示)

直角三角形Sine Cosinus

使用圈子中心坐标,这可以被编码C#为:
(Distance是三角形的斜边)

float SinTheta = (Math.Max(Circle1Center.Y, Circle2Center.Y) - 
                  Math.Min(Circle1Center.Y, Circle2Center.Y)) / Distance;
Run Code Online (Sandbox Code Playgroud)

SinTheta表达一个角度Radians.我们需要表达的角度Degrees:Graphics对象将此度量用于其世界变换函数.

float RotationAngle = (float)(Math.Asin(SinTheta) * (180 / Math.PI));
Run Code Online (Sandbox Code Playgroud)

现在,我们需要构建一个连接器,一个链接2个圆圈的形状.我们需要一个多边形; 矩形不能有不同的边对(我们正在考虑具有不同直径的圆).
此Polygon将具有较长的边=与圆中心之间的距离,较短的边=与圆的直径.

要构建Polygon,我们可以使用Graphics.DrawPolygonGraphicsPath.AddPolygon.我正在选择这种GraphicsPath方法,因为a GraphicsPath可以容纳更多的形状,这些形状可以在某种程度上相互作用.

要将2个考虑过的圆与多边形连接起来,我们需要使用RotationAngle之前计算的旋转多边形.
执行旋转的一种简单方法是使用Graphics.TranslateTransform方法将世界坐标移动到其中一个圆的中心,然后使用Graphics.RotateTransform旋转新坐标.

我们需要绘制我们的Polygon定位其中一个短边 - 对应于圆的直径,这是坐标变换的中心 - 在Cirle的中心.因此,当应用旋转时,它的短边将在此变换的中间,锚定到中心.

在这里,figure 3显示了Polygon的定位(黄色形状)(好吧,它看起来像一个矩形,没关系);
figure 4旋转后的同一个多边形.

居中和旋转多边形

注意:
正如TaW所指出的,这个绘图需要使用具有不透明颜色的SolidBrush来执行,这有点令人失望.
嗯,不禁止半透明刷子,但重叠的形状将具有不同的颜色,即交叉点的透明颜色的总和.

然而,可以得出用半透明刷形状而不的颜色变化,使用所述GraphicsPath能力使用被施加到所有的重叠部分一个颜色来填充其形状.我们只需要更改默认的FillMode(参见文档中的示例),将其设置为FillMode.Winding.

示例代码:
在此示例中,在Graphics上下文中绘制了两对Circles.然后将它们连接成使用的多边形形状GraphicsPath.AddPolygon().
(当然,我们需要使用Paint可绘制控件的事件,这里是一个表单)

重载的辅助函数接受圆圈的中心位置,表示为a PointFRectangleF结构,表示圆圈边界.

这是视觉效果,使用全彩色并使用半透明刷:

绘制的形状视觉结果

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);
}
Run Code Online (Sandbox Code Playgroud)

辅助功能:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)


TaW*_*TaW 5

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

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

在此输入图像描述

笔记:

  • 通常最好将图形对象的平滑模式设置为反别名.

  • 要连接两个不同大小的圆将需要一些数学来计算四个外切点.从这些中可以得到一个多边形来填充,或者如果需要,可以创建一个GraphicsPath填充,以防颜色的alpha <1.

  • Jimi的评论指出了一种利用GDI +转换功能的不同解决方案.

  • 一些答案或评论将所需的形状称为椭圆形.虽然这在普通演讲中没问题,但在这里,特别是当提到几何书籍时,这是错误的,因为椭圆形将没有任何直线.

  • 正如吉米所说,你所谓的半径实际上是圆的直径.我在代码中留下了错误的术语,但你不应该!