Examples

Problem: Find the parallel projection of an ellipse onto a plane

A parallel projection of an ellipse - or circle - onto a plane is an ellipse again. With V#, you can compute the projected ellipse using the ProjectParallelOn method of the ellipse object.

Following we will construct the projected ellipse per pedes using V# - just for demonstration purposes.

We start with the observation that the diameters of an ellipse - this is, the semimajor and semiminor axes - can be seen a pair of conjugate diameters. A parallel projection transforms these conjugate diameters into a pair of conjugate diameters again - which are, of course, no longer perpendicular to each other in a common case. 

Fig. 1: The projection of two conjugate diameters.

We start with some simple code to project the diameters of an ellipse onto a plane.

 

Parallel projection: conjugate diameter projection

  1. public static Ellipse ProjectParallelOn(Ellipse ellipse, Plane plane, Vector3d viewDirection)
  2. {
  3.     if ( viewDirection.IsOrthogonalTo(plane.NormalVector) )
  4.         throw new ArgumentException("View direction parallel projection plane!");
  5.     if ( viewDirection.IsOrthogonalTo(this.NormalVector) )
  6.         throw new ArgumentException("View direction parallel to circle!");
  7.  
  8.     // Project center, semiminor and semimajor axis endpoints onto
  9.     // the plane. In general, the projected axes are no longer prependicular
  10.     // to each other but conjugate. This allows to apply a Rytz construction
  11.     // on the projected conjugate axes to find the main axes of the ellipse
  12.     Point pt = ellipse.Center + ellipse.SemimajorAxisVector.ToPoint();
  13.     Point qt = ellipse.Center + ellipse.SemiminorAxisVector.ToPoint();
  14.     Point P = pt.ProjectParallelOn(plane, viewDirection);
  15.     Point Q = qt.ProjectParallelOn(plane, viewDirection);
  16.     Point M = ellipse.Center.ProjectParallelOn(plane, viewDirection);
  17.  
  18.     return Ellipse.ConstructFromConjugateDiameters(M, P, Q);
  19. }  

The method calls the function ConstructFomConjugateDiameters, which actually constructs the ellipse from the projected conjugate diameters. The geometric construction is often referred to as Rytz' construction. It comprises several construction steps and is a nightmare of descriptive geometry. Using V#, you will sleep well because you easily code the individual steps one after another.


Fig. 2: The Rytz construction.

Following we give the code with comments using V#.

Parallel projection: Rytz' construction

  1. public static Ellipse ConstructFromConjugateDiameters(Point M, Point P, Point Q)
  2. {
  3.     try
  4.     {
  5.         // The construction requires P-M to be longer than Q-M.
  6.         // This is, swap P and Q if necessary.
  7.         // Use the DeepCopy() method or swapping will fail!
  8.         if ( P.DistanceTo(M) > Q.DistanceTo(M) )
  9.         {
  10.             Point tmp = P.DeepCopy();
  11.             P = Q.DeepCopy();
  12.             Q = tmp.DeepCopy();
  13.         }
  14.  
  15.         // Check if axes are already main axes.
  16.         // This is the case if the conjugate diameters are already orthogonal.
  17.         // Return the ellipse then.
  18.         Vector3d qm = new Vector3d(Q - M);
  19.         Vector3d pm = new Vector3d(P - M);
  20.         if ( pm.IsOrthogonalTo(qm) ) return new Ellipse(M, qm, pm);
  21.  
  22.         // Find the plane the ellipse lies in
  23.         // This time, the easiest is to construct the plane from three points:
  24.         Plane plane = new Plane(M, P, Q);
  25.         // Normalizing may increase robustness:
  26.         plane.Normalize();
  27.  
  28.         // Rotate P by 90° around the normal vector of the plane.
  29.         // The rotation matrix can be easily generated:
  30.         Matrix3d rotationMatrix =
  31.             Matrix3d.RotationArbitraryAxis(plane.NormalVector, 0.5 * Math.PI );
  32.         // The center of rotation is M
  33.         Point rotP = P.Rotate(M, rotationMatrix);
  34.         // The construction requires rotP to be rotated towards Q so that
  35.         // the angle between rotP-M and Q-M is smaller than 90°.
  36.         // This is, mirror rotP if the angle is greater than 90°:
  37.         if ( Vector3d.Angle(qm, new Vector3d(rotP - M)) > 0.5 * Math.PI )
  38.             rotP = -1.0 * ( rotP - M ) + M;
  39.  
  40.         // Construct the Rytz-circle.
  41.         // The center of the Rytz-circle is the midpoint of the edge from
  42.         // rotP to Q. The radius is the distance from M to the center. The
  43.         // circle lies in the plane of the ellipse, so the normal vector of
  44.         // the plane the ellipse lies in is also the normal vector of the circle.
  45.         Point C = new Edge(rotP, Q).MidPoint;
  46.         Circle rytzCircle =
  47.             new Circle(C, M.DistanceTo(C), plane.NormalVector);
  48.  
  49.         // Intersect the Rytz-circle with an orthogonal plane containing
  50.         // the points rotP and the center of the circle. The length of the
  51.         // intersection edge is the diameter of the Rytz-circle. A defining
  52.         // point on the plane may be the center of the Rytz-circle. Direction
  53.         // vectors can be rotP - C and the normal vector of the plane.
  54.         Plane orthogonalPlane =
  55.             new Plane(C, new Vector3d(rotP - C), plane.NormalVector);
  56.         // Normalizing may increase robustness:
  57.         orthogonalPlane.Normalize();
  58.         // Get the intersection edge
  59.         Edge intersectionEdge = orthogonalPlane.Intersect3d(rytzCircle);
  60.  
  61.         // Get main axis length of final ellipse
  62.         // The length of the main axes is as follows:
  63.         double da = Q.DistanceTo(intersectionEdge.StartPoint);
  64.         double db = Q.DistanceTo(intersectionEdge.EndPoint);
  65.         if ( da < db )
  66.         {
  67.             da = db;
  68.             db = Q.DistanceTo(intersectionEdge.StartPoint);
  69.         }
  70.  
  71.         // Get main axes. The direction of the main axes
  72.         // vectors a and b of the ellipse follows from the vectors pointing
  73.         // from M to the end points of the intersection edge.
  74.         // In this construction, a must be longer than b!
  75.         Vector3d a = new Vector3d(intersectionEdge.EndPoint - M);
  76.         Vector3d b = new Vector3d(intersectionEdge.StartPoint - M);
  77.         if ( b.Norm > a.Norm )
  78.         {
  79.             a = new Vector3d(intersectionEdge.StartPoint - M);
  80.             b = new Vector3d(intersectionEdge.EndPoint - M);
  81.         }
  82.  
  83.         // Set main axes length
  84.         // Up to now, a and b only point in the correct direction.
  85.         // The actual length has been determined before:
  86.         a.Norm = da;
  87.         b.Norm = db;
  88.  
  89.         // Construct and return the ellipse
  90.         return new Ellipse(M, a, b);
  91.     }
  92.     catch ( Exception ex )
  93.     {
  94.         throw new ArithmeticException(
  95.             "Rytz construction failed due to numerical problems:\n" +
  96.              ex.Message);
  97.     }
  98. }

<< back