在环

时间:2016-04-01 13:53:02

标签: delphi computational-geometry

美好的一天,

我目前正面临着工作中的挑战,这一挑战正变得非常痛苦。

挑战:

我需要为穿孔环形板创建一个孔图案的CAD图,该板将被激光切割。这些孔需要尽可能接近等距,以便均匀地分配气流。孔的总面积需要等于总环形面积的40%。根据我的计算,我需要10,190个6.35mm的孔来完成这个......

解决方案:

编写一个小程序来计算点集的XY坐标,我可以将其导入到我的CAD软件中。

问题:

为此,我想使用 Fermat's Spiral 方法计算点坐标。作为起点,我使用了来自 Marmakoide's Blog 的信息。到目前为止,我已经均匀地完成了点分布并且看起来大致等距间隔。我的问题是我需要以某种方式指定环的内半径和外半径,其中N点的点集必须下降。请注意,我不是在数学上倾向于所有,所以请尽量保持答案清晰。

这是我的代码:

procedure TForm1.btnCalcClick(Sender: TObject);
var golden_angle, radI, radO, rad, theta, x, y : Double;
    i, k, holeQTY, index : integer;
    xCoords, yCoords : Array of Double;
begin
 holeQTY := StrToInt(edtNumHoles.Text);
 golden_angle := Pi * (3 - Sqrt(5));
 radI := StrToFloat(edtRadInner.Text);
 radO := StrToFloat(edtRadOuter.Text);
 SetLength(xCoords, holeQTY);
 SetLength(yCoords, holeQTY);
 for i := 0 to holeQTY - 1 do
   begin
     theta := i * golden_angle;
     rad := Sqrt(i) / Sqrt(holeQTY);
     x := rad * Cos(theta);
     y := rad * Sin(theta);
     xCoords[i] := x;
     yCoords[i] := y;
     StatusBar1.Panels[1].Text := IntToStr(i+1) + ' of ' + IntToStr(holeQTY);
     StatusBar1.Update;
     Application.ProcessMessages;
   end;

for k := 0 to holeQTY - 1 do
  begin
    Chart1.Series[0].AddXY(xCoords[k], yCoords[k], '', clBlack);
  end;

end;

提前致谢,

马丁

2 个答案:

答案 0 :(得分:1)

修改:将规范化考虑在内

Spreading points页面中的方法用于填充单元圈。
你必须添加乘法系数A

rad := A * Sqrt(i) / Sqrt(N);

索引为N的最后一个洞应该在radO:

之内
A * Sqrt(N) / Sqrt(N) < radO - HoleRadius
so
A  =  radO - HoleRadius;

现在是第一洞:

A * Sqrt(N - holeQTY + 1) / Sqrt(N) > (radI + HoleRadius)
N - holeQTY + 1 > N * Sqr((radI + HoleRadius) / A)
N * (1 - Sqr((radI + HoleRadius) / A)) > holeQTY - 1
N = Ceil((holeQTY - 1) / (1 - Sqr((radI + HoleRadius) / A)) 

结果:
您必须使用系数A绘制索引holeQTY的{​​{1}}个点。示例代码:

(N - holeQTY + 1)..N

(MinGap介绍前的图片)

enter image description here

带双值的代码

var
  A, golden_angle, radI, radO, rad, theta, MinGap: Double;
  N, x, y, i, holeQTY, holeRadius: Integer;
begin
  holeQTY := 120;
  radI := 50;
  radO := 200;
  holeRadius := 4;
  MinGap := 3;
  golden_angle := Pi * (3 - Sqrt(5));
  A := Floor(radO - (holeRadius + MinGap));
  N := Ceil((holeQTY - 1) / (1 - Sqr((radI + holeRadius + MinGap) / A)));
  Canvas.Ellipse(300 - 200, 300 - 200, 300 + 201, 300 + 201);
  Canvas.Ellipse(300 - 50, 300 - 50, 300 + 51, 300 + 51);
  for i := (N - holeQTY + 1) to N do begin
    theta := i * golden_angle;
    rad := A * Sqrt(i) / Sqrt(N);
    x := 300 + Round(rad * Cos(theta));
    y := 300 + Round(rad * Sin(theta));
    Canvas.Ellipse(x - holeRadius, y - holeRadius, x + holeRadius + 1, y + holeRadius + 1);
  end;

产生输出

 holeQTY := 17;
  radI := 0.1234;
  radO := 0.23456;
  holeRadius := 0.00635;
  MinGap :=     0.0000635;
  golden_angle := Pi * (3 - Sqrt(5));
  A := radO - (holeRadius + MinGap);
  N := Ceil((holeQTY - 1) / (1 - Sqr((radI + holeRadius + MinGap) / A)));
  for i := (N - holeQTY + 1) to N do begin
    theta := i * golden_angle;
    rad := A * Sqrt(i) / Sqrt(N);
    x := rad * Cos(theta);
    y := rad * Sin(theta);
    Memo1.Lines.Add(Format('r:%5.4f  x:%f y:%f', [rad, x, y]));
  end;

最后可能的功能与GUI分离

r:0.1317  x:0.12 y:0.05
r:0.1397  x:-0.13 y:0.05
r:0.1473  x:0.06 y:-0.13
r:0.1545  x:0.05 y:0.15
r:0.1613  x:-0.14 y:-0.08
r:0.1679  x:0.16 y:-0.04
r:0.1742  x:-0.10 y:0.14
r:0.1804  x:-0.02 y:-0.18
r:0.1863  x:0.14 y:0.12
r:0.1920  x:-0.19 y:0.01
r:0.1976  x:0.14 y:-0.14
r:0.2030  x:-0.01 y:0.20
r:0.2083  x:-0.13 y:-0.16
r:0.2134  x:0.21 y:0.03
r:0.2184  x:-0.18 y:0.12
r:0.2233  x:0.05 y:-0.22
r:0.2281  x:0.11 y:0.20

及其用法

type
  TPointDouble = record
    X, Y: Double;
  end;

function CalcRingPoints(const
                        InnerRadius,
                        OuterRadius,
                        HoleRadius,
                        CoverageRatio //range 0..1
                        : Double)
                        : TArray<TPointDouble>;
//doesn't check input validity and possible hole overlaps!
var
  ACoeff, golden_angle, rad, theta, MinGap, Area: Double;
  N, i, j, holeQTY: Integer;
begin
  holeQTY := Round(CoverageRatio * (Sqr(OuterRadius) - Sqr(InnerRadius)) /
    Sqr(HoleRadius));
  MinGap := 0.1 * HoleRadius;
  golden_angle := Pi * (3 - Sqrt(5));
  ACoeff := OuterRadius - (HoleRadius + MinGap);
  N := Ceil((holeQTY - 1) / (1 - Sqr((InnerRadius + HoleRadius + MinGap) /
    ACoeff)));
  SetLength(Result, holeQTY);

  for i := (N - holeQTY + 1) to N do begin
    theta := i * golden_angle;
    rad := ACoeff * Sqrt(i / N);
    j := i - (N - holeQTY + 1);
    Result[j].X := rad * Cos(theta);
    Result[j].Y := rad * Sin(theta);
  end;
end;

答案 1 :(得分:0)

让我们看看。

固体磁盘的公式很明显,对吧?

r[max] = r_coeff * sqrt(max)给了我们r_coeff = r[max]/sqrt[max];

您的问题似乎是关于当我们的飞机不是固体磁盘时要做什么,而是从较大的磁盘减去较小的磁盘。

嗯,懒惰的方法是纠正漏洞的数量。由于圆盘的平方与半径的平方成正比,并且由于我们希望我们的孔大部分分散,我们可以希望因为我们有足够多的孔,所以孔的份额(百分比)与圆盘方块成正比,对吧?

所以我们有

  • N = 10 190 = N_outer_disk - N_inner_disk
  • N_outer_disk / N_inner_disk ≈ S_outer / S_inner = (R_outer / R_inner)^2
  • N_inner_disk / N_outer_disk ≈ S_inner / S_outer = (R_inner / R_outer)^2

我们必须运行循环I := N_inner_disk + 1 to N_outer_disk计算

  • r[I] = r_coeff * SqRt(I)
  • angle[I] = I * 137.508 * degrees-to-radians-coefficient

或许我们最好从N_inner_disk - 10% to N_outer_disk + 10%运行循环,只过滤(hole_radius + 10%) + r_hole[I] < R_outerr_hole[I] > R_inner + (hole_radius + 10%)的所有超大输出 - 因为我们的计算是近似的,我们无论如何都必须使用过滤,然后我们就可以从初学者那里使用它。

我相信你还需要增加一个变量 - 孔边缘和内/外圆边缘之间的最小间隙,这样就不会让洞穴变得干净了#34;在边缘部分地离开平台。

回到我们的N-S ......

N = 10 190 ≈ N_outer_disk - N_outer_disk*(R_inner / R_outer)^2 = N_outer_disk * (1 - (R_inner / R_outer)^2)

N = 10 190 ≈ N_inner_disk*(R_outer / R_inner)^2 - N_inner_disk = N_inner_disk * ((R_outer / R_inner)^2 - 1)

因此对于给定的N,R_outer,R_inner

的值
  • N_inner_disk ≈ N / ((R_outer / R_inner)^2 - 1)
  • N_outer_disk ≈ N / (1 - (R_inner / R_outer)^2)

现在运行从N_inner+1N_outer的循环,检查通过上述几何过滤器的孔的总量及其累积的平方,如果需要,稍微调整系数以稍微好一些{ {1}}和N_inner,直到你能够达到足够适合你的分配。

因此N_outer会变成r_coeff = r[max]/sqrt[max] 检查用这两种方法计算的系数是否彼此非常接近将是另一项检查,即两个N-s本身的计算在内部是一致的。