Решение для случаев, когда Круги не имеют одинаковый диаметр.
Первая необходимая информация - это расстояние между центрами двух кругов.
Для вычисления мы используем Евклидово расстояние , применяемое к декартовой плоскости:
![Euclidean Distance](https://i.stack.imgur.com/mw033.png)
Где (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;
Теперь у нас есть расстояние между центрами двух кругов, которое также выражает направление.
Нам также нужно знать, как эта виртуальная линия- Соединение двух Центров - вращается относительно нашей плоскости чертежа.На рисунке ниже расстояние можно рассматривать как гипотенузу прямоугольного h = (A, B)
.Угол C
определяется пересечением прямых, параллельных оси, которые пересекают центры кругов.
Нам нужно вычислить угол Theta (θ)
.
Используя теорему Пифагора , мы можем получить, что синус угла тета равен Sinθ = b/h
(как на рисунке)
![Right Triangle](https://i.stack.imgur.com/KYev3.png)
![Sine Cosinus](https://i.stack.imgur.com/MGVp4.png)
Используя координаты центров окружностей, это можно кодировать в C#
как:
(Distance
- гипотенуза треугольника)
float SinTheta = (Math.Max(Circle1Center.Y, Circle2Center.Y) -
Math.Min(Circle1Center.Y, Circle2Center.Y)) / Distance;
SinTheta
выражает угол в Radians
.Нам нужен угол, выраженный в Degrees
: объект Graphics
использует эту меру для своих функций преобразования мира.
float RotationAngle = (float)(Math.Asin(SinTheta) * (180 / Math.PI));
Теперь нам нужно построить Connector , форма, которая связывает 2 круга.Нам нужен полигон;a Прямоугольник не может иметь разные пары сторон (мы рассматриваем круги с разными диаметрами).
Этот многоугольник будет иметь более длинные стороны = до расстояния между центрами кругов, более короткие стороны = до диаметров кругов.
Для построения многоугольника мы можем использовать как Graphics.DrawPolygon , так и GraphicsPath.AddPolygon .Я выбираю метод GraphicsPath
, потому что GraphicsPath
может содержать более одной фигуры, и эти фигуры могут взаимодействовать , в некотором роде.
To соедините 2 рассматриваемых Круга с Полигоном, нам нужно повернуть Полигон, используя ранее вычисленный RotationAngle
.
Простой способ выполнить вращение, это переместить мировые координаты в Центр одного изКруги, используя метод Graphics.TranslateTransform , затем вращайте новые координаты, используя Graphics.RotateTransform .
. Нам нужно нарисовать наш полигон, позиционируя одно изкороткие стороны - соответствующие диаметру окружности, которая является центром преобразования координат - в центре окружности.Следовательно, когда будет применено вращение, его короткая сторона будет находиться в середине этого преобразования, привязанного к центру.
Здесь figure 3
показывает расположение многоугольника (желтая форма) (хорошо, это похоже на прямоугольник, не говоря уже о);
в figure 4
тот же самый многоугольник после вращения.
![Centering and Rotating a Polygon](https://i.stack.imgur.com/AGvLY.png)
Примечания:
Как указывало TaW , этот рисунок должен быть выполнен с использованием SolidBrush с непрозрачным цветом, что несколько разочаровывает.
Хорошополупрозрачная кисть не запрещена, но перекрывающиеся фигуры будут иметь другой цвет, сумма прозрачных цветов пересечений.
Однако возможноНарисуйте фигуры, используя полупрозрачную кисть без изменения цвета, используя способность GraphicsPath
для заливки фигур, используя цвет, который применяется ко всем перекрывающимся частям.Нам просто нужно изменить значение по умолчанию FillMode (см. Пример в Документах), установив его на FillMode.Winding
.
Пример кода:
В этом примере две пары кругов нарисованы в графическом контексте.Затем они соединяются с многоугольной формой, созданной с использованием GraphicsPath.AddPolygon()
.
(Конечно, нам нужно использовать событие Paint
отрисовываемого элемента управления, здесь форму)
перегруженная вспомогательная функция принимает как положение центров окружностей, выраженное в виде PointF
, так и RectangleF
структуры, представляющей границы окружностей.
Это визуальный результат с полными цветами и использованиемполупрозрачная кисть:
![Drawn Shapes Visual result](https://i.stack.imgur.com/EYFEh.png)
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();
}
}