### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Crescent Moon Done: Soddy Crescent how to make it ?  (Read 586 times)

#### Boleeman

• Hero Member
• Posts: 594
##### Crescent Moon Done: Soddy Crescent how to make it ?
« on: June 30, 2024, 05:48:05 pm »
Thanks to bobby100 for the update to the Crescent Moon type curve.

Now I would like to try to make the Soddy Crescent Curve which is described here:

https://www.codeproject.com/Articles/808880/Soddy-Crescent-Construction

As shown in the attached png, the Radii of the filled circles get smaller progressively as they go around the circumference of the smaller circle. Lots of maths involved to get the radius sizes of each circle correct.

« Last Edit: July 01, 2024, 06:06:46 am by Boleeman »

#### bobby100

• Full Member
• Posts: 220
##### Re: Moon Crescent Curve: How to Make it ?
« Reply #1 on: June 30, 2024, 05:52:16 pm »
Move the center of the inner circle a bit more down and probably increase the diameter of the inner circle.

Edit: if that doesn't work, you'll probably need an ellipsis instead of the inner circle.
« Last Edit: June 30, 2024, 05:54:49 pm by bobby100 »

#### bobby100

• Full Member
• Posts: 220
##### Re: Moon Crescent Curve: How to Make it ?
« Reply #2 on: June 30, 2024, 06:38:18 pm »
So:
- I've got your clipping, but you lost the black inner line. You'll need to draw an extra arc there
- I've re-arranged your code. Don't put the same code in 3 procedures. Make an extra procedure with the code (ReDraw in my code) and call it from that 3 procedures.

#### Boleeman

• Hero Member
• Posts: 594
##### Re: Moon Crescent Curve: How to Make it ?
« Reply #3 on: July 01, 2024, 05:51:27 am »
Many Thanks for that update bobby100. Much Appreciated.

It was very late yesterday and had to go to bed, so sorry about disappearing so suddenly.

I also added a TSpinedit Rotate, a fill colour selection Tcolorbutton and a save to png for the Crescent Moon.
« Last Edit: July 01, 2024, 06:51:08 am by Boleeman »

#### Boleeman

• Hero Member
• Posts: 594
##### Re: Crescent Moon Done: Soddy Crescent how to make it ?
« Reply #4 on: July 01, 2024, 06:32:00 am »
Here is the CSharp code of the Soddy Crescent:

Code: Pascal  [Select][+][-]
1.  using System;
2. using System.Collections.Generic;
3. using System.Collections.ObjectModel;
4. using System.Linq;
5. using System.Text;
6.
7. namespace SoddyCrescent
8. {
9.     public class SoddyCircle
10.     {
13.
14.         public SoddyCircle(double curvature)
15.         {
16.             this.curvature = curvature;
17.             radius = Math.Abs(1 / curvature);
18.         }
19.
20.         public override string ToString()
21.         {
23.             return s;
24.         }
25.
26.     } // class SoddyCircle
27.
28.
29.     public class ForthSoddyCircle
30.     {
31.         private List<SoddyCircle> forthSoddyCircle;
33.         {
34.             get { return forthSoddyCircle.AsReadOnly(); }
35.         }
36.
37.         public ForthSoddyCircle(double curvature1, double curvature2, double curvature3)
38.         {
39.             forthSoddyCircle = new List<SoddyCircle>();
40.             double K1K2_K2K3_K3K1_Prod = curvature1 * curvature2 + curvature2 * curvature3 + curvature3 * curvature1;
41.             if ((K1K2_K2K3_K3K1_Prod < 0) &&
42.                 (Math.Abs(K1K2_K2K3_K3K1_Prod)<.0000000000001))
43.             {
44.                 K1K2_K2K3_K3K1_Prod = 0.0;
45.             }
46.             if (K1K2_K2K3_K3K1_Prod < 0)
47.             {
48.                 throw new System.ArgumentException("Impossible curvatures for Soddy circles.");
49.             }
50.             forthSoddyCircle.Add(new SoddyCircle(curvature1 + curvature2 + curvature3 + 2 * (Math.Sqrt(K1K2_K2K3_K3K1_Prod))));
51.             if (K1K2_K2K3_K3K1_Prod == 0) return;
52.             forthSoddyCircle.Add(new SoddyCircle(curvature1 + curvature2 + curvature3 - 2 * (Math.Sqrt(K1K2_K2K3_K3K1_Prod))));
53.         }
54.
55.     } // class ForthSoddyCircle
56.
57. }
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83. using System;
84. using System.Windows;
85. using System.Collections.Generic;
86. using System.Collections.ObjectModel;
87. using System.Linq;
88. using System.Text;
89.
90. namespace SoddyCrescent
91. {
92.     public class CoOrd
93.     {
94. /*
95.         public override string ToString()
96.         {
97.             return (String.Format("({0},{1})", x, y));
98.         }
99. */
100.         public static double DistanceSquared(Point p0, Point p1)
101.         {
102.  //           return (Math.Pow(p0.x - p1.x, 2) + Math.Pow(p0.y - p1.y, 2));
103.             return (p0.X - p1.X) * (p0.X - p1.X) + (p0.Y - p1.Y) * (p0.Y - p1.Y);
104.         }
105.     } // class CoOrd
106.
107.
108.     public class MyCircle
109.     {
111.         public Point center;
112.         // kappa = .5522847490
113.         private static double Kappa = (Math.Sqrt(2) - 1) * 4 / 3;
114.
115.         public MyCircle()
116.         {
118.             center = new Point();
119.         }
120.
121.         public MyCircle(double radius, Point coord)
122.         {
124.             center = coord;
125.         }
126.
127.         public override string ToString()
128.         {
130.             s += "center: " + center.ToString();
131.             return s;
132.         }
133.
134.         public Curve GenerateCurve()
135.         {
136.             Curve curve = new Curve(4);   // 4 bezier segments in curve for circle
137.             // start and endpoint for each segment
138.             curve.startPoint = new Point(center.X, center.Y - radius);  //down from center
139.             curve.points[2] =  new Point(center.X + radius, center.Y);  //right from center
140.             curve.points[5] =  new Point(center.X, center.Y + radius);  //up from center
141.             curve.points[8] =  new Point(center.X - radius, center.Y);  //left from center
142.             curve.points[11] = new Point(center.X, center.Y - radius);  //down from center (same as start point)
143.
144.             double rk = radius * Kappa;
145.             curve.points[0] = new Point(curve.startPoint.X + rk, curve.startPoint.Y);
146.             curve.points[1] = new Point(curve.points[2].X, curve.points[2].Y - rk);
147.
148.             curve.points[3] = new Point(curve.points[2].X, curve.points[2].Y + rk);
149.             curve.points[4] = new Point(curve.points[5].X + rk, curve.points[5].Y);
150.
151.             curve.points[6] = new Point(curve.points[5].X - rk, curve.points[5].Y);
152.             curve.points[7] = new Point(curve.points[8].X, curve.points[8].Y + rk);
153.
154.             curve.points[9] = new Point(curve.points[8].X, curve.points[8].Y - rk);
155.             curve.points[10] = new Point(curve.points[11].X-rk, curve.points[11].Y);
156.
157.             return curve;
158.         }
159.     } // class MyCircle
160.
161.
162.     public class Intersect
163.     {
164.         private List<Point> intersects;
166.         {
167.             get { return intersects.AsReadOnly(); }
168.         }
169.
170.         public Intersect()
171.         {
172.             intersects = new List<Point>();
173.         }
175.         {
177.         }
178.
179.     } // class Intersect
180.
181.
182.     public class CircleIntersect
183.     // Given 2 circles P0 and P1
184.     // p2 is the CoOrd of the point at the intersection of the line
185.     // between the circle centers and the chord line between the intersections.
186.     // the p1-->p2 distance is a
187.     // the p0-->p2 distance is b
188.     // h is the p2-->intersect distance on the chord
189.     {
190.         private MyCircle P0, P1;
191.         private double dSquared;    //distance squared between centers
192.         private double d;           //distance between centers
193.         private double a, b;
194.         private Point p2;
195.         private double h;
196.         private double dx, dy;
197.         public Intersect intersects;
198.
199.         public CircleIntersect(MyCircle P0, MyCircle P1)
200.         {
201.             intersects = new Intersect();
202.             p2 = new Point();
203.             this.P0 = P0;
204.             this.P1 = P1;
205.             dSquared = CoOrd.DistanceSquared(P0.center, P1.center);
206.             d = Math.Sqrt(dSquared);
207.
209.                 return;
211.                 return;
212.
214.             a = d - b;
215.             double dx = P0.center.X - P1.center.X;
216.             p2.X = P1.center.X + (a / d) * dx;
217.             double dy = P0.center.Y - P1.center.Y;
218.             p2.Y = P1.center.Y + (a / d) * dy;
219.
221.             dx = (P1.center.Y - P0.center.Y) * (h / d);
222.             dy = (P1.center.X - P0.center.X) * (h / d);
223.             intersects.AddIntersect(new Point((p2.X + dx), (p2.Y - dy)));
224.             intersects.AddIntersect(new Point((p2.X - dx), (p2.Y + dy)));
225.         }
226.
227.         private string CheckIntersect(Point i, MyCircle P)
228.         {
229.             Double sum;
230.             sum = (i.X - P.center.X) * (i.X - P.center.X);
231.             sum += (i.Y - P.center.Y) * (i.Y - P.center.Y);
233.             return ("Check sum: " + sum);
234.         }
235.
236.         public void ShowIntersect()
237.         {
238.             Console.WriteLine("====  CircleIntersect ====");
239.             Console.WriteLine("\tcircle P0: " + P0.ToString());
240.             Console.WriteLine("\tcircle P1: " + P1.ToString());
241.             Console.WriteLine("\tdSquared: " + dSquared);
242.             Console.WriteLine("\td: " + d);
243.             Console.WriteLine("\ta: " + a);
244.             Console.WriteLine("\tb: " + b);
245.             Console.WriteLine("p2: " + p2.ToString());
246.             Console.WriteLine("h: " + h);
248.             for (int i = 0; i < inter.Count; i++)
249.             {
250.                 Console.WriteLine("\tintersect: " + inter[i].ToString());
251.                 Console.WriteLine(CheckIntersect(inter[i], P0));
252.                 Console.WriteLine(CheckIntersect(inter[i], P1));
253.             }
254.             Console.WriteLine("\t\t\t====");
255.
256.         }
257.
258.     } // class CircleIntersect
259.
260. }
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277. using System;
278. using System.Collections.Generic;
279. using System.Collections.ObjectModel;
280. using System.Linq;
281. using System.Text;
282. using System.Windows;
283. using System.Windows.Controls;
284.
285. namespace SoddyCrescent
286. {
287.     public class GrafCan
288.     {
289.         private double xScale;
290.         public double XScale
291.         { get { return xScale; } }
292.         private double yScale;
293.         public double YScale
294.         { get { return yScale; } }
295.
296.         private double yMax;
297.         public double YMAx
298.         { get { return yMax; } }
299.         private double yMin;
300.         public double YMin
301.         { get { return yMin; } }
302.
303.         private double actWidth;
304.         public double ActWidth
305.         { get { return actWidth; } }
306.
307.         private double actHeight;
308.         public double ActHeight
309.         { get { return actHeight; } }
310.
311.         private double yZero;
312.
313.         public Canvas can;
314.
315.
316.
317.
318.         public GrafCan(double actWidth, double actHeight, double yMax, double yMin, Canvas can)
319.         {
320.             this.can = can;
321.             this.actWidth = actWidth;
322.             this.actHeight = actHeight;
323.             this.yMax = yMax;
324.             this.yMin = yMin;
325.
326.             yScale = actHeight / (yMax - yMin);
327.             xScale = actWidth;
328.             //            xScale = actWidth / 2 * Math.PI;
329.
330.             yZero = yMax * yScale;
331.         } // ctor
332.
333.
334.         public Point GraphPoint(Point curvePoint)
335.         {
336.             Point grafPoint = new Point();
337.             grafPoint.X = curvePoint.X * xScale;
338.             grafPoint.Y = yZero - curvePoint.Y * yScale;
339.             return grafPoint;
340.         } // GraphPoint()
341.
342.     }
343. }
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363. using System;
364. using System.Collections.Generic;
365. using System.Linq;
366. using System.Text;
367. using System.Windows;
368. using System.Windows.Controls;
369. using System.Windows.Media;
370. using System.Windows.Shapes;
371.
372. namespace SoddyCrescent
373. {
374.     public class CurveStyle
375.     {
376.         public double strokeThickness;
377.         public Brush brush;
378.         public Brush fillBrush;
379.
380.         public CurveStyle()
381.         {
382.             strokeThickness = 1;
383.             brush = new SolidColorBrush(Colors.Black);
384.         } //ctor
385.
386.         public CurveStyle(double strokeThickness, Brush brush)
387.         {
388.             this.strokeThickness = strokeThickness;
389.             this.brush = brush;
390.         } //ctor
391.     } // class Style
392.
393.
394.
395.     public class Curve
396.     {
397.         public static List<Point> bPts = new List<Point>();            //for debugging!!!!!
398.
399.         public Point startPoint;
400.         public Point[] points;
401.         public Boolean hasBeenAdded;    //to canvas children
402.         public int addedAt;             //canvas child number
403.         public CurveStyle style;
404.
405.
406.         public Curve(int numberPoints)
407.         {
408.             points = new Point[numberPoints * 3];   //each Bezier in a polybs has 3 points
409.             style = new CurveStyle();
410.         }
411.
412.         private Curve()
413.         {
414.             style = new CurveStyle();
415.         }
416.
417.
418.
419.         public Curve Graf(GrafCan gc)
420.         {
421.             Curve grafCurve = new Curve();
422.             grafCurve.style = style;
423.             grafCurve.points = new Point[points.Length];
424.             grafCurve.startPoint = gc.GraphPoint(startPoint);
425.             for (int i = 0; i < points.Length; i++)
426.                 grafCurve.points[i] = gc.GraphPoint(points[i]);
427.             return grafCurve;
428.         } // Graf()
429.
430.
431.         public PathGeometry ToGeometry(Curve c)
432.         {
433.             PolyBezierSegment pbs = new PolyBezierSegment(c.points, true);
434.
435.             //put single PolyBezierSegment into a PathSegmentCollection
436.             PathSegmentCollection psc = new PathSegmentCollection();
438.
439.             PathFigure pf = new PathFigure();
440.             pf.StartPoint = c.startPoint;
441.             pf.Segments = psc;
442.
443.             PathFigureCollection pfc = new PathFigureCollection();
445.
446.             PathGeometry pg = new PathGeometry();
447.             pg.Figures = pfc;
448.
449.             return pg;
450.         }
451.
452.
453.         public void ToString()
454.         {
455.             Console.WriteLine("Curve - " + points.Length + " DataFormat points");
456.             Console.WriteLine("\tstartPoint: " + startPoint.ToString());
457.             for (int i = 1; i <= points.Length; i++)
458.             {
459.                 Console.WriteLine("\tP" + i + ": " + points[i - 1].ToString());
460.             }
461.         }
462.
463.
464.         public static void ShowPoints(GrafCan gc, Point[] points)
465.         {
466.             Path path = new Path();
467.             path.Stroke = Brushes.Green;
468.             path.StrokeThickness = 1;
469.             GeometryGroup gg = new GeometryGroup();
470.
471.             foreach (Point p in points)
472.             {
473.                 Point gp = gc.GraphPoint(p);
474.                 EllipseGeometry eg = new EllipseGeometry(gp, 3, 3);
476.             }
477.             path.Data = gg;
479.         } // ShowPoints()
480.
481.
482.         public void ShowControlPoints(GrafCan gc)
483.         {
484.             Curve cGraf = Graf(gc);           //create new curve adjusted to geometry
485.             Path path = new Path();
486.             path.Stroke = Brushes.Green;
487.             path.StrokeThickness = 1;
488.
489.             GeometryGroup gg = new GeometryGroup();
490.             foreach (Point p in cGraf.points)
491.             {
492.                 EllipseGeometry eg = new EllipseGeometry(p, 3, 3);
494.             }
495.             path.Data = gg;
497.
498.             Path endPointPath = new Path();
499.             endPointPath.Stroke = Brushes.Black;
500.             endPointPath.StrokeThickness = 2;
501.             GeometryGroup egg = new GeometryGroup();
502.             EllipseGeometry egStart = new EllipseGeometry(cGraf.startPoint, 3, 3);
504.
505.             for (int i = 2; i < cGraf.points.Length; i += 3)
506.             {
507.                 EllipseGeometry eg = new EllipseGeometry(cGraf.points[i], 3, 3);
509.             }
510.             endPointPath.Data = egg;
512.
513.         } // ShowControlPoints()
514.
515.
517.         {
518.             Curve cGraf = Graf(gc);           //create new curve adjusted to geometry
519.             //           cGraf.ToString();
520.             //           MyPath.Data = c.ToGeometry(cGraf);
521.             Path p = new Path();
522.
523.             //            p.Fill = new SolidColorBrush(Colors.LightCyan);
524.
525.             p.StrokeThickness = style.strokeThickness;
526.             //            p.Fill =
527.             p.Stroke = style.brush;
528.             p.Fill = style.fillBrush;
529.             //            p.Stretch = Stretch.Fill;
530.             p.Data = ToGeometry(cGraf);
533.             {
536.                 addedAt = gc.can.Children.Count - 1;
537.             }
538.             else
539.             {
542.             }
544.
545.
546.         public Curve CopyCurveAndMove(double dx, double dy)
547.         {
548.             Curve cc = new Curve(points.Length / 3);
549.             cc.style.strokeThickness = style.strokeThickness;
550.             cc.style.brush = style.brush;
551.             cc.startPoint.X = startPoint.X + dx;
552.             cc.startPoint.Y = startPoint.Y + dy;
553.             int i = 0;
554.             foreach (Point p in points)
555.             {
556.                 cc.points[i].X = p.X + dx;
557.                 cc.points[i].Y = p.Y + dy;
558.                 i++;
559.             }
560.             return cc;
561.         } // CopyCurveAndMove()
562.
563.
564.     } // class Curve
565. }
566.
567.

Attached is my failed attempt at converting the CSharp version to Lazarus.
Not getting the right curves happening.
grafcan.pas does the scaling
forthsoddycircle.pas creates the 4th and new circles according to the algorithm k4 = k1 + k2 + k3 +/- 2(k1*k2+k1*k3+k2*k3)^0.5
soddycrescent.pas calculates the intersections
curve.pas creates the curve points and moves them

Also attached is:

The screenshot of the CSharp working version that I tried to convert to Lazarus.
Animation showing the formation of the curve in the CSharp version.  (last attached gif)

« Last Edit: July 01, 2024, 09:10:48 am by Boleeman »