/// <summary> /// Moves, scales, and/or rotates the current entity given a 3x3 transformation matrix and a translation vector. /// </summary> /// <param name="transformation">Transformation matrix.</param> /// <param name="translation">Translation vector.</param> /// <remarks>Matrix3 adopts the convention of using column vectors to represent a transformation matrix.</remarks> public override void TransformBy(Matrix3 transformation, Vector3 translation) { // NOTE: this is a generic implementation of the ellipse transformation, // for non rotated ellipses and/or uniform scaling the code can be simplified // rectangle that circumscribe the ellipse double semiMajorAxis = this.MajorAxis * 0.5; double semiMinorAxis = this.MinorAxis * 0.5; Vector2 p1 = new Vector2(-semiMajorAxis, semiMinorAxis); Vector2 p2 = new Vector2(semiMajorAxis, semiMinorAxis); Vector2 p3 = new Vector2(-semiMajorAxis, -semiMinorAxis); Vector2 p4 = new Vector2(semiMajorAxis, -semiMinorAxis); List <Vector2> ocsPoints = MathHelper.Transform(new[] { p1, p2, p3, p4 }, this.Rotation * MathHelper.DegToRad, CoordinateSystem.Object, CoordinateSystem.World); Vector3 p1Prime = new Vector3(ocsPoints[0].X, ocsPoints[0].Y, 0.0); Vector3 p2Prime = new Vector3(ocsPoints[1].X, ocsPoints[1].Y, 0.0); Vector3 p3Prime = new Vector3(ocsPoints[2].X, ocsPoints[2].Y, 0.0); Vector3 p4Prime = new Vector3(ocsPoints[3].X, ocsPoints[3].Y, 0.0); List <Vector3> wcsPoints = MathHelper.Transform(new[] { p1Prime, p2Prime, p3Prime, p4Prime }, this.Normal, CoordinateSystem.Object, CoordinateSystem.World); for (int i = 0; i < wcsPoints.Count; i++) { wcsPoints[i] += this.Center; wcsPoints[i] = transformation * wcsPoints[i]; wcsPoints[i] += translation; } Vector3 newNormal = transformation * this.Normal; if (Vector3.Equals(Vector3.Zero, newNormal)) { newNormal = this.Normal; } List <Vector3> rectPoints = MathHelper.Transform(wcsPoints, newNormal, CoordinateSystem.World, CoordinateSystem.Object); // corners of the transformed rectangle that circumscribe the new ellipse Vector2 pointA = new Vector2(rectPoints[0].X, rectPoints[0].Y); Vector2 pointB = new Vector2(rectPoints[1].X, rectPoints[1].Y); Vector2 pointC = new Vector2(rectPoints[2].X, rectPoints[2].Y); Vector2 pointD = new Vector2(rectPoints[3].X, rectPoints[3].Y); // the new ellipse is tangent at the mid points Vector2 pointM = Vector2.MidPoint(pointA, pointB); Vector2 pointN = Vector2.MidPoint(pointC, pointD); Vector2 pointH = Vector2.MidPoint(pointA, pointC); Vector2 pointK = Vector2.MidPoint(pointB, pointD); // we need to find a fifth point Vector2 origin = Vector2.MidPoint(pointH, pointK); Vector2 pointX = Vector2.MidPoint(pointH, origin); // a point along the OH segment // intersection line AC and line parallel to BC through pointX Vector2 pointY = MathHelper.FindIntersection(pointA, pointC - pointA, pointX, pointC - pointB); if (Vector2.IsNaN(pointY)) { Debug.Assert(false, "The transformation cannot be applied."); return; } // find the fifth point in the ellipse Vector2 pointZ = MathHelper.FindIntersection(pointM, pointX - pointM, pointN, pointY - pointN); if (Vector2.IsNaN(pointZ)) { Debug.Assert(false, "The transformation cannot be applied."); return; } Vector3 oldNormal = this.Normal; double oldRotation = this.Rotation * MathHelper.DegToRad; if (ConicThroughFivePoints.EllipseProperties(pointM, pointN, pointH, pointK, pointZ, out Vector2 _, out double newSemiMajorAxis, out double newSemiMinorAxis, out double newRotation)) { double axis1 = 2 * newSemiMajorAxis; axis1 = MathHelper.IsZero(axis1) ? MathHelper.Epsilon : axis1; double axis2 = 2 * newSemiMinorAxis; axis2 = MathHelper.IsZero(axis2) ? MathHelper.Epsilon : axis2; this.Center = transformation * this.Center + translation; this.MajorAxis = axis1; this.MinorAxis = axis2; this.Rotation = newRotation * MathHelper.RadToDeg; this.Normal = newNormal; }
/// <summary> /// Moves, scales, and/or rotates the current entity given a 3x3 transformation matrix and a translation vector. /// </summary> /// <param name="transformation">Transformation matrix.</param> /// <param name="translation">Translation vector.</param> /// <remarks>Matrix3 adopts the convention of using column vectors to represent a transformation matrix.</remarks> public override void TransformBy(Matrix3 transformation, Vector3 translation) { // NOTE: this is a generic implementation of the ellipse transformation, // for non rotated ellipses and/or uniform scaling the code can be simplified // rectangle that circumscribe the ellipse double semiMajorAxis = this.MajorAxis * 0.5; double semiMinorAxis = this.MinorAxis * 0.5; Vector2 p1 = new Vector2(-semiMajorAxis, semiMinorAxis); Vector2 p2 = new Vector2(semiMajorAxis, semiMinorAxis); Vector2 p3 = new Vector2(-semiMajorAxis, -semiMinorAxis); Vector2 p4 = new Vector2(semiMajorAxis, -semiMinorAxis); List <Vector2> ocsPoints = MathHelper.Transform(new[] { p1, p2, p3, p4 }, this.Rotation * MathHelper.DegToRad, CoordinateSystem.Object, CoordinateSystem.World); Vector3 p1Prime = new Vector3(ocsPoints[0].X, ocsPoints[0].Y, 0.0); Vector3 p2Prime = new Vector3(ocsPoints[1].X, ocsPoints[1].Y, 0.0); Vector3 p3Prime = new Vector3(ocsPoints[2].X, ocsPoints[2].Y, 0.0); Vector3 p4Prime = new Vector3(ocsPoints[3].X, ocsPoints[3].Y, 0.0); List <Vector3> wcsPoints = MathHelper.Transform(new[] { p1Prime, p2Prime, p3Prime, p4Prime }, this.Normal, CoordinateSystem.Object, CoordinateSystem.World); for (int i = 0; i < wcsPoints.Count; i++) { wcsPoints[i] += this.Center; wcsPoints[i] = transformation * wcsPoints[i]; wcsPoints[i] += translation; } Vector3 newNormal = transformation * this.Normal; if (Vector3.Equals(Vector3.Zero, newNormal)) { newNormal = this.Normal; } List <Vector3> rectPoints = MathHelper.Transform(wcsPoints, newNormal, CoordinateSystem.World, CoordinateSystem.Object); // corners of the transformed rectangle that circumscribe the new ellipse Vector2 pointA = new Vector2(rectPoints[0].X, rectPoints[0].Y); Vector2 pointB = new Vector2(rectPoints[1].X, rectPoints[1].Y); Vector2 pointC = new Vector2(rectPoints[2].X, rectPoints[2].Y); Vector2 pointD = new Vector2(rectPoints[3].X, rectPoints[3].Y); // the new ellipse is tangent at the mid points Vector2 pointM = Vector2.MidPoint(pointA, pointB); Vector2 pointN = Vector2.MidPoint(pointC, pointD); Vector2 pointH = Vector2.MidPoint(pointA, pointC); Vector2 pointK = Vector2.MidPoint(pointB, pointD); // we need to find a fifth point Vector2 origin = Vector2.MidPoint(pointH, pointK); Vector2 pointX = Vector2.MidPoint(pointH, origin); // a point along the OH segment // intersection line AC and line parallel to BC through pointX Vector2 pointY = MathHelper.FindIntersection(pointA, pointC - pointA, pointX, pointC - pointB); if (Vector2.IsNaN(pointY)) { Debug.Assert(false, "The transformation cannot be applied."); return; } // find the fifth point in the ellipse Vector2 pointZ = MathHelper.FindIntersection(pointM, pointX - pointM, pointN, pointY - pointN); if (Vector2.IsNaN(pointZ)) { Debug.Assert(false, "The transformation cannot be applied."); return; } Vector3 oldNormal = this.Normal; double oldRotation = this.Rotation; Vector2 newCenter; double newSemiMajorAxis; double newSemiMinorAxis; double newRotation; if (ConicThroughFivePoints.EllipseProperties(pointM, pointN, pointH, pointK, pointZ, out newCenter, out newSemiMajorAxis, out newSemiMinorAxis, out newRotation)) { double axis1 = 2 * newSemiMajorAxis; axis1 = MathHelper.IsZero(axis1) ? MathHelper.Epsilon : axis1; double axis2 = 2 * newSemiMinorAxis; axis2 = MathHelper.IsZero(axis2) ? MathHelper.Epsilon : axis2; this.Center = transformation * this.Center + translation; this.MajorAxis = axis1; this.MinorAxis = axis2; this.Rotation = newRotation * MathHelper.RadToDeg; this.Normal = newNormal; } else { Debug.Assert(false, "The transformation cannot be applied."); return; } if (this.IsFullEllipse) { return; } //if not full ellipse calculate start and end angles Vector2 start = this.PolarCoordinateRelativeToCenter(this.StartAngle); Vector2 end = this.PolarCoordinateRelativeToCenter(this.EndAngle); if (!MathHelper.IsZero(oldRotation)) { double beta = oldRotation * MathHelper.DegToRad; double sinBeta = Math.Sin(beta); double cosBeta = Math.Cos(beta); start = new Vector2(start.X * cosBeta - start.Y * sinBeta, start.X * sinBeta + start.Y * cosBeta); end = new Vector2(end.X * cosBeta - end.Y * sinBeta, end.X * sinBeta + end.Y * cosBeta); } Vector3 pStart = new Vector3(start.X, start.Y, 0.0); Vector3 pEnd = new Vector3(end.X, end.Y, 0.0); List <Vector3> wcsAnglePoints = MathHelper.Transform(new[] { pStart, pEnd }, oldNormal, CoordinateSystem.Object, CoordinateSystem.World); for (int i = 0; i < wcsAnglePoints.Count; i++) { wcsPoints[i] += this.Center; wcsAnglePoints[i] = transformation * wcsAnglePoints[i]; wcsPoints[i] += translation; } List <Vector3> ocsAnglePoints = MathHelper.Transform(wcsAnglePoints, newNormal, CoordinateSystem.World, CoordinateSystem.Object); Vector2 newStart = new Vector2(ocsAnglePoints[0].X, ocsAnglePoints[0].Y); Vector2 newEnd = new Vector2(ocsAnglePoints[1].X, ocsAnglePoints[1].Y); double invert = Math.Sign(transformation.M11 * transformation.M22 * transformation.M33) < 0 ? 180.0 : 0.0; this.StartAngle = invert + Vector2.Angle(newStart) * MathHelper.RadToDeg - this.Rotation; this.EndAngle = invert + Vector2.Angle(newEnd) * MathHelper.RadToDeg - this.Rotation; }