Crescent Moon Bgrabmp: With some Extra Parameters and Save Png lazarus / 39-Graphics ---------------------------------------------------------------------- Boleeman · 2024-06-30 15:48 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. ---------------------------------------------------------------------- bobby100 · 2024-06-30 15:52 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. ---------------------------------------------------------------------- bobby100 · 2024-06-30 16:38 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 · 2024-07-01 03:51 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. ---------------------------------------------------------------------- Boleeman · 2024-07-01 04:32 Here is the CSharp code of the Soddy Crescent: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Linq;using System.Text; namespace SoddyCrescent{    public class SoddyCircle    {        public readonly double curvature;        public readonly double radius;         public SoddyCircle(double curvature)        {          this.curvature = curvature;            radius = Math.Abs(1 / curvature);        }         public override string ToString()        {            string s = String.Format("curvature: {0}\t\tradius: {1}", curvature, radius);            return s;        }     } // class SoddyCircle      public class ForthSoddyCircle    {        private List forthSoddyCircle;        public ReadOnlyCollection rdOnlyForthSoddyCircle        {          get { return forthSoddyCircle.AsReadOnly(); }        }         public ForthSoddyCircle(double curvature1, double curvature2, double curvature3)        {            forthSoddyCircle = new List();            double K1K2_K2K3_K3K1_Prod = curvature1 * curvature2 + curvature2 * curvature3 + curvature3 * curvature1;            if ((K1K2_K2K3_K3K1_Prod < 0) &&              (Math.Abs(K1K2_K2K3_K3K1_Prod)<.0000000000001))            {              K1K2_K2K3_K3K1_Prod = 0.0;            }            if (K1K2_K2K3_K3K1_Prod < 0)            {                throw new System.ArgumentException("Impossible curvatures for Soddy circles.");            }            forthSoddyCircle.Add(new SoddyCircle(curvature1 + curvature2 + curvature3 + 2 * (Math.Sqrt(K1K2_K2K3_K3K1_Prod))));          if (K1K2_K2K3_K3K1_Prod == 0) return;            forthSoddyCircle.Add(new SoddyCircle(curvature1 + curvature2 + curvature3 - 2 * (Math.Sqrt(K1K2_K2K3_K3K1_Prod))));        }     } // class ForthSoddyCircle }                         using System;using System.Windows;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Linq;using System.Text; namespace SoddyCrescent{    public class CoOrd    {/*      public override string ToString()        {            return (String.Format("({0},{1})", x, y));        }*/        public static double DistanceSquared(Point p0, Point p1)        { //           return (Math.Pow(p0.x - p1.x, 2) + Math.Pow(p0.y - p1.y, 2));          return (p0.X - p1.X) * (p0.X - p1.X) + (p0.Y - p1.Y) * (p0.Y - p1.Y);        }    } // class CoOrd      public class MyCircle    {      public double radius;        public Point center;        // kappa = .5522847490        private static double Kappa = (Math.Sqrt(2) - 1) * 4 / 3;         public MyCircle()        {            radius = 0;          center = new Point();        }         public MyCircle(double radius, Point coord)        {            this.radius = radius;          center = coord;        }         public override string ToString()        {            string s = String.Format("radius: {0}\t\t", radius);            s += "center: " + center.ToString();            return s;        }         public Curve GenerateCurve()        {          Curve curve = new Curve(4);   // 4 bezier segments in curve for circle            // start and endpoint for each segment            curve.startPoint = new Point(center.X, center.Y - radius);  //down from center            curve.points[2] =  new Point(center.X + radius, center.Y);  //right from center            curve.points[5] =  new Point(center.X, center.Y + radius);  //up from center            curve.points[8] =  new Point(center.X - radius, center.Y);  //left from center            curve.points[11] = new Point(center.X, center.Y - radius);  //down from center (same as start point)             double rk = radius * Kappa;            curve.points[0] = new Point(curve.startPoint.X + rk, curve.startPoint.Y);            curve.points[1] = new Point(curve.points[2].X, curve.points[2].Y - rk);             curve.points[3] = new Point(curve.points[2].X, curve.points[2].Y + rk);            curve.points[4] = new Point(curve.points[5].X + rk, curve.points[5].Y);             curve.points[6] = new Point(curve.points[5].X - rk, curve.points[5].Y);            curve.points[7] = new Point(curve.points[8].X, curve.points[8].Y + rk);             curve.points[9] = new Point(curve.points[8].X, curve.points[8].Y - rk);            curve.points[10] = new Point(curve.points[11].X-rk, curve.points[11].Y);             return curve;        }    } // class MyCircle      public class Intersect    {        private List intersects;        public ReadOnlyCollection rdOnlyIntersects      {            get { return intersects.AsReadOnly(); }        }       public Intersect()        {            intersects = new List();        }        public void AddIntersect(Point intersect)        {            intersects.Add(intersect);        }   } // class Intersect      public class CircleIntersect    // Given 2 circles P0 and P1    // p2 is the CoOrd of the point at the intersection of the line    // between the circle centers and the chord line between the intersections.    // the p1-->p2 distance is a    // the p0-->p2 distance is b    // h is the p2-->intersect distance on the chord    {        private MyCircle P0, P1;        private double dSquared;    //distance squared between centers        private double d;           //distance between centers        private double a, b;        private Point p2;        private double h;        private double dx, dy;        public Intersect intersects;         public CircleIntersect(MyCircle P0, MyCircle P1)        {            intersects = new Intersect();            p2 = new Point();            this.P0 = P0;            this.P1 = P1;            dSquared = CoOrd.DistanceSquared(P0.center, P1.center);            d = Math.Sqrt(dSquared);             if (d > P1.radius + P0.radius)              return;            if (d < Math.Abs(P1.radius - P0.radius))              return;             b = (P0.radius * P0.radius - P1.radius * P1.radius + dSquared) / (2 * d);            a = d - b;            double dx = P0.center.X - P1.center.X;            p2.X = P1.center.X + (a / d) * dx;            double dy = P0.center.Y - P1.center.Y;          p2.Y = P1.center.Y + (a / d) * dy;             h = Math.Sqrt(P1.radius * P1.radius - a * a);            dx = (P1.center.Y - P0.center.Y) * (h / d);            dy = (P1.center.X - P0.center.X) * (h / d);            intersects.AddIntersect(new Point((p2.X + dx), (p2.Y - dy)));            intersects.AddIntersect(new Point((p2.X - dx), (p2.Y + dy)));        }         private string CheckIntersect(Point i, MyCircle P)        {            Double sum;          sum = (i.X - P.center.X) * (i.X - P.center.X);            sum += (i.Y - P.center.Y) * (i.Y - P.center.Y);            sum -= P.radius * P.radius;            return ("Check sum: " + sum);        }         public void ShowIntersect()        {            Console.WriteLine("====  CircleIntersect ====");            Console.WriteLine("\tcircle P0: " + P0.ToString());            Console.WriteLine("\tcircle P1: " + P1.ToString());            Console.WriteLine("\tdSquared: " + dSquared);            Console.WriteLine("\td: " + d);            Console.WriteLine("\ta: " + a);            Console.WriteLine("\tb: " + b);            Console.WriteLine("p2: " + p2.ToString());            Console.WriteLine("h: " + h);            ReadOnlyCollection inter = intersects.rdOnlyIntersects;            for (int i = 0; i < inter.Count; i++)            {                Console.WriteLine("\tintersect: " + inter[i].ToString());              Console.WriteLine(CheckIntersect(inter[i], P0));                Console.WriteLine(CheckIntersect(inter[i], P1));            }          Console.WriteLine("\t\t\t====");         }     } // class CircleIntersect }                using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Linq;using System.Text;using System.Windows;using System.Windows.Controls; namespace SoddyCrescent{    public class GrafCan    {        private double xScale;        public double XScale        { get { return xScale; } }        private double yScale;        public double YScale        { get { return yScale; } }         private double yMax;        public double YMAx        { get { return yMax; } }        private double yMin;        public double YMin        { get { return yMin; } }         private double actWidth;        public double ActWidth        { get { return actWidth; } }       private double actHeight;        public double ActHeight        { get { return actHeight; } }         private double yZero;         public Canvas can;            public GrafCan(double actWidth, double actHeight, double yMax, double yMin, Canvas can)        {            this.can = can;            this.actWidth = actWidth;            this.actHeight = actHeight;            this.yMax = yMax;            this.yMin = yMin;             yScale = actHeight / (yMax - yMin);          xScale = actWidth;            //            xScale = actWidth / 2 * Math.PI;             yZero = yMax * yScale;        } // ctor        public Point GraphPoint(Point curvePoint)        {            Point grafPoint = new Point();            grafPoint.X = curvePoint.X * xScale;            grafPoint.Y = yZero - curvePoint.Y * yScale;          return grafPoint;        } // GraphPoint()     }}                   using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Windows;using System.Windows.Controls;using System.Windows.Media;using System.Windows.Shapes; namespace SoddyCrescent{    public class CurveStyle    {        public double strokeThickness;        public Brush brush;        public Brush fillBrush;         public CurveStyle()        {            strokeThickness = 1;            brush = new SolidColorBrush(Colors.Black);        } //ctor         public CurveStyle(double strokeThickness, Brush brush)        {            this.strokeThickness = strokeThickness;            this.brush = brush;        } //ctor    } // class Style       public class Curve  {        public static List bPts = new List();          //for debugging!!!!!         public Point startPoint;        public Point[] points;        public Boolean hasBeenAdded;    //to canvas children        public int addedAt;             //canvas child number        public CurveStyle style;          public Curve(int numberPoints)        {            points = new Point[numberPoints * 3];   //each Bezier in a polybs has 3 points            style = new CurveStyle();        }         private Curve()        {            style = new CurveStyle();        }           public Curve Graf(GrafCan gc)        {            Curve grafCurve = new Curve();            grafCurve.style = style;            grafCurve.points = new Point[points.Length];            grafCurve.startPoint = gc.GraphPoint(startPoint);            for (int i = 0; i < points.Length; i++)                grafCurve.points[i] = gc.GraphPoint(points[i]);            return grafCurve;        } // Graf()          public PathGeometry ToGeometry(Curve c)        {          PolyBezierSegment pbs = new PolyBezierSegment(c.points, true);           //put single PolyBezierSegment into a PathSegmentCollection          PathSegmentCollection psc = new PathSegmentCollection();          psc.Add(pbs);             PathFigure pf = new PathFigure();          pf.StartPoint = c.startPoint;            pf.Segments = psc;           PathFigureCollection pfc = new PathFigureCollection();          pfc.Add(pf);             PathGeometry pg = new PathGeometry();          pg.Figures = pfc;             return pg;        }          public void ToString()        {            Console.WriteLine("Curve - " + points.Length + " DataFormat points");            Console.WriteLine("\tstartPoint: " + startPoint.ToString());          for (int i = 1; i <= points.Length; i++)            {                Console.WriteLine("\tP" + i + ": " + points[i - 1].ToString());          }        }          public static void ShowPoints(GrafCan gc, Point[] points)        {            Path path = new Path();          path.Stroke = Brushes.Green;            path.StrokeThickness = 1;          GeometryGroup gg = new GeometryGroup();             foreach (Point p in points)            {                Point gp = gc.GraphPoint(p);                EllipseGeometry eg = new EllipseGeometry(gp, 3, 3);                gg.Children.Add(eg);          }            path.Data = gg;            gc.can.Children.Add(path);        } // ShowPoints()          public void ShowControlPoints(GrafCan gc)        {            Curve cGraf = Graf(gc);           //create new curve adjusted to geometry            Path path = new Path();          path.Stroke = Brushes.Green;            path.StrokeThickness = 1;           GeometryGroup gg = new GeometryGroup();            foreach (Point p in cGraf.points)            {                EllipseGeometry eg = new EllipseGeometry(p, 3, 3);                gg.Children.Add(eg);            }            path.Data = gg;          gc.can.Children.Add(path);             Path endPointPath = new Path();            endPointPath.Stroke = Brushes.Black;            endPointPath.StrokeThickness = 2;            GeometryGroup egg = new GeometryGroup();            EllipseGeometry egStart = new EllipseGeometry(cGraf.startPoint, 3, 3);            egg.Children.Add(egStart);             for (int i = 2; i < cGraf.points.Length; i += 3)            {                EllipseGeometry eg = new EllipseGeometry(cGraf.points[i], 3, 3);              egg.Children.Add(eg);            }            endPointPath.Data = egg;            gc.can.Children.Add(endPointPath);         } // ShowControlPoints()          public void AddCurveToCan(GrafCan gc)        {            Curve cGraf = Graf(gc);           //create new curve adjusted to geometry          //           cGraf.ToString();            //           MyPath.Data = c.ToGeometry(cGraf);            Path p = new Path();           //            p.Fill = new SolidColorBrush(Colors.LightCyan);             p.StrokeThickness = style.strokeThickness;            //            p.Fill =            p.Stroke = style.brush;            p.Fill = style.fillBrush;          //            p.Stretch = Stretch.Fill;            p.Data = ToGeometry(cGraf);            //            gc.can.Children.Add(p);          if (!hasBeenAdded)            {                gc.can.Children.Add(p);                hasBeenAdded = true;              addedAt = gc.can.Children.Count - 1;            }            else            {                gc.can.Children.RemoveAt(addedAt);              gc.can.Children.Insert(addedAt, p);            }        } // AddCurveToCan()          public Curve CopyCurveAndMove(double dx, double dy)        {            Curve cc = new Curve(points.Length / 3);            cc.style.strokeThickness = style.strokeThickness;          cc.style.brush = style.brush;            cc.startPoint.X = startPoint.X + dx;            cc.startPoint.Y = startPoint.Y + dy;          int i = 0;            foreach (Point p in points)            {                cc.points[i].X = p.X + dx;                cc.points[i].Y = p.Y + dy;                i++;            }            return cc;      } // CopyCurveAndMove()      } // class Curve}   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) ---------------------------------------------------------------------- Boleeman · 2024-08-30 09:32 Never got that CSharp Soddy Crescent conversion to Lazarus to work properly to make that cool Soddy Crescent. Frustrated! But found and made the Bgrabmp Crescent Moon Shadow from bgrabmp Wiki page. ---------------------------------------------------------------------- Boleeman · 2024-08-31 04:01 Some extra parameters added to vary the Crescent and also save to png. I like how layer.EraseEllipseAntialias was used to erase the pre-drawn ellipse. Also like how the curve resizes when the size of the form is changed.