public static Point[] GetInterestingPoints(this PrimitiveEllipse ellipse, int maxSeg) { var startAngleDeg = ellipse.StartAngle; var endAngleDeg = ellipse.EndAngle; if (endAngleDeg < startAngleDeg) { endAngleDeg += MathHelper.ThreeSixty; } var startAngleRad = startAngleDeg * MathHelper.DegreesToRadians; var endAngleRad = endAngleDeg * MathHelper.DegreesToRadians; if (endAngleRad < startAngleRad) { endAngleRad += MathHelper.TwoPI; } var vertexCount = (int)Math.Ceiling((endAngleDeg - startAngleDeg) / MathHelper.ThreeSixty * maxSeg); var points = new Point[vertexCount + 1]; var angleDelta = MathHelper.ThreeSixty / maxSeg * MathHelper.DegreesToRadians; var trans = ellipse.FromUnitCircle; double angle; int i; for (angle = startAngleRad, i = 0; i < vertexCount; angle += angleDelta, i++) { points[i] = trans.Transform(new Point(Math.Cos(angle), Math.Sin(angle), 0.0)); } points[i] = trans.Transform(new Point(Math.Cos(angle), Math.Sin(angle), 0.0)); return(points); }
public static IEnumerable <Point> IntersectionPoints(this PrimitiveEllipse ellipse, PrimitivePoint point, bool withinBounds = true) { var fromUnit = ellipse.FromUnitCircle; var toUnit = fromUnit.Inverse(); var pointOnUnit = (Vector)toUnit.Transform(point.Location); if (MathHelper.CloseTo(pointOnUnit.LengthSquared, 1.0)) { if (!withinBounds) { return new Point[] { point.Location } } ; // always a match else { var pointAngle = pointOnUnit.ToAngle(); if (ellipse.IsAngleContained(pointAngle)) { return new Point[] { point.Location } } ; else { return(new Point[0]); } } } else { // wrong distance return(new Point[0]); } }
public static Point GetPoint(this PrimitiveEllipse ellipse, double angle) { var pointUnit = new Point(Math.Cos(angle * MathHelper.DegreesToRadians), Math.Sin(angle * MathHelper.DegreesToRadians), 0.0); var pointTransformed = ellipse.FromUnitCircle.Transform(pointUnit); return(pointTransformed); }
public static IEnumerable <Point> IntersectionPoints(this PrimitiveEllipse ellipse, IPrimitive primitive, bool withinBounds = true) { IEnumerable <Point> result; switch (primitive.Kind) { case PrimitiveKind.Ellipse: result = ellipse.IntersectionPoints((PrimitiveEllipse)primitive, withinBounds); break; case PrimitiveKind.Line: result = ((PrimitiveLine)primitive).IntersectionPoints(ellipse, withinBounds); break; case PrimitiveKind.Point: result = ellipse.IntersectionPoints((PrimitivePoint)primitive, withinBounds); break; case PrimitiveKind.Text: result = Enumerable.Empty <Point>(); break; default: Debug.Assert(false, "Unsupported primitive type"); result = Enumerable.Empty <Point>(); break; } return(result); }
public Circle(PrimitiveEllipse ellipse, object tag = null) : base(ellipse.Color, tag) { if (!ellipse.IsCircle) { throw new ArgumentException($"{nameof(PrimitiveEllipse)} was not a circle"); } _primitive = ellipse; _primitives = new[] { _primitive }; Radius = ellipse.MajorAxis.Length; var right = Vector.RightVectorFromNormal(Normal); var points = TransformedPoints(Center, Normal, right, Radius, Radius, 0, 90, 180, 270); var quadrant1 = points[0]; var quadrant2 = points[1]; var quadrant3 = points[2]; var quadrant4 = points[3]; _snapPoints = new SnapPoint[] { new CenterPoint(Center), new QuadrantPoint(quadrant1), new QuadrantPoint(quadrant2), new QuadrantPoint(quadrant3), new QuadrantPoint(quadrant4) }; BoundingBox = BoundingBox.FromPoints(points); }
public Arc(PrimitiveEllipse arc, object tag = null) : base(arc.Color, tag) { if (arc.MinorAxisRatio != 1.0) { throw new ArgumentException($"{nameof(PrimitiveEllipse)} is not circular"); } _primitive = arc; _primitives = new[] { _primitive }; Radius = arc.MajorAxis.Length; var right = Vector.RightVectorFromNormal(Normal); var midAngle = (StartAngle + EndAngle) / 2.0; if (StartAngle > EndAngle) { midAngle -= 180.0; } var points = Circle.TransformedPoints(Center, Normal, right, Radius, Radius, StartAngle, EndAngle, midAngle); EndPoint1 = points[0]; EndPoint2 = points[1]; MidPoint = points[2]; _snapPoints = new SnapPoint[] { new CenterPoint(Center), new EndPoint(EndPoint1), new EndPoint(EndPoint2), new MidPoint(MidPoint) }; BoundingBox = BoundingBox.FromPoints(Circle.TransformedPoints(Center, Normal, right, Radius, Radius, 0, 90, 180, 270)); }
public static double GetAngle(this PrimitiveEllipse ellipse, Point point) { var transform = ellipse.FromUnitCircle.Inverse(); var pointFromUnitCircle = transform.Transform(point); var angle = Math.Atan2(pointFromUnitCircle.Y, pointFromUnitCircle.X) * MathHelper.RadiansToDegrees; return(angle.CorrectAngleDegrees()); }
private static bool IsPointOnPrimitive(this PrimitiveEllipse el, Point point) { var unitPoint = el.FromUnitCircle.Inverse().Transform(point); return(MathHelper.CloseTo(0.0, unitPoint.Z) && // on the XY plane MathHelper.CloseTo(1.0, ((Vector)unitPoint).LengthSquared) && // on the unit circle el.IsAngleContained(Math.Atan2(unitPoint.Y, unitPoint.X) * MathHelper.RadiansToDegrees)); // within angle bounds }
public void EllipseAngleContainmentTest() { var el = new PrimitiveEllipse(Point.Origin, 1.0, 90.0, 360.0, Vector.ZAxis); Assert.True(el.IsAngleContained(90.0)); Assert.True(el.IsAngleContained(180.0)); Assert.True(el.IsAngleContained(270.0)); Assert.True(el.IsAngleContained(360.0)); Assert.False(el.IsAngleContained(45.0)); }
public static PrimitiveEllipse Project(PrimitiveEllipse ellipse, Matrix4 transform) { // brute-force the endpoints var fromUnit = Matrix4.CreateScale(1.0, 1.0, 0.0) * transform * ellipse.FromUnitCircle; var center = transform.Transform(ellipse.Center); var minPoint = fromUnit.Transform(new Point(MathHelper.COS[0], MathHelper.SIN[0], 0)); var maxPoint = minPoint; var minDistance = (minPoint - center).LengthSquared; var maxDistance = minDistance; for (int i = 1; i < 360; i++) { var next = fromUnit.Transform(new Point(MathHelper.COS[i], MathHelper.SIN[i], 0)); var dist = (next - center).LengthSquared; if (dist < minDistance) { minPoint = next; minDistance = dist; } if (dist > maxDistance) { maxPoint = next; maxDistance = dist; } } var majorAxis = maxPoint - center; var minorAxisRatio = Math.Sqrt(minDistance) / Math.Sqrt(maxDistance); var startAngle = ellipse.StartAngle; var endAngle = ellipse.EndAngle; // projection looks like a circle if (Math.Abs(maxDistance - minDistance) < MathHelper.Epsilon * 1000) // TODO: need a better way to do this { majorAxis = new Vector(majorAxis.Length, 0.0, 0.0); // right vector minorAxisRatio = 1.0; } // correct start/end angles if (!ellipse.IsClosed) { var tempEllipse = new PrimitiveEllipse(center, majorAxis, Vector.ZAxis, minorAxisRatio, 0.0, MathHelper.ThreeSixty, ellipse.Color); var majorAngle = majorAxis.ToAngle(); var startPoint = transform.Transform(ellipse.StartPoint()); var endPoint = transform.Transform(ellipse.EndPoint()); var toUnit = tempEllipse.FromUnitCircle.Inverse(); var startUnit = (Vector)toUnit.Transform(startPoint); var endUnit = (Vector)toUnit.Transform(endPoint); startAngle = (startUnit.ToAngle() - majorAngle).CorrectAngleDegrees(); endAngle = (endUnit.ToAngle() - majorAngle).CorrectAngleDegrees(); } return(new PrimitiveEllipse(center, majorAxis, Vector.ZAxis, minorAxisRatio, startAngle, endAngle, ellipse.Color)); }
public async Task <bool> Execute(IWorkspace workspace, object arg) { var first = await workspace.InputService.GetPoint(new UserDirective("First point")); if (!first.Cancel && first.HasValue) { var second = await workspace.InputService.GetPoint(new UserDirective("Second point"), (p) => { return(new[] { new PrimitiveLine(first.Value, p) }); }); if (!second.Cancel && second.HasValue) { var third = await workspace.InputService.GetPoint(new UserDirective("Third point"), (p) => { var a = PrimitiveEllipse.ThreePointArc(first.Value, second.Value, p, workspace.DrawingPlane.Normal); if (a == null) { return(new IPrimitive[0]); } else { return(new IPrimitive[] { a, new PrimitivePoint(first.Value), new PrimitivePoint(second.Value) }); } }); if (!third.Cancel && third.HasValue) { var primitiveArc = PrimitiveEllipse.ThreePointArc(first.Value, second.Value, third.Value, workspace.DrawingPlane.Normal); if (primitiveArc != null) { var arc = new Arc( primitiveArc.Center, primitiveArc.MajorAxis.Length, primitiveArc.StartAngle, primitiveArc.EndAngle, primitiveArc.Normal); workspace.AddToCurrentLayer(arc); return(true); } } } } return(false); }
public static IPrimitive PrimitiveFromPointAndVertex(Point lastPoint, Vertex nextVertex) { if (nextVertex.IsLine) { return(new PrimitiveLine(lastPoint, nextVertex.Location)); } else { return(PrimitiveEllipse.ArcFromPointsAndIncludedAngle(lastPoint, nextVertex.Location, nextVertex.IncludedAngle, nextVertex.Direction)); } }
public static IPrimitive PrimitiveFromVertices(Vertex last, Vertex next) { if (last.IsLine) { return(new PrimitiveLine(last.Location, next.Location)); } else { return(PrimitiveEllipse.ArcFromPointsAndIncludedAngle(last.Location, next.Location, last.IncludedAngle, last.Direction)); } }
private static void TestThreePointArcNormal(Point a, Point b, Point c, Vector idealNormal, Point expectedCenter, double expectedRadius, double expectedStartAngle, double expectedEndAngle) { var arc = PrimitiveEllipse.ThreePointArc(a, b, c, idealNormal); AssertClose(idealNormal, arc.Normal); AssertClose(expectedCenter, arc.Center); AssertClose(1.0, arc.MinorAxisRatio); AssertClose(expectedRadius, arc.MajorAxis.Length); AssertClose(expectedStartAngle, arc.StartAngle); AssertClose(expectedEndAngle, arc.EndAngle); }
public void ThreePointCircleTest() { var a = new Point(0, 0, 0); var b = new Point(0, 2, 0); var c = new Point(1, 1, 0); var circ = PrimitiveEllipse.ThreePointCircle(a, b, c); Assert.NotNull(circ); Assert.Equal(new Point(0, 1, 0), circ.Center); Assert.Equal(Vector.XAxis, circ.MajorAxis); Assert.Equal(Vector.ZAxis, circ.Normal); }
public void EllipseGetPointTest() { var el = new PrimitiveEllipse(Point.Origin, 1.0, 0.0, 180.0, Vector.ZAxis); Assert.True(el.StartPoint().CloseTo(new Point(1.0, 0.0, 0.0))); Assert.True(el.EndPoint().CloseTo(new Point(-1.0, 0.0, 0.0))); Assert.True(el.GetPoint(90.0).CloseTo(new Point(0.0, 1.0, 0.0))); el = new PrimitiveEllipse(new Point(1.0, 1.0, 0.0), 1.0, 0.0, 180.0, Vector.ZAxis); Assert.True(el.StartPoint().CloseTo(new Point(2.0, 1.0, 0.0))); Assert.True(el.EndPoint().CloseTo(new Point(0.0, 1.0, 0.0))); Assert.True(el.GetPoint(90.0).CloseTo(new Point(1.0, 2.0, 0.0))); }
public Ellipse(PrimitiveEllipse ellipse, object tag = null) : base(ellipse.Color, tag) { _primitive = ellipse; _primitives = new[] { _primitive }; var majorLength = MajorAxis.Length; var points = Circle.TransformedPoints(Center, Normal, MajorAxis, majorLength, majorLength * MinorAxisRatio, 0, 90, 180, 270, StartAngle, EndAngle, (StartAngle + EndAngle) / 2.0); var quadrant1 = points[0]; var quadrant2 = points[1]; var quadrant3 = points[2]; var quadrant4 = points[3]; var endPoint1 = points[4]; var endPoint2 = points[5]; var midPoint = points[6]; var snaps = new List <SnapPoint>(); snaps.Add(new CenterPoint(Center)); if (StartAngle == 0.0 && EndAngle == 360.0) { // treat it like a circle snaps.Add(new QuadrantPoint(quadrant1)); snaps.Add(new QuadrantPoint(quadrant2)); snaps.Add(new QuadrantPoint(quadrant3)); snaps.Add(new QuadrantPoint(quadrant4)); } else { // treat it like an arc snaps.Add(new EndPoint(endPoint1)); snaps.Add(new EndPoint(endPoint2)); snaps.Add(new MidPoint(midPoint)); } if (MinorAxisRatio != 1.0) { // a true ellipse with two distinct foci var majorNormalized = MajorAxis.Normalize(); var minorLength = majorLength * MinorAxisRatio; var focusDistance = Math.Sqrt((majorLength * majorLength) - (minorLength * minorLength)); var focus1 = (Point)((majorNormalized * focusDistance) + Center); var focus2 = (Point)((majorNormalized * -focusDistance) + Center); snaps.Add(new FocusPoint(focus1)); snaps.Add(new FocusPoint(focus2)); } _snapPoints = snaps.ToArray(); BoundingBox = BoundingBox.FromPoints(quadrant1, quadrant2, quadrant3, quadrant4); }
public async Task <bool> Execute(IWorkspace workspace, object arg) { var drawingPlane = workspace.DrawingPlane; var center = await workspace.InputService.GetPoint(new UserDirective("Center")); if (!center.Cancel && center.HasValue) { var majorEnd = await workspace.InputService.GetPoint(new UserDirective("Major axis endpoint"), p => { return(new[] { new PrimitiveLine(center.Value, p) }); }); var majorPrimitive = new PrimitiveLine(center.Value, majorEnd.Value); var majorAxis = majorEnd.Value - center.Value; var majorAxisLength = majorAxis.Length; if (!majorEnd.Cancel && majorEnd.HasValue) { var minorEnd = await workspace.InputService.GetPoint(new UserDirective("Minor axis endpoint"), lastPoint : center.Value, onCursorMove : p => { var tempMinorAxis = p - center.Value; var tempMinorAxisRatio = tempMinorAxis.Length / majorAxisLength; var el = new PrimitiveEllipse(center.Value, majorAxis, drawingPlane.Normal, tempMinorAxisRatio, 0.0, 360.0); return(new IPrimitive[] { majorPrimitive, // major axis line new PrimitiveLine(center.Value, p), // minor axis line el // the ellipse }); }); if (!minorEnd.Cancel && minorEnd.HasValue) { var minorAxis = minorEnd.Value - center.Value; var minorAxisRatio = minorAxis.Length / majorAxisLength; if (!minorEnd.Cancel && minorEnd.HasValue) { var el = new Ellipse(center.Value, majorAxis, minorAxisRatio, 0.0, 360.0, drawingPlane.Normal); workspace.AddToCurrentLayer(el); return(true); } } } } return(false); }
private static Entity ToEntity(StepEdge edge) { switch (edge.ItemType) { case StepItemType.EdgeCurve: { var edgeCurve = (StepEdgeCurve)edge; if (edgeCurve.EdgeGeometry is StepCircle) { var circle = (StepCircle)edgeCurve.EdgeGeometry; var center = circle.Position.Location; var normal = ToVector(circle.Position.RefDirection); normal = Vector.ZAxis; var primitiveCircle = new PrimitiveEllipse(ToPoint(center), circle.Radius, normal); var toUnit = primitiveCircle.FromUnitCircle.Inverse(); var startPoint = toUnit.Transform(ToPoint((StepVertexPoint)edgeCurve.EdgeStart)); var endPoint = toUnit.Transform(ToPoint((StepVertexPoint)edgeCurve.EdgeEnd)); var startAngle = Math.Atan2(startPoint.Y, startPoint.X).CorrectAngleRadians() * MathHelper.RadiansToDegrees; var endAngle = Math.Atan2(endPoint.Y, endPoint.X).CorrectAngleRadians() * MathHelper.RadiansToDegrees; return(new Arc(primitiveCircle.Center, circle.Radius, startAngle, endAngle, primitiveCircle.Normal)); } else if (edgeCurve.EdgeGeometry is StepLine) { var stepLine = (StepLine)edgeCurve.EdgeGeometry; Line line; if (edgeCurve.EdgeStart == null || edgeCurve.EdgeEnd == null) { // use auto values line = ToLine(stepLine); } else { // use explicit values var p1 = ToPoint((StepVertexPoint)edgeCurve.EdgeStart); var p2 = ToPoint((StepVertexPoint)edgeCurve.EdgeEnd); line = new Line(p1, p2); } return(line); } break; } } return(null); }
public void TrimSplineOnLineTest2() { // _______|_ _______| // / | \ / | \ // / | \ / | // | | |o | | // | | | => | | | // \ | / \ | / // \ | / \ | / // -------|- -------|_ var sqrt2over2 = Math.Sqrt(2.0) / 2.0; var unitCircle = new PrimitiveEllipse(Point.Origin, 1.0, Vector.ZAxis); var circleSpline = Spline.FromBeziers(unitCircle.AsBezierCurves()); var trimLine = new Line(new Point(sqrt2over2, -1.0, 0.0), new Point(sqrt2over2, 1.0, 0.0)); // 45-90 degrees var q1trimBezier = new PrimitiveBezier( new Point(0.70696813418525, 0.70696813418525, 0.0), new Point(0.525957512247, 0.8879787561235, 0.0), new Point(0.275957512247, 1.0, 0.0), new Point(0.0, 1.0, 0.0)); // 270-315 degrees var q4trimBezier = new PrimitiveBezier( new Point(0.0, -1.0, 0.0), new Point(0.275957512247, -1.0, 0.0), new Point(0.525957512247, -0.8879787561235, 0.0), new Point(0.70696813418525, -0.70696813418525, 0.0)); var selectionPoint = new Point(Math.Cos(1.0 * MathHelper.DegreesToRadians), Math.Sin(1.0 * MathHelper.DegreesToRadians), 0.0); // one degree up var resultSpline = Spline.FromBeziers(new[] { new PrimitiveBezier(new Point(1.0, 0.0, 0.0), new Point(1.0, -0.551915024494, 0.0), new Point(0.551915024494, -1.0, 0.0), new Point(0.0, -1.0, 0.0)), new PrimitiveBezier(new Point(0.0, 1.0, 0.0), new Point(0.27602797672647056, 1.0, 0.0), new Point(0.5260851832511362, 0.8879215406024443, 0.0), new Point(0.7071067813752437, 0.7068294600060859, 0.0)), }); DoTrim( existingEntities: new[] { trimLine }, entityToTrim: circleSpline, selectionPoint: selectionPoint, expectTrim: true, expectedAdded: new[] { resultSpline }); }
public static bool IsAngleContained(this PrimitiveEllipse ellipse, double angle) { angle = angle.CorrectAngleDegrees(); var startAngle = ellipse.StartAngle; var endAngle = ellipse.EndAngle; if (endAngle < startAngle) { // we pass zero. angle should either be [startAngle, 360.0] or [0.0, endAngle] return(MathHelper.Between(startAngle, 360.0, angle) || MathHelper.Between(0.0, endAngle, angle)); } else { // we're within normal bounds return(MathHelper.Between(startAngle, endAngle, angle) || MathHelper.Between(startAngle, endAngle, angle + 360.0)); } }
public static Entity ToArc(this IgesCircularArc arc) { var center = TransformPoint(arc, arc.ProperCenter); var startPoint = TransformPoint(arc, arc.ProperStartPoint); var endPoint = TransformPoint(arc, arc.ProperEndPoint); // all points have the same Z-value, so the normal will be the transformed Z-axis vector var igesNormal = TransformPoint(arc, IgesVector.ZAxis); var normal = new Vector(igesNormal.X, igesNormal.Y, igesNormal.Z).Normalize(); // find radius from start/end points var startVector = startPoint - center; var endVector = endPoint - center; var startRadius = startVector.Length; var endRadius = endVector.Length; // these should be very close, if not identical, but not necessarily var radius = (startRadius + endRadius) / 2; // if start/end points are the same, it's a circle. otherwise it's an arc if (startPoint.CloseTo(endPoint)) { return(new Circle(center, radius, normal, GetColor(arc), arc)); } else { // project back to unit circle to find start/end angles var primitiveCircle = new PrimitiveEllipse(center, radius, normal); var fromUnit = primitiveCircle.FromUnitCircle; Debug.Assert(AreAllValuesValid(fromUnit)); var toUnit = fromUnit.Inverse(); Debug.Assert(AreAllValuesValid(toUnit)); var startUnit = toUnit.Transform(startPoint); var endUnit = toUnit.Transform(endPoint); var startAngle = ((Vector)startUnit).ToAngle(); var endAngle = ((Vector)endUnit).ToAngle(); return(new Arc(center, radius, startAngle, endAngle, normal, GetColor(arc), arc)); } }
public void ArcsFromPointsAndRadiusTest1() { // given the points (0, 1) and (0, -1) and an included angle of 90 degrees, the possible centers for the arcs // here are (1, 0) and (-1, 0) and a radius of sqrt(2) var p1 = new Point(0, 1, 0); var p2 = new Point(0, -1, 0); var includedAngle = 90.0; var sqrt2 = Math.Sqrt(2.0); var arc1 = PrimitiveEllipse.ArcFromPointsAndIncludedAngle(p1, p2, includedAngle, VertexDirection.Clockwise); AssertClose(new Point(-1, 0, 0), arc1.Center); AssertClose(sqrt2, arc1.MajorAxis.Length); AssertClose(315.0, arc1.StartAngle); AssertClose(45.0, arc1.EndAngle); var arc2 = PrimitiveEllipse.ArcFromPointsAndIncludedAngle(p1, p2, includedAngle, VertexDirection.CounterClockwise); AssertClose(new Point(1, 0, 0), arc2.Center); AssertClose(sqrt2, arc2.MajorAxis.Length); AssertClose(135.0, arc2.StartAngle); AssertClose(225.0, arc2.EndAngle); }
/// <summary> /// Using algorithm as described at http://www.ceometric.com/support/examples/v-ellipse-projection-using-rytz-construction.html /// </summary> public static ProjectedCircle FromConjugateDiameters(Circle originalCircle, Layer originalLayer, Point center, Point majorAxisConjugate, Point minorAxisConjugate) { // re-map to shorter variables var m = center; var p = majorAxisConjugate; var q = minorAxisConjugate; // PM must be longer than QM. Swap if necessary. var pm = p - m; var qm = q - m; if (pm.LengthSquared > qm.LengthSquared) { var temp = p; p = q; q = temp; pm = p - m; qm = q - m; } // if axes are already orthoganal, no transform is needed if (MathHelper.CloseTo(0.0, pm.Dot(qm))) { return(new ProjectedCircle(originalCircle, originalLayer, m, qm.Length, pm.Length, 0.0)); } // find the plane containing the projected ellipse var plane = Plane.From3Points(m, p, q); // rotate P by 90 degrees around the normal var rotationMatrix = Matrix4.Identity; rotationMatrix.RotateAt(new Quaternion(plane.Normal, 90.0), m); var rotp = rotationMatrix.Transform(p); // the angle between (rotp-M) and (Q-M) should be less than 90 degrees. mirror if not if (Vector.AngleBetween(rotp - m, qm) > 90.0) { rotp = ((rotp - m) * -1.0) + m; } // construct the rytz circle // the center is the midpoint of the edge from rotp to Q // the radius is the distance from M to the center // the normal is the normal of the plane var rc = (rotp + q) / 2.0; var rytz = new PrimitiveEllipse(rc, (m - rc).Length, plane.Normal); // intersect the rytz circle with a line passing through the center and Q var intersectingLine = new PrimitiveLine(rc, q); var intersectingPoints = intersectingLine.IntersectionPoints(rytz, false).ToList(); if (intersectingPoints.Count != 2) { return(null); } var da = (q - intersectingPoints[0]).Length; var db = (q - intersectingPoints[1]).Length; if (da < db) { // da must be large than db var temp = da; da = db; db = temp; } // get main axes var a = intersectingPoints[0] - m; var b = intersectingPoints[1] - m; if (b.LengthSquared > a.LengthSquared) { var temp = a; a = b; b = temp; } // return the new ellipse return(new ProjectedCircle(originalCircle, originalLayer, m, da, db, a.ToAngle() * -1.0)); }
public static IEnumerable <Point> IntersectionPoints(this PrimitiveLine line, PrimitiveEllipse ellipse, bool withinSegment = true) { var empty = Enumerable.Empty <Point>(); var l0 = line.P1; var l = line.P2 - line.P1; var p0 = ellipse.Center; var n = ellipse.Normal; var right = ellipse.MajorAxis.Normalize(); var up = ellipse.Normal.Cross(right).Normalize(); var radiusX = ellipse.MajorAxis.Length; var radiusY = radiusX * ellipse.MinorAxisRatio; var transform = ellipse.FromUnitCircle; var inverse = transform.Inverse(); var denom = l.Dot(n); var num = (p0 - l0).Dot(n); var flatPoints = new List <Point>(); if (Math.Abs(denom) < MathHelper.Epsilon) { // plane either contains the line entirely or is parallel if (Math.Abs(num) < MathHelper.Epsilon) { // parallel. normalize the plane and find the intersection var flatLine = line.Transform(inverse); // the ellipse is now centered at the origin with a radius of 1. // find the intersection points then reproject var dv = flatLine.P2 - flatLine.P1; var dx = dv.X; var dy = dv.Y; var dr2 = dx * dx + dy * dy; var D = flatLine.P1.X * flatLine.P2.Y - flatLine.P2.X * flatLine.P1.Y; var det = dr2 - D * D; var points = new List <Point>(); if (det < 0.0 || Math.Abs(dr2) < MathHelper.Epsilon) { // no intersection or line is too short } else if (Math.Abs(det) < MathHelper.Epsilon) { // 1 point var x = (D * dy) / dr2; var y = (-D * dx) / dr2; points.Add(new Point(x, y, 0.0)); } else { // 2 points var sgn = dy < 0.0 ? -1.0 : 1.0; var det2 = Math.Sqrt(det); points.Add( new Point( (D * dy + sgn * dx * det2) / dr2, (-D * dx + Math.Abs(dy) * det2) / dr2, 0.0)); points.Add( new Point( (D * dy - sgn * dx * det2) / dr2, (-D * dx - Math.Abs(dy) * det2) / dr2, 0.0)); } // ensure the points are within appropriate bounds if (withinSegment) { // line test points = points.Where(p => MathHelper.Between(flatLine.P1.X, flatLine.P2.X, p.X) && MathHelper.Between(flatLine.P1.Y, flatLine.P2.Y, p.Y)).ToList(); // circle test points = points.Where(p => ellipse.IsAngleContained(((Vector)p).ToAngle())).ToList(); } return(points.Select(p => (Point)transform.Transform(p))); } } else { // otherwise line and plane intersect in only 1 point, p var d = num / denom; var p = (Point)(l * d + l0); // verify within the line segment if (withinSegment && !MathHelper.Between(0.0, 1.0, d)) { return(empty); } // p is the point of intersection. verify if on transformed unit circle var unitVector = (Vector)inverse.Transform(p); if (Math.Abs(unitVector.Length - 1.0) < MathHelper.Epsilon) { // point is on the unit circle if (withinSegment) { // verify within the angles specified var angle = Math.Atan2(unitVector.Y, unitVector.X) * MathHelper.RadiansToDegrees; if (MathHelper.Between(ellipse.StartAngle, ellipse.EndAngle, angle.CorrectAngleDegrees())) { return(new[] { p }); } } else { return(new[] { p }); } } } // point is not on unit circle return(empty); }
public static IEnumerable <Point> IntersectionPoints(this PrimitiveEllipse first, PrimitiveEllipse second, bool withinBounds = true) { var empty = Enumerable.Empty <Point>(); IEnumerable <Point> results; // planes either intersect in a line or are parallel var lineVector = first.Normal.Cross(second.Normal); if (lineVector.IsZeroVector) { // parallel or the same plane if ((first.Center == second.Center) || Math.Abs((second.Center - first.Center).Dot(first.Normal)) < MathHelper.Epsilon) { // if they share a point or second.Center is on the first plane, they are the same plane // project second back to a unit circle and find intersection points var fromUnit = first.FromUnitCircle; var toUnit = fromUnit.Inverse(); // transform second ellipse to be on the unit circle's plane var secondCenter = toUnit.Transform(second.Center); var secondMajorEnd = toUnit.Transform(second.Center + second.MajorAxis); var secondMinorEnd = toUnit.Transform(second.Center + (second.Normal.Cross(second.MajorAxis).Normalize() * second.MajorAxis.Length * second.MinorAxisRatio)); RoundedDouble a = (secondMajorEnd - secondCenter).Length; RoundedDouble b = (secondMinorEnd - secondCenter).Length; if (a == b) { // if second ellipse is a circle we can absolutely solve for the intersection points // rotate to place the center of the second circle on the x-axis var angle = ((Vector)secondCenter).ToAngle(); var rotation = Matrix4.RotateAboutZ(angle); var returnTransform = fromUnit * Matrix4.RotateAboutZ(-angle); var newSecondCenter = rotation.Transform(secondCenter); var secondRadius = a; if (Math.Abs(newSecondCenter.X) > secondRadius + 1.0) { // no points of intersection results = empty; } else { // 2 points of intersection var x = (secondRadius * secondRadius - newSecondCenter.X * newSecondCenter.X - 1.0) / (-2.0 * newSecondCenter.X); var y = Math.Sqrt(1.0 - x * x); results = new[] { new Point(x, y, 0), new Point(x, -y, 0) }; } results = results.Distinct().Select(p => returnTransform.Transform(p)); } else { // rotate about the origin to make the major axis align with the x-axis var angle = (secondMajorEnd - secondCenter).ToAngle(); var rotation = Matrix4.RotateAboutZ(angle); var finalCenter = rotation.Transform(secondCenter); fromUnit = fromUnit * Matrix4.RotateAboutZ(-angle); toUnit = fromUnit.Inverse(); if (a < b) { // rotate to ensure a > b fromUnit = fromUnit * Matrix4.RotateAboutZ(90); toUnit = fromUnit.Inverse(); finalCenter = Matrix4.RotateAboutZ(-90).Transform(finalCenter); // and swap a and b var temp = a; a = b; b = temp; } RoundedDouble h = finalCenter.X; RoundedDouble k = finalCenter.Y; var h2 = h * h; var k2 = k * k; var a2 = a * a; var b2 = b * b; var a4 = a2 * a2; var b4 = b2 * b2; // RoundedDouble is used to ensure all operations are rounded to a certain precision if (Math.Abs(h) < MathHelper.Epsilon) { // ellipse x = 0; wide // find x values var A = a2 - a2 * b2 + a2 * k2 - ((2 * a4 * k2) / (a2 - b2)); var B = (2 * a2 * k * Math.Sqrt(b2 * (-a2 + a4 + b2 - a2 * b2 + a2 * k2))) / (a2 - b2); var C = (RoundedDouble)Math.Sqrt(a2 - b2); var x1 = -Math.Sqrt(A + B) / C; var x2 = (RoundedDouble)(-x1); var x3 = -Math.Sqrt(A - B) / C; var x4 = (RoundedDouble)(-x3); // find y values var D = a2 * k; var E = Math.Sqrt(-a2 * b2 + a4 * b2 + b4 - a2 * b4 + a2 * b2 * k2); var F = a2 - b2; var y1 = (D - E) / F; var y2 = (D + E) / F; results = new[] { new Point(x1, y1, 0), new Point(x2, y1, 0), new Point(x3, y2, 0), new Point(x4, y2, 0) }; } else if (Math.Abs(k) < MathHelper.Epsilon) { // ellipse y = 0; wide // find x values var A = -b2 * h; var B = Math.Sqrt(a4 - a2 * b2 - a4 * b2 + a2 * b4 + a2 * b2 * h2); var C = a2 - b2; var x1 = (A - B) / C; var x2 = (A + B) / C; // find y values var D = -b2 + a2 * b2 - b2 * h2 - ((2 * b4 * h2) / (a2 - b2)); var E = (2 * b2 * h * Math.Sqrt(a2 * (a2 - b2 - a2 * b2 + b4 + b2 * h2))) / (a2 - b2); var F = (RoundedDouble)Math.Sqrt(a2 - b2); var y1 = -Math.Sqrt(D - E) / F; var y2 = (RoundedDouble)(-y1); var y3 = -Math.Sqrt(D + E) / F; var y4 = (RoundedDouble)(-y3); results = new[] { new Point(x1, y1, 0), new Point(x1, y2, 0), new Point(x2, y3, 0), new Point(x2, y4, 0) }; } else { // brute-force approximate intersections results = BruteForceEllipseWithUnitCircle(finalCenter, a, b); } results = results .Where(p => !(double.IsNaN(p.X) || double.IsNaN(p.Y) || double.IsNaN(p.Z))) .Where(p => !(double.IsInfinity(p.X) || double.IsInfinity(p.Y) || double.IsInfinity(p.Z))) .Distinct() .Select(p => fromUnit.Transform(p)) .Select(p => new Point((RoundedDouble)p.X, (RoundedDouble)p.Y, (RoundedDouble)p.Z)); } } else { // parallel with no intersections results = empty; } } else { // intersection was a line // find a common point to complete the line then intersect that line with the circles double x = 0, y = 0, z = 0; var n = first.Normal; var m = second.Normal; var q = first.Center; var r = second.Center; if (Math.Abs(lineVector.X) >= MathHelper.Epsilon) { x = 0.0; y = (-m.Z * n.X * q.X - m.Z * n.Y * q.Y - m.Z * n.Z * q.Z + m.X * n.Z * r.X + m.Y * n.Z * r.Y + m.Z * n.Z * r.Z) / (-m.Z * n.Y + m.Y * n.Z); z = (-m.Y * n.X * q.X - m.Y * n.Y * q.Y - m.Y * n.Z * q.Z + m.X * n.Y * r.X + m.Y * n.Y * r.Y + m.Z * n.Y * r.Z) / (-m.Z * n.Y + m.Y * n.Z); } else if (Math.Abs(lineVector.Y) >= MathHelper.Epsilon) { x = (-m.Z * n.X * q.X - m.Z * n.Y * q.Y - m.Z * n.Z * q.Z + m.X * n.Z * r.X + m.Y * n.Z * r.Y + m.Z * n.Z * r.Z) / (-m.Z * n.X + m.X * n.Z); y = 0.0; z = (-m.X * n.X * q.X - m.X * n.Y * q.Y - m.X * n.Z * q.Z + m.X * n.X * r.X + m.Y * n.X * r.Y + m.Z * n.X * r.Z) / (-m.Z * n.X + m.X * n.Z); } else if (Math.Abs(lineVector.Z) >= MathHelper.Epsilon) { x = (-m.Y * n.X * q.X - m.Y * n.Y * q.Y - m.Y * n.Z * q.Z + m.X * n.Y * r.X + m.Y * n.Y * r.Y + m.Z * n.Y * r.Z) / (m.Y * n.X - m.X * n.Y); y = (-m.X * n.X * q.X - m.X * n.Y * q.Y - m.X * n.Z * q.Z + m.X * n.X * r.X + m.Y * n.X * r.Y + m.Z * n.X * r.Z) / (m.Y * n.X - m.X * n.Y); z = 0.0; } else { Debug.Assert(false, "zero-vector shouldn't get here"); } var point = new Point(x, y, z); var other = point + lineVector; var intersectionLine = new PrimitiveLine(point, other); var firstIntersections = intersectionLine.IntersectionPoints(first, false) .Select(p => new Point((RoundedDouble)p.X, (RoundedDouble)p.Y, (RoundedDouble)p.Z)); var secondIntersections = intersectionLine.IntersectionPoints(second, false) .Select(p => new Point((RoundedDouble)p.X, (RoundedDouble)p.Y, (RoundedDouble)p.Z)); results = firstIntersections .Union(secondIntersections) .Distinct(); } // verify points are in angle bounds if (withinBounds) { var toFirstUnit = first.FromUnitCircle.Inverse(); var toSecondUnit = second.FromUnitCircle.Inverse(); results = from res in results // verify point is within first ellipse's angles let trans1 = toFirstUnit.Transform(res) let ang1 = ((Vector)trans1).ToAngle() where first.IsAngleContained(ang1) // and same for second let trans2 = toSecondUnit.Transform(res) let ang2 = ((Vector)trans2).ToAngle() where second.IsAngleContained(ang2) select res; } return(results); }
public async Task <bool> Execute(IWorkspace workspace, object arg) { Entity circle = null; var drawingPlane = workspace.DrawingPlane; var cen = await workspace.InputService.GetPoint(new UserDirective("Select center, [ttr], or [th]ree-point", "ttr", "th")); if (cen.Cancel) { return(false); } if (cen.HasValue) { var mode = CircleMode.Radius; while (circle == null) { switch (mode) { case CircleMode.Radius: { var rad = await workspace.InputService.GetPoint(new UserDirective("Enter radius or [d]iameter/[i]sometric", "d", "i"), (p) => { return(new IPrimitive[] { new PrimitiveLine(cen.Value, p), new PrimitiveEllipse(cen.Value, (p - cen.Value).Length, drawingPlane.Normal) }); }); if (rad.Cancel) { return(false); } if (rad.HasValue) { circle = new Circle(cen.Value, (rad.Value - cen.Value).Length, drawingPlane.Normal); } else // switch modes { if (rad.Directive == null) { return(false); } switch (rad.Directive) { case "d": mode = CircleMode.Diameter; break; case "i": mode = CircleMode.Isometric; break; } } break; } case CircleMode.Diameter: { var dia = await workspace.InputService.GetPoint(new UserDirective("Enter diameter or [r]adius/[i]sometric", "r", "i"), (p) => { return(new IPrimitive[] { new PrimitiveLine(cen.Value, p), new PrimitiveEllipse(cen.Value, (p - cen.Value).Length, drawingPlane.Normal) }); }); if (dia.Cancel) { return(false); } if (dia.HasValue) { circle = new Circle(cen.Value, (dia.Value - cen.Value).Length * 0.5, drawingPlane.Normal); } else // switch modes { if (dia.Directive == null) { return(false); } switch (dia.Directive) { case "r": mode = CircleMode.Radius; break; case "i": mode = CircleMode.Isometric; break; } } break; } case CircleMode.Isometric: { var isoRad = await workspace.InputService.GetPoint(new UserDirective("Enter isometric-radius or [r]adius/[d]iameter", "r", "d"), (p) => { return(new IPrimitive[] { new PrimitiveLine(cen.Value, p), new PrimitiveEllipse(cen.Value, Vector.SixtyDegrees * (p - cen.Value).Length *MathHelper.SqrtThreeHalves, drawingPlane.Normal, IsoMinorRatio, 0.0, 360.0) }); }); if (isoRad.Cancel) { return(false); } if (isoRad.HasValue) { circle = new Ellipse(cen.Value, Vector.SixtyDegrees * (isoRad.Value - cen.Value).Length * MathHelper.SqrtThreeHalves, IsoMinorRatio, 0.0, 360.0, drawingPlane.Normal); } else // switch modes { if (isoRad.Directive == null) { return(false); } switch (isoRad.Directive) { case "r": mode = CircleMode.Radius; break; case "d": mode = CircleMode.Diameter; break; } } break; } } } } else { switch (cen.Directive) { case "ttr": var firstEntity = await workspace.InputService.GetEntity(new UserDirective("First entity")); if (firstEntity.Cancel || !firstEntity.HasValue) { break; } var secondEntity = await workspace.InputService.GetEntity(new UserDirective("Second entity")); if (secondEntity.Cancel || !secondEntity.HasValue) { break; } var radius = await workspace.InputService.GetDistance(); var ellipse = EditUtilities.Ttr(drawingPlane, firstEntity.Value, secondEntity.Value, radius.Value); if (ellipse != null) { circle = ellipse.ToEntity(); } break; case "2": break; case "th": var first = await workspace.InputService.GetPoint(new UserDirective("First point")); if (first.Cancel || !first.HasValue) { break; } var second = await workspace.InputService.GetPoint(new UserDirective("Second point"), p => new[] { new PrimitiveLine(first.Value, p) }); if (second.Cancel || !second.HasValue) { break; } var third = await workspace.InputService.GetPoint(new UserDirective("Third point"), p => { var c = PrimitiveEllipse.ThreePointCircle(first.Value, second.Value, p); if (c == null) { return new IPrimitive[] { new PrimitiveLine(first.Value, second.Value), new PrimitiveLine(second.Value, p), new PrimitiveLine(p, first.Value) } } ; else { return new IPrimitive[] { new PrimitiveLine(first.Value, second.Value), new PrimitiveLine(second.Value, p), new PrimitiveLine(p, first.Value), c } }; }); if (third.Cancel || !third.HasValue) { break; } var circ = PrimitiveEllipse.ThreePointCircle(first.Value, second.Value, third.Value); if (circ != null) { circle = new Circle(circ.Center, circ.MajorAxis.Length, circ.Normal); } break; } } if (circle != null) { workspace.AddToCurrentLayer(circle); } return(true); }
private Shape CreatePrimitiveEllipse(PrimitiveEllipse ellipse, CadColor?color) { // rendering zero-radius ellipses (and arcs/circles) can cause the WPF layout engine to throw if (ellipse.MajorAxis.LengthSquared == 0.0) { return(null); } // find axis endpoints var projected = ProjectionHelper.Project(ellipse, PlaneProjection); var radiusX = projected.MajorAxis.Length; var radiusY = radiusX * projected.MinorAxisRatio; Shape shape; if (projected.StartAngle == 0.0 && projected.EndAngle == 360.0) { // full circle shape = new Shapes.Ellipse() { Width = radiusX * 2.0, Height = radiusY * 2.0 }; shape.RenderTransform = new RotateTransform() { Angle = Math.Atan2(projected.MajorAxis.Y, projected.MajorAxis.X) * MathHelper.RadiansToDegrees, CenterX = radiusX, CenterY = radiusY }; Canvas.SetLeft(shape, projected.Center.X - radiusX); Canvas.SetTop(shape, projected.Center.Y - radiusY); } else { // arc var endAngle = ellipse.EndAngle; if (endAngle < ellipse.StartAngle) { endAngle += 360.0; } var startPoint = projected.StartPoint(); var endPoint = projected.EndPoint(); shape = new Path() { Data = new GeometryGroup() { Children = new GeometryCollection() { new PathGeometry() { Figures = new PathFigureCollection() { new PathFigure() { StartPoint = new DisplayPoint(startPoint.X, startPoint.Y), Segments = new PathSegmentCollection() { new ArcSegment() { IsLargeArc = (endAngle - ellipse.StartAngle) > 180.0, Point = new DisplayPoint(endPoint.X, endPoint.Y), SweepDirection = SweepDirection.Clockwise, RotationAngle = Math.Atan2(projected.MajorAxis.Y, projected.MajorAxis.X) * MathHelper.RadiansToDegrees, Size = new DisplaySize(radiusX, radiusY) } } }, } } } } }; } SetThicknessBinding(shape); SetColor(shape, color); return(shape); }
public static IPrimitive Offset(Plane drawingPlane, IPrimitive primitive, Point offsetDirection, double offsetDistance) { if (!drawingPlane.Contains(offsetDirection)) { return(null); } IPrimitive result; switch (primitive.Kind) { case PrimitiveKind.Ellipse: var el = (PrimitiveEllipse)primitive; var projection = el.FromUnitCircle.Inverse(); var isInside = projection.Transform((Vector)offsetDirection).LengthSquared <= 1.0; var majorLength = el.MajorAxis.Length; if (isInside && (offsetDistance > majorLength * el.MinorAxisRatio) || (offsetDistance >= majorLength)) { result = null; } else { Vector newMajor; if (isInside) { newMajor = el.MajorAxis.Normalize() * (majorLength - offsetDistance); } else { newMajor = el.MajorAxis.Normalize() * (majorLength + offsetDistance); } result = new PrimitiveEllipse( center: el.Center, majorAxis: newMajor, normal: el.Normal, minorAxisRatio: el.MinorAxisRatio, startAngle: el.StartAngle, endAngle: el.EndAngle, color: el.Color); } break; case PrimitiveKind.Line: // find what side the offset occured on and move both end points var line = (PrimitiveLine)primitive; // normalize to XY plane var picked = drawingPlane.ToXYPlane(offsetDirection); var p1 = drawingPlane.ToXYPlane(line.P1); var p2 = drawingPlane.ToXYPlane(line.P2); var pline = new PrimitiveLine(p1, p2); var perpendicular = new PrimitiveLine(picked, pline.PerpendicularSlope()); var intersection = pline.IntersectionPoint(perpendicular, false); if (intersection.HasValue && intersection.Value != picked) { var offsetVector = (picked - intersection.Value).Normalize() * offsetDistance; offsetVector = drawingPlane.FromXYPlane(offsetVector); result = new PrimitiveLine( p1: line.P1 + offsetVector, p2: line.P2 + offsetVector, color: line.Color); } else { // the selected point was directly on the line result = null; } break; case PrimitiveKind.Point: var point = (PrimitivePoint)primitive; var pointOffsetVector = (offsetDirection - point.Location).Normalize() * offsetDistance; result = new PrimitivePoint(point.Location + pointOffsetVector, point.Color); break; case PrimitiveKind.Text: result = null; break; default: throw new ArgumentException("primitive.Kind"); } return(result); }
public static IEnumerable <Point> GetProjectedVerticies(this PrimitiveEllipse ellipse, Matrix4 transformationMatrix, int maxSeg) { return(ellipse.GetInterestingPoints(maxSeg) .Select(p => transformationMatrix.Transform(p))); }