问题:我需要在C#/ VB.NET中绘制如下图片
请注意,我的问题不是在C#中绘制圆圈。
我的问题是在没有太多空白和没有交叉的情况下绘制它们。
我的想法是使用“轨道”,然后绘制中心在“轨道”线上的圆圈。 (有些轨道线用于较大的轨道线,有些仅用于较小的圆圈)
问题是不应该有交叉点。
任何人都有更好的主意? 或者我如何测试给定半径的绘制圆是否与已存在的圆相交?
答案 0 :(得分:3)
圆的交点很容易计算:如果沿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 ++
答案 1 :(得分:1)
这是我尝试解释@ Sjoerd的代码(在VB.Net中)。代码来自标准的空白WinForm应用程序。它将一堆圆圈绘制成一个矩形。我将把它留给OP将这些限制在一个圆圈内。 DoCirclesIntersect
函数采用可选的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