using OpenNest.Math; using System.Collections.Generic; namespace OpenNest.Geometry { public class Circle : Entity { private Vector center; private double radius; public Circle() { } public Circle(double x, double y, double radius) : this(new Vector(x, y), radius) { } public Circle(Vector center, double radius) { this.center = center; this.radius = radius; this.Rotation = RotationType.CCW; UpdateBounds(); } /// /// Creates a circle from two points. /// /// /// /// public static Circle CreateFrom2Points(Vector pt1, Vector pt2) { var line = new Line(pt1, pt2); return new Circle(line.MidPoint, line.Length * 0.5); } /// /// Center point of the circle. /// public Vector Center { get { return center; } set { var offset = value - center; boundingBox.Offset(offset); center = value; } } /// /// Radius of the circle. /// public double Radius { get { return radius; } set { radius = value; UpdateBounds(); } } /// /// Radius * 2. Value NOT stored. /// public double Diameter { get { return Radius * 2.0; } set { Radius = value / 2.0; } } /// /// Rotation direction. /// public RotationType Rotation { get; set; } /// /// Area of the circle. /// /// public double Area() { return System.Math.PI * Radius * Radius; } /// /// Linear distance around the circle. /// /// public double Circumference() { return System.Math.PI * Diameter; } /// /// Returns true if the given circle has the same radius as this. /// /// /// public bool IsConcentricTo(Circle circle) { return center == circle.center; } /// /// Returns true if the given circle has the same radius as this. /// /// /// public bool IsConcentricTo(Arc arc) { return center == arc.Center; } public bool ContainsPoint(Vector pt) { return Center.DistanceTo(pt) <= Radius; } /// /// Returns the minimum number of segments needed so that the chord-to-arc /// deviation (sagitta) does not exceed the given tolerance. /// public int SegmentsForTolerance(double tolerance) { if (tolerance >= Radius) return 3; var maxAngle = 2.0 * System.Math.Acos(1.0 - tolerance / Radius); return System.Math.Max(3, (int)System.Math.Ceiling(Angle.TwoPI / maxAngle)); } public List ToPoints(int segments = 1000, bool circumscribe = false) { var points = new List(); var stepAngle = Angle.TwoPI / segments; var r = circumscribe && segments > 0 ? Radius / System.Math.Cos(stepAngle / 2.0) : Radius; for (int i = 0; i <= segments; ++i) { var angle = stepAngle * i; points.Add(new Vector( System.Math.Cos(angle) * r + Center.X, System.Math.Sin(angle) * r + Center.Y)); } return points; } /// /// Linear distance around the circle. /// public override double Length { get { return Circumference(); } } /// /// Reverses the rotation direction. /// public override void Reverse() { if (Rotation == RotationType.CCW) Rotation = RotationType.CW; else Rotation = RotationType.CCW; } /// /// Moves the center point to the given coordinates. /// /// /// public override void MoveTo(double x, double y) { Center = new Vector(x, y); } /// /// Moves the center point to the given point. /// /// public override void MoveTo(Vector pt) { Center = pt; } /// /// Offsets the center point by the given distances. /// /// /// public override void Offset(double x, double y) { Center = new Vector(Center.X + x, Center.Y + y); } /// /// Offsets the center point by the given distances. /// /// public override void Offset(Vector voffset) { Center += voffset; } /// /// Scales the circle from the zero point. /// /// public override void Scale(double factor) { center *= factor; radius *= factor; UpdateBounds(); } /// /// Scales the circle from the origin. /// /// /// public override void Scale(double factor, Vector origin) { center = center.Scale(factor, origin); radius *= factor; UpdateBounds(); } /// /// Rotates the circle from the zero point. /// /// public override void Rotate(double angle) { Center = Center.Rotate(angle); } /// /// /// Rotates the circle from the origin. /// /// /// public override void Rotate(double angle, Vector origin) { Center = Center.Rotate(angle, origin); } /// /// Updates the bounding box. /// public override void UpdateBounds() { boundingBox.X = Center.X - Radius; boundingBox.Y = Center.Y - Radius; boundingBox.Width = Diameter; boundingBox.Length = Diameter; } public override Entity OffsetEntity(double distance, OffsetSide side) { if (side == OffsetSide.Left && Rotation == RotationType.CCW) { return Radius <= distance ? null : new Circle(center, Radius - distance) { Layer = Layer, Rotation = Rotation }; } else { return new Circle(center, Radius + distance) { Layer = Layer }; } } public override Entity OffsetEntity(double distance, Vector pt) { if (ContainsPoint(pt)) { return Radius <= distance ? null : new Circle(center, Radius - distance) { Layer = Layer, Rotation = Rotation }; } else { return new Circle(center, Radius + distance) { Layer = Layer }; } } /// /// Gets the closest point on the circle to the specified point. /// /// /// public override Vector ClosestPointTo(Vector pt) { var angle = Center.AngleTo(pt); return new Vector( System.Math.Cos(angle) * Radius + Center.X, System.Math.Sin(angle) * Radius + Center.Y); } /// /// Returns true if the given arc is intersecting this. /// /// /// public override bool Intersects(Arc arc) { List pts; return Intersect.Intersects(arc, this, out pts); } /// /// Returns true if the given arc is intersecting this. /// /// /// Points of intersection. /// public override bool Intersects(Arc arc, out List pts) { return Intersect.Intersects(arc, this, out pts); } /// /// Returns true if the given circle is intersecting this. /// /// /// public override bool Intersects(Circle circle) { var dist = Center.DistanceTo(circle.Center); return (dist < (Radius + circle.Radius) && dist > System.Math.Abs(Radius - circle.Radius)); } /// /// Returns true if the given circle is intersecting this. /// /// /// Points of intersection. /// public override bool Intersects(Circle circle, out List pts) { return Intersect.Intersects(this, circle, out pts); } /// /// Returns true if the given line is intersecting this. /// /// /// public override bool Intersects(Line line) { List pts; return Intersect.Intersects(this, line, out pts); } /// /// Returns true if the given line is intersecting this. /// /// /// Points of intersection. /// public override bool Intersects(Line line, out List pts) { return Intersect.Intersects(this, line, out pts); } /// /// Returns true if the given polygon is intersecting this. /// /// /// public override bool Intersects(Polygon polygon) { List pts; return Intersect.Intersects(this, polygon, out pts); } /// /// Returns true if the given polygon is intersecting this. /// /// /// Points of intersection. /// public override bool Intersects(Polygon polygon, out List pts) { return Intersect.Intersects(this, polygon, out pts); } /// /// Returns true if the given shape is intersecting this. /// /// /// public override bool Intersects(Shape shape) { List pts; return Intersect.Intersects(this, shape, out pts); } /// /// Returns true if the given shape is intersecting this. /// /// /// Points of intersection. /// public override bool Intersects(Shape shape, out List pts) { return Intersect.Intersects(this, shape, out pts); } /// /// Type of entity. /// public override EntityType Type { get { return EntityType.Circle; } } } }