Advertisement
WarPie90

Approximated MinAreaCircle

Sep 21st, 2023 (edited)
1,646
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 5.62 KB | None | 0 0
  1. program new;
  2. {$I SRL/osr.simba}
  3.  
  4. var
  5.   bmp: TMufasaBitmap;
  6.  
  7. //technically this can be solved in n log n time.. but meh.. so much work
  8. procedure FurthestPoints(const TPA:TPointArray; out A,B:TPoint);
  9. var
  10.   i,j: Integer;
  11.   dist,tmp: Single;
  12. begin
  13.   for i:=0 to High(TPA) do
  14.     for j:=i+1 to High(TPA) do
  15.     begin
  16.       tmp := Hypot(TPA[i].x-TPA[j].x, TPA[i].y-TPA[j].y);
  17.       if tmp > dist then
  18.       begin
  19.         dist := tmp;
  20.         A := TPA[i];
  21.         B := TPA[j];
  22.       end;
  23.     end;
  24. end;
  25.  
  26. (*
  27.   Approximate smallest bounding circle algorithm by Slacky
  28.   This formula makes 3 assumptions of outlier points that defines the circle.
  29.  
  30.   p1 and p2 are the furthest possible points from one another
  31.   p3 is the furthest point from the center of the line of p1 and p2 at a 90 degree angle.
  32.  
  33.   Already now we have a circle that is very close to encapsulating.
  34.  
  35.   We assume that p3 is nearly always correct, and if there is one wrong assumption it's either p1, or p2.
  36.   We move p1 and/or p2 until the criteria is reached, odds are one of them is correct already alongside p3.
  37.   If we actually didn't solve it so far, we move p3 to see if that does it.
  38.  
  39.   https://en.wikipedia.org/wiki/Smallest-circle_problem
  40.  
  41.   Time complexity is somewhere along the lines of
  42.     O(max(n log n, h^2))
  43.  
  44.   Where `h` is the remainding points after convex hull is performed.
  45.   `n` is the number of points, and `n log n` is assuming optimal convexhull algorithm.
  46. *)
  47. function MinAreaCircle(arr: TPointArray): TCircle;
  48.   function Circle3(P1,P2,P3: TPoint): TCircle;
  49.   var
  50.     t,d,l: Single;
  51.     p,pcs,q,qcs: record x,y: Single; end;
  52.   begin
  53.     t   := ArcTan2(p1.y-p2.y, p1.x-p2.x) - PI/2;
  54.     p   := [(p1.x+p2.x) / 2, (p1.y+p2.y) / 2];
  55.     pcs := [Cos(t), Sin(t)];
  56.  
  57.     t   := ArcTan2(p1.y-p3.y, p1.x-p3.x) - PI/2;
  58.     q   := [(p1.x+p3.x) / 2, (p1.y+p3.y) / 2];
  59.     qcs := [Cos(t), Sin(t)];
  60.  
  61.     d := pcs.x * qcs.y - pcs.y * qcs.x;
  62.     if Abs(d) < 0.001 then Exit();
  63.     l := (qcs.x * (p.y - q.y) - qcs.y * (p.x - q.x)) / d;
  64.     Result.x := Round(p.x + l * pcs.x);
  65.     Result.y := Round(p.y + l * pcs.y);
  66.     Result.Radius := Ceil(Sqrt(Sqr((p.x + l * pcs.x)-p1.x) + Sqr((p.y + l * pcs.y)-p1.y)));
  67.   end;
  68.  
  69.   function InCircle(arr: TPointArray; c: TCircle): Boolean;
  70.   var i: Int32;
  71.   begin
  72.     Result := True;
  73.     for i:=0 to High(arr) do
  74.       if Hypot(arr[i].x-c.x, arr[i].y-c.Y) > c.Radius+1 then Exit(False);
  75.   end;
  76.  
  77.   function pass(p1,p2,p3: TPoint; now:TCircle; var new: TPoint): TCircle;
  78.   var i: Int32; c: TCircle;
  79.   begin
  80.     new := p2;
  81.     Result := now;
  82.     for i:=0 to High(arr) do
  83.     begin
  84.       if (arr[i] = p3) or (arr[i] = p1) then continue;
  85.       c := Circle3(p1,arr[i],p3);
  86.       if (c.Radius < Result.Radius) and InCircle(arr, c) then
  87.       begin
  88.         Result := c;
  89.         new := arr[i];
  90.       end;
  91.     end;
  92.   end;
  93. var
  94.   i: Int32;
  95.   v,q,p1,p2,p3: TPoint;
  96. begin
  97.   arr := arr.ConvexHull();
  98.   FurthestPoints(arr, p1,p2);
  99.  
  100.   v := [(p1.x + p2.x) div 2, (p1.y + p2.y) div 2];
  101.   p3 := arr[0];
  102.   for i:=0 to High(arr) do
  103.     if Sqrt(Sqr(v.x-arr[i].x)+Sqr(v.y-arr[i].y)) > Sqrt(Sqr(v.x-p3.x)+Sqr(v.y-p3.y)) then
  104.       p3 := arr[i];
  105.  
  106.   if (p3 = p2) or (p3 = p1) then
  107.     Exit(TCircle([Round(v.x), Round(v.y), Ceil(Distance(p1,p2) / 2)]));
  108.  
  109.   Result := Circle3(p1,p2,p3);
  110.   if Result.Radius = 0 then
  111.     Exit(TCircle([Round(v.x), Round(v.y), Ceil(Distance(p1,p2) / 2)]));
  112.  
  113.   if InCircle(arr, Result) then Exit(Result);
  114.  
  115.   Result.Radius := $FFFF;
  116.   Result := pass(p1,p2,p3, Result, v);
  117.   if v <> p2 then q := v;
  118.   Result := pass(p2,p1,p3, Result, v);
  119.   if v <> p1 then Result := pass(v,q,p3, Result, v);
  120.  
  121.   if InCircle(arr, Result) and (Result.Radius <> $FFFF) then Exit(Result);
  122.  
  123.   Result := pass(p2,p3,p1, Result, v); // very rarely is the p3 assumption is wrong.
  124.  
  125.   if (Result.Radius = $FFFF) then Result.Radius := Ceil(Distance(p1,p2) / 2);
  126. end;
  127.  
  128. function ToString(s: TPoint): string; override;
  129. begin
  130.   Result := '['+ToString(s.x)+','+ToString(s.y)+']';
  131. end;
  132.  
  133. function InCircle(arr: TPointArray; c: TCircle): Boolean;
  134. var i: Int32;
  135. begin
  136.   Result := True;
  137.   for i:=0 to High(arr) do
  138.     if Hypot(arr[i].x-c.x, arr[i].y-c.Y) > c.Radius+1 then Exit(False);
  139. end;
  140.  
  141.  
  142. var
  143.   TPA: TPointArray;
  144.   a: T2DPointArray;
  145.   e1,e2,i: Int32;
  146.   t1,t2,t1_sum,t2_sum: Double;
  147.   c1,c2: TCircle;
  148. begin
  149.   bmp.Init();
  150.   bmp.SetSize(900,900);
  151.   bmp.Debug();
  152.  
  153.   while True do
  154.   begin
  155.     TPA := RandomTPA(Random(5,150), [250,250,550,550]);
  156.  
  157.     c1 := MinAreaCircle(TPA);
  158.     c2 := TPA.MinAreaCircle();
  159.  
  160.     c1.Radius += 1;
  161.     (*
  162.     if (not InCircle(TPA.ConvexHull(),c1)) then
  163.     begin
  164.       WriteLn(c1);
  165.       WriteLn(TPA.ConvexHull());
  166.       bmp.DrawCircle(c1.x, c1.y, c1.Radius, $FF);
  167.       bmp.DrawCircle(c2.x, c2.y, c2.Radius, $00FF00);
  168.       bmp.DrawTPA(TPA.ConvexHull().Connect(), $FF00FF);
  169.       bmp.Debug();
  170.       bmp.Clear();
  171.  
  172.       WaitUntil(isKeyDown(VK_ENTER), 50, 60000);
  173.       Wait(20)
  174.     end;
  175.     *)
  176.  
  177.     if (Abs(c1.Radius - c2.Radius) > 10) then
  178.     begin
  179.       WriteLn(c1);
  180.       WriteLn(TPA.ConvexHull());
  181.       bmp.DrawCircle(c1.x, c1.y, c1.Radius, $FF);
  182.       bmp.DrawCircle(c2.x, c2.y, c2.Radius, $00FF00);
  183.       bmp.DrawTPA(TPA.ConvexHull().Connect(), $FF00FF);
  184.       bmp.Debug();
  185.       bmp.Clear();
  186.  
  187.       WriteLn(TPA.ConvexHull());
  188.       WaitUntil(isKeyDown(VK_ENTER), 50, 60000);
  189.       Wait(20);
  190.  
  191.       if c1.Radius > c2.Radius then Inc(e1);
  192.       if c1.Radius < c2.Radius then Inc(e2);
  193.     end;
  194.  
  195.     Inc(i);
  196.     if i mod 1000 = 0 then
  197.     begin
  198.       WriteLn('Tested: ', i, '. Error count (new/old): ', e1, '/', e2);
  199.     end;
  200.   end;
  201.  
  202.   //bmp.Debug();
  203. end.
  204.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement