Delphi 环空上均匀(但非随机)间隔的n点集

Delphi 环空上均匀(但非随机)间隔的n点集,delphi,computational-geometry,Delphi,Computational Geometry,你好 我目前在工作中面临着一个挑战,这是一个很难解决的问题 挑战: 我需要创建一个穿孔环形板,将激光切割孔模式的CAD图纸。孔需要尽可能接近等距,以便气流分布均匀。孔的总面积需要等于总环形面积的40%。根据我的计算,我需要10190个6.35毫米的孔来完成这个 解决方案: 编写一个小程序来计算点集的XY坐标,我可以将其导入我的CAD软件 问题: 为此,我想使用一种方法来计算点坐标。作为起点,我使用了来自的信息。到目前为止,我已经均匀地完成了点分布,看起来是近似等距分布的。我的问题是,我需要以某种

你好

我目前在工作中面临着一个挑战,这是一个很难解决的问题

挑战:


我需要创建一个穿孔环形板,将激光切割孔模式的CAD图纸。孔需要尽可能接近等距,以便气流分布均匀。孔的总面积需要等于总环形面积的40%。根据我的计算,我需要10190个6.35毫米的孔来完成这个

解决方案:

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

问题:

为此,我想使用一种方法来计算点坐标。作为起点,我使用了来自的信息。到目前为止,我已经均匀地完成了点分布,看起来是近似等距分布的。我的问题是,我需要以某种方式指定环空的内外半径,其中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;
提前感谢,


Martin

编辑:考虑标准化

扩展点中给出的
页面方法用于填充单元圆圈。
你必须加上乘法系数A

rad := A * Sqrt(i) / Sqrt(N);
索引为N的最后一个孔应位于radO内:

A * Sqrt(N) / Sqrt(N) < radO - HoleRadius
so
A  =  radO - HoleRadius;
结果:
您必须使用系数A绘制带有索引的
holeQTY
点(N-holeQTY+1)…N
。示例代码:

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;
(在MinGap介绍之前拍摄的图片)

双值编码

 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;
产出

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
最后,从GUI中分离出可能的功能

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;
类型
TPointDouble=记录
十、 Y:双倍;
结束;
函数计算点(常数
内半径,
外层的,
霍雷拉迪乌斯,
覆盖率//范围0..1
:双份)
:焦油;
//不检查输入有效性和可能的孔重叠!
变量
ACoeff,golden_angle,rad,theta,MinGap,面积:双;
N、 i,j,孔数量:整数;
开始
孔数量:=圆形(覆盖范围*(Sqr(外表面)-Sqr(内半径))/
Sqr(HoleRadius));
MinGap:=0.1*HoleRadius;
黄金角:=Pi*(3-Sqrt(5));
ACoeff:=外层-(HoleRadius+MinGap);
N:=Ceil((孔数量-1)/(1-Sqr((内半径+孔半径+明盖普)/
ACoeff),;
设置长度(结果、孔数量);
对于i:=(N-孔数量+1)到N do begin
θ:=i*黄金_角;
rad:=ACoeff*Sqrt(i/N);
j:=i-(N-孔数量+1);
结果[j].X:=rad*Cos(θ);
结果[j].Y:=rad*Sin(θ);
结束;
结束;
这是一种用法

var
  Pts: TArray<TPointDouble>;
  i: Integer;
begin
  Pts := CalcRingPoints(1, 2, 0.2, 0.5);
  for i := 0 to High(Pts) do
    Memo1.Lines.Add(Format('%d  r:%5.4f  x:%f y:%f',
      [i, Hypot(Pts[i].X, Pts[i].Y), Pts[i].X, Pts[i].Y]));   
   //37 points
var
临时秘书处:焦油;
i:整数;
开始
Pts:=计算点(1,2,0.2,0.5);
对于i:=0至高(Pts)do
备忘录1.行.添加(格式(“%dr:%5.4f x:%fy:%f',
[i,Hypot(Pts[i].X,Pts[i].Y),Pts[i].X,Pts[i].Y]);
//37分
让我们看看

实心圆盘的公式是显而易见的,对吗

r[max]=r_系数*sqrt(max)
这给了我们
r_系数=r[max]/sqrt[max]

你的问题似乎是,当我们的平面不是实心的圆盘,而是一个较小的圆盘与较大的圆盘相减时,该怎么办

嗯,愚蠢的懒惰方法是修正洞的数量。既然圆盘的平方与半径平方成正比,既然我们希望我们的孔以大致相等的距离分布,我们可以希望当我们有足够多的孔时,孔的份额(百分比)与圆盘的平方成正比,对吗

所以我们有

  • N=10190=N\u外部磁盘-N\u内部磁盘
  • N\u外部磁盘/N\u内部磁盘≈ S_外部/S_内部=(R_外部/R_内部)^2
  • N\u内部磁盘/N\u外部磁盘≈ S_内部/S_外部=(R_内部/R_外部)^2
我们必须运行循环
I:=N\u inner\u disk+1到N\u outer\u disk
计算

  • r[I]=r_系数*SqRt(I)
  • 角度[I]=I*137.508*弧度系数
或者我们最好运行从
N_内部磁盘-10%到N_外部磁盘+10%
的循环,然后通过
(孔半径+10%)+r_孔[I]
r_孔[I]>r_内部+(孔半径+10%)
过滤所有超大的输出,因为我们的计算是近似的,所以我们无论如何都必须使用过滤,然后我们就可以从一开始就使用它


我相信您还需要添加一个变量-孔边缘和内/外圆边缘之间的最小间隙,这样hoels就不会在边缘部分脱离平台。由于缺少上述变量,并且为了简单起见,我在上面将间隙设置为孔半径的10%,这并不完全正确

回到我们的N-s

N=10190≈ N\u外部磁盘-N\u外部磁盘*(内部/外部)^2=N外部磁盘*(1-(内部/外部)^2)

N=10190≈ N\u内部磁盘*(R\u外部/R\u内部)^2-N\u内部磁盘=N\u内部磁盘*((R\u外部/R\u内部)^2-1)

因此对于给定的N,R_外部,R_内部

  • N\u内部磁盘≈ N/((外部/内部)^2-1)
  • N\u外部磁盘≈ N/(1-(内部/外部)^2)
现在从
N_-inner+1
N_-outer
运行循环,检查通过上述几何过滤器的孔总数及其累积平方,如果需要,稍微调整系数,使其略好于
N_-inner
N_-outer
,直到您能够接近适合您的分布

C
var
  Pts: TArray<TPointDouble>;
  i: Integer;
begin
  Pts := CalcRingPoints(1, 2, 0.2, 0.5);
  for i := 0 to High(Pts) do
    Memo1.Lines.Add(Format('%d  r:%5.4f  x:%f y:%f',
      [i, Hypot(Pts[i].X, Pts[i].Y), Pts[i].X, Pts[i].Y]));   
   //37 points