繁体   English   中英

如何绘制 ishihara-transformations(没有交叉的圆圈)?

[英]How to draw ishihara-transformations (circles in circle without intersection)?

问题:我需要在 C#/VB.NET 中绘制如下图
请注意,我的问题不是在 C# 中绘制圆圈。石原变身

我的问题是在没有太多空白和没有交集的情况下绘制它们。
我的想法是使用“轨道”,然后在“轨道”线上绘制以中心为中心的圆圈。 (有些轨道线较大,有些仅适用于较小的圆圈)问题是不应该有交叉点。

轨道

有人有更好的主意吗? 或者我将如何测试给定半径的要绘制的圆是否会与已经存在的圆相交?

圆的交点很容易计算:如果沿 x 的差的平方加上沿 y 的差的平方小于半径之和的平方,则圆相交。

请注意,这已经进行了一些优化,因为它避免了平方根。 其他优化是可能的,例如当沿 x 的差异大于半径的总和时,它们将永远不会相交。

只需针对所有现有圈子测试新圈子,就完成了。

这是 O(n^2),但很容易而且非常快,因为每个测试只是几个快速的操作。

当然,您可以寻找一种无需针对所有其他圆圈测试每个圆圈的优化,但这些都是昂贵的、大量的代码,因此只对许多圆圈值得。 首先尝试简单的解决方案。

在 C++ 代码中(对不起,我不会说 VB):

struct { double x, y, r; } Circle;

bool circleIsAllowed(const std::vector<Circle>& circles, const Circle& newCircle)
{
   for(std::vector<Circle>::const_iterator it = circles.begin(); it != circles.end(); ++it) // foreach(Circle it in circles)
   {
      double sumR = it->r + newCircle.r; // + minimumDistanceBetweenCircles (if you want)
      double dx = it->x - newCircle.x;
      double dy = it->y - newCircle.y;
      double squaredDist = dx*dx + dy*dy;
      if (squaredDist < sumR*sumR) return false;
   }

   return true; // no existing circle overlaps
}

编辑:更正了小错误,并注意到问题与 C++ 无关

这是我尝试解释@Sjoerd 的代码(在 VB.Net 中)。 该代码来自一个标准的空白 WinForm 应用程序。 它将一堆圆圈绘制成一个矩形。 我将把它留给 OP 将它们限制在一个圆圈内。 DoCirclesIntersect function 采用可选的PadCircle参数,该参数试图在圆圈之间提供更大的间距,因此它们不会相互碰撞。 它有点天真,但似乎有效。 您绘制的圆圈越多,速度就越慢,因为它需要检查越来越多的边界。

Option Explicit On
Option Strict On

Public Class Form1
    ''//NOTE: The circles in this code are bound to a rectangle but it should be fairly trivial to create a master circle and check that

    ''//Dimension of the bounding image
    Private Shared ReadOnly ImageMaxDimension As Integer = 500
    Private Shared ReadOnly MinCircleDiameter As Integer = 4
    Private Shared ReadOnly MaxCircleDiameter As Integer = 15
    Private Shared ReadOnly CircleCount As Integer = 500
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ''//Create a picture box to output to
        Dim PB As New PictureBox()
        PB.Dock = DockStyle.Fill
        Me.Controls.Add(PB)

        ''//List of bounds of all circles created so far
        Dim AllBounds As New List(Of RectangleF)

        ''//Our random number generator
        Dim R As New Random()

        ''//Values for our individual circles
        Dim W, X, Y As Integer
        Dim Re As RectangleF

        ''//Create a bitmap to draw on
        Dim TempB As New Bitmap(ImageMaxDimension, ImageMaxDimension)
        Using G = Graphics.FromImage(TempB)
            For I = 1 To CircleCount
                ''//We can only draw so many circles, this just gives us a counter so we know when we reach the limit for a given size
                Trace.WriteLine(I)

                ''//Create an infinite loop that we will break out of if we have found a circle that does not intersect anything
                Do While True
                    ''//Create a random diameter
                    W = R.Next(MinCircleDiameter, MaxCircleDiameter + 1)
                    ''//Create a random X,Y
                    X = R.Next(0 + W, ImageMaxDimension - W)
                    Y = R.Next(0 + W, ImageMaxDimension - W)
                    ''//Create our rectangle
                    Re = New RectangleF(X, Y, W, W)

                    ''//Check each existing bound to see if they intersect with the current rectangle
                    For Each B In AllBounds
                        ''//If they do, start the loop over again
                        If DoCirclesIntersect(B, Re, 1) Then Continue Do
                    Next

                    ''//If we are here, no circles intersected, break from the infinite loop
                    Exit Do
                Loop

                ''//All the circle to our list
                AllBounds.Add(Re)

                ''/Draw the circle on the screen
                G.FillEllipse(Brushes.BurlyWood, Re)
            Next

            ''//Draw the image to the picture box
            PB.Image = TempB
        End Using
    End Sub
    Private Shared Function DoCirclesIntersect(ByVal r1 As RectangleF, ByVal r2 As RectangleF, Optional ByVal PadCircle As Integer = 0) As Boolean
        ''//This code is hopefully what @Sjoerd said in his post
        Dim aX = Math.Pow(r1.X - r2.X, 2)
        Dim aY = Math.Pow(r1.Y - r2.Y, 2)
        Dim Dif = Math.Abs(aX - aY)
        Dim ra1 = r1.Width / 2
        Dim ra2 = r2.Width / 2
        Dim raDif = Math.Pow(ra1 + ra2, 2)
        Return (raDif + PadCircle) > Dif
    End Function
End Class

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM