Como conectar visualmente 2 círculos?

votos
1

Sabemos 2 círculo' x e y posição central, e o raio é o mesmo. Eu quero ligar visualmente os círculos sem loop a elipse sorteio para cada ponto na linha que conecta é o centro do círculo 2.

A partir deste:

Para isso:

Código:

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));
Publicado 20/10/2018 em 14:04
fonte usuário
Em outras línguas...                            


3 respostas

votos
6

Uma solução para quando os Círculos não têm o mesmo diâmetro.

A primeira informação necessária é a distância entre os centros de dois círculos.
Para calculá-lo, usamos a distância euclidiana aplicada a um plano cartesiano:

Distância euclidiana

Onde (x1, y1)e (x2, y2)são as coordenadas dos centros dos dois círculos.
Nós também precisamos saber a direção (expresso como um valor positivo ou negativo): o cálculo [Distance]será sempre positivo.

em C#que, ela pode ser codificada por:

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;

Agora, temos a distância entre os centros de dois círculos, que também expressa uma direção.
Nós também precisa saber como esta linha virtual - que liga os dois Centros - é girada em relação ao nosso plano de desenho. Na figura abaixo, a distância pode ser visto como a hipotenusa de um triângulo retângulo h = (A, B) . O Cângulo é determinado pelo ponto de intersecção das linhas rectas, paralelas ao eixo, que atravessam os centros dos círculos.

Precisamos calcular o ângulo Theta (θ).
Usando o Teorema de Pitágoras , podemos derivar que o seno do ângulo Theta é Sinθ = b/h(como na figura)

Triângulo direitasine Cosinus

Usando coordenadas Centros dos círculos, isso pode ser codificado em C#como:
( Distanceé a hipotenusa do triângulo)

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

SinThetaexpressa um ângulo em Radians. Precisamos do ângulo expresso em Degrees: a Graphicsobjeto usa esta medida por suas funções de transformação mundo.

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

Agora, precisamos construir um conector , uma forma que liga os 2 círculos. Precisamos de um polígono; um retângulo não pode ter diferentes pares de lados (estamos considerando círculos com diâmetros diferentes).
Este polígono terá os lados mais longos = a distância entre os círculos Centros, os lados mais curtos = aos círculos diâmetros.

Para construir um polígono, podemos usar tanto Graphics.DrawPolygon e GraphicsPath.AddPolygon . Eu estou escolhendo o GraphicsPathmétodo, porque uma GraphicsPathpode conter mais do que uma forma e essas formas podem interagir , de certa forma.

Para conectar os 2 círculos considerados com um Polygon, é preciso girar o polígono usando o RotationAnglecalculado anteriormente.
Uma maneira simples de executar a rotação, é mover coordena o mundo para o Centro de um dos círculos, usando o Graphics.TranslateTransform método, em seguida, gire as novas coordenadas, usando Graphics.RotateTransform .

Precisamos desenhar nosso posicionamento Polygon um dos lados curtos - correspondente ao diâmetro do círculo que é o centro da transformação coordenadas - no centro da Cirle. Assim, quando a rotação será aplicado, é curto lado ele vai estar no meio dessa transformação, ancorado ao Centro.

Aqui, figure 3mostra o posicionamento do polígono (forma amarelo) (ok, parece que um retângulo, não importa); no mesmo polígono após a rotação.
figure 4

Centrando e rodando um Polígono

Notas:
Como TAW apontou , este desenho precisa ser realizada utilizando uma SolidBrush com uma cor não transparente, que é uma espécie de decepcionante.
Bem, uma escova semi-transparente não é proibido, mas as formas sobrepostas terá uma cor diferente, a soma das cores transparentes dos cruzamentos.

No entanto, é possível desenhar as formas usando uma escova semi-transparente, sem uma mudança de cor, usando a GraphicsPathcapacidade de preencher as suas formas usando uma cor que é aplicada a todas as partes sobrepostas. Nós apenas precisamos de alterar o padrão FILLMODE (veja o exemplo na documentação), definindo-o FillMode.Winding.

Código de exemplo:
Neste exemplo, dois casais de círculos são desenhadas em um contexto Graphics. Eles são, então, conectado com uma forma poligonal, criado usando GraphicsPath.AddPolygon().
(Claro, precisamos usar o Paintevento de um controle drawable, um formulário aqui)

A função auxiliar sobrecarregado aceita tanto posição centros dos círculos, expressa como um PointFe uma RectangleFestrutura, que representa os limites dos círculos.

Este é o resultado visual, com plenos Cores e usando uma escova semi-transparente:

Desenhar formas resultado Visual

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);
}

função auxiliar:

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();
    }
}
Respondeu 22/10/2018 em 01:41
fonte usuário

votos
5

Como as outras respostas até agora ligeiramente perca a solução correta, aqui está um que conecta dois círculos de igual tamanho :

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

digite descrição da imagem aqui

Notas:

  • Geralmente é uma boa idéia para definir o modo de suavização dos gráficos opor-se anti-alias ..

  • Para conectar dois círculos de tamanhos diferentes vai levar um pouco de matemática para calcular os quatro exteriores pontos de tangência . Destes pode-se obter um polígono para preencher ou, se necessário, poderia criar um GraphicsPathpara preencher, no caso da cor tem um alfa <1.

  • Os comentários de Jimi apontar para uma solução diferente que fazem uso das capacidades de GDI + transformação.

  • Algumas das respostas ou comentários referem-se a forma desejada como um oval . Enquanto isso ok no discurso comum, aqui, especialmente quando os livros de geometria são mencionados, isso é errado, como um oval não terá quaisquer linhas retas.

  • Como Jimi observou, o que você chama raio é realmente o diâmetro dos círculos. Deixei o termo errado no código, mas você não deve !

Respondeu 21/10/2018 em 14:20
fonte usuário

votos
0

estilo pseudo:

  circle1x;
    circle1y;

circle2x;
circle2y;


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

draw circle at midx midy;

repita para Midy midx, em ambas as direções. adicionar outro círculo. honestamente o homem, este não vale a pena, a fim de torná-lo bom, você vai precisar de vários círculos. você precisa desenhar um oval usando o centro de ambos os círculos como os dois centros do seu oval

Respondeu 20/10/2018 em 14:34
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more