/// <summary> /// Determines whether two objects are equal. /// </summary> public override bool Equals(object obj) { if (obj == null || (!object.ReferenceEquals(this.GetType(), obj.GetType()))) { return(false); } Ellipse e = (Ellipse)obj; if (GeometRi3D.UseAbsoluteTolerance) { if (GeometRi3D.AlmostEqual(this.A, this.B)) { // Ellipse is circle if (GeometRi3D.AlmostEqual(e.A, e.B)) { // Second ellipse also circle return(this.Center == e.Center && GeometRi3D.AlmostEqual(this.A, e.A) && e.Normal.IsParallelTo(this.Normal)); } else { return(false); } } else { return(this.Center == e.Center && GeometRi3D.AlmostEqual(this.A, e.A) && GeometRi3D.AlmostEqual(this.B, e.B) && e.MajorSemiaxis.IsParallelTo(this.MajorSemiaxis) && e.MinorSemiaxis.IsParallelTo(this.MinorSemiaxis)); } } else { double tol = GeometRi3D.Tolerance; GeometRi3D.Tolerance = tol * e.MajorSemiaxis.Norm; GeometRi3D.UseAbsoluteTolerance = true; if (GeometRi3D.AlmostEqual(this.A, this.B)) { // Ellipse is circle if (GeometRi3D.AlmostEqual(e.A, e.B)) { // Second ellipse also circle bool res1 = this.Center == e.Center && GeometRi3D.AlmostEqual(this.A, e.A); GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; bool res2 = e.Normal.IsParallelTo(this.Normal); return(res1 && res2); } else { GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; return(false); } } else { bool res1 = this.Center == e.Center && GeometRi3D.AlmostEqual(this.A, e.A) && GeometRi3D.AlmostEqual(this.B, e.B); GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; bool res2 = e.MajorSemiaxis.IsParallelTo(this.MajorSemiaxis) && e.MinorSemiaxis.IsParallelTo(this.MinorSemiaxis); return(res1 && res2); } } }
/// <summary> /// Intersection of ellipsoid with plane. /// Returns 'null' (no intersection) or object of type 'Point3d' or 'Ellipse'. /// </summary> public object IntersectionWith(Plane3d plane) { // Solution 1: // Peter Paul Klein // On the Ellipsoid and Plane Intersection Equation // Applied Mathematics, 2012, 3, 1634-1640 (DOI:10.4236/am.2012.311226) // Solution 2: // Sebahattin Bektas // Intersection of an Ellipsoid and a Plane // International Journal of Research in Engineering and Applied Sciences, VOLUME 6, ISSUE 6 (June, 2016) Coord3d lc = new Coord3d(_point, _v1, _v2, "LC1"); plane.SetCoord(lc); double Ax, Ay, Az, Ad; double a, b, c; if (Abs(plane.C) >= Abs(plane.A) && Abs(plane.C) >= Abs(plane.B)) { a = this.A; b = this.B; c = this.C; } else { lc = new Coord3d(_point, _v2, _v3, "LC2"); plane.SetCoord(lc); if (Abs(plane.C) >= Abs(plane.A) && Abs(plane.C) >= Abs(plane.B)) { a = this.B; b = this.C; c = this.A; } else { lc = new Coord3d(_point, _v3, _v1, "LC3"); plane.SetCoord(lc); a = this.C; b = this.A; c = this.B; } } Ax = plane.A; Ay = plane.B; Az = plane.C; Ad = plane.D; double tmp = (Az * Az * c * c); double AA = 1.0 / (a * a) + Ax * Ax / tmp; double BB = 2.0 * Ax * Ay / tmp; double CC = 1.0 / (b * b) + Ay * Ay / tmp; double DD = 2.0 * Ax * Ad / tmp; double EE = 2.0 * Ay * Ad / tmp; double FF = Ad * Ad / tmp - 1.0; double det = 4.0 * AA * CC - BB * BB; if (GeometRi3D.AlmostEqual(det, 0)) { return(null); } double X0 = (BB * EE - 2 * CC * DD) / det; double Y0 = (BB * DD - 2 * AA * EE) / det; double Z0 = -(Ax * X0 + Ay * Y0 + Ad) / Az; Point3d P0 = new Point3d(X0, Y0, Z0, lc); if (P0.IsOnBoundary(this)) { // the plane is tangent to ellipsoid return(P0); } else if (P0.IsInside(this)) { Vector3d q = P0.ToVector.ConvertTo(lc); Matrix3d D1 = Matrix3d.DiagonalMatrix(1 / a, 1 / b, 1 / c); Vector3d r = plane.Normal.ConvertTo(lc).OrthogonalVector.Normalized; Vector3d s = plane.Normal.ConvertTo(lc).Cross(r).Normalized; double omega = 0; double qq, qr, qs, rr, ss, rs; if (!GeometRi3D.AlmostEqual((D1 * r) * (D1 * s), 0)) { rr = (D1 * r) * (D1 * r); rs = (D1 * r) * (D1 * s); ss = (D1 * s) * (D1 * s); if (GeometRi3D.AlmostEqual(rr - ss, 0)) { omega = PI / 4; } else { omega = 0.5 * Atan(2.0 * rs / (rr - ss)); } Vector3d rprim = Cos(omega) * r + Sin(omega) * s; Vector3d sprim = -Sin(omega) * r + Cos(omega) * s; r = rprim; s = sprim; } qq = (D1 * q) * (D1 * q); qr = (D1 * q) * (D1 * r); qs = (D1 * q) * (D1 * s); rr = (D1 * r) * (D1 * r); ss = (D1 * s) * (D1 * s); double d = qq - qr * qr / rr - qs * qs / ss; AA = Sqrt((1 - d) / rr); BB = Sqrt((1 - d) / ss); return(new Ellipse(P0, AA * r, BB * s)); } else { return(null); } }
private object _line_intersection(Line3d l, double t0, double t1) { // Smith's algorithm: // "An Efficient and Robust Ray–Box Intersection Algorithm" // Amy Williams, Steve Barrus, R. Keith Morley, Peter Shirley // http://www.cs.utah.edu/~awilliam/box/box.pdf // Modified to allow tolerance based checks // Relative tolerance ================================ if (!GeometRi3D.UseAbsoluteTolerance) { double tol = GeometRi3D.Tolerance; GeometRi3D.Tolerance = tol * this.Diagonal; GeometRi3D.UseAbsoluteTolerance = true; object result = _line_intersection(l, t0, t1); GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; return(result); } //==================================================== // Define local CS aligned with box Coord3d local_CS = new Coord3d(this.Center, this.Orientation.ConvertToGlobal().ToRotationMatrix, "local_CS"); Point3d Pmin = this.P1.ConvertTo(local_CS); Point3d Pmax = this.P7.ConvertTo(local_CS); l = new Line3d(l.Point.ConvertTo(local_CS), l.Direction.ConvertTo(local_CS).Normalized); double tmin, tmax, tymin, tymax, tzmin, tzmax; double divx = 1 / l.Direction.X; if (divx >= 0) { tmin = (Pmin.X - l.Point.X) * divx; tmax = (Pmax.X - l.Point.X) * divx; } else { tmin = (Pmax.X - l.Point.X) * divx; tmax = (Pmin.X - l.Point.X) * divx; } double divy = 1 / l.Direction.Y; if (divy >= 0) { tymin = (Pmin.Y - l.Point.Y) * divy; tymax = (Pmax.Y - l.Point.Y) * divy; } else { tymin = (Pmax.Y - l.Point.Y) * divy; tymax = (Pmin.Y - l.Point.Y) * divy; } if (GeometRi3D.Greater(tmin, tymax) || GeometRi3D.Greater(tymin, tmax)) { return(null); } if (GeometRi3D.Greater(tymin, tmin)) { tmin = tymin; } if (GeometRi3D.Smaller(tymax, tmax)) { tmax = tymax; } double divz = 1 / l.Direction.Z; if (divz >= 0) { tzmin = (Pmin.Z - l.Point.Z) * divz; tzmax = (Pmax.Z - l.Point.Z) * divz; } else { tzmin = (Pmax.Z - l.Point.Z) * divz; tzmax = (Pmin.Z - l.Point.Z) * divz; } if (GeometRi3D.Greater(tmin, tzmax) || GeometRi3D.Greater(tzmin, tmax)) { return(null); } if (GeometRi3D.Greater(tzmin, tmin)) { tmin = tzmin; } if (GeometRi3D.Smaller(tzmax, tmax)) { tmax = tzmax; } // Now check the overlapping portion of the segments // This part is missing in the original algorithm if (GeometRi3D.Greater(tmin, t1)) { return(null); } if (GeometRi3D.Smaller(tmax, t0)) { return(null); } if (GeometRi3D.Smaller(tmin, t0)) { tmin = t0; } if (GeometRi3D.Greater(tmax, t1)) { tmax = t1; } if (GeometRi3D.AlmostEqual(tmin, tmax)) { return(l.Point.Translate(tmin * l.Direction)); } else { return(new Segment3d(l.Point.Translate(tmin * l.Direction), l.Point.Translate(tmax * l.Direction))); } }
/// <summary> /// Intersection of ellipse with line. /// Returns 'null' (no intersection) or object of type 'Point3d' or 'Segment3d'. /// </summary> public object IntersectionWith(Line3d l) { // Relative tolerance ================================ if (!GeometRi3D.UseAbsoluteTolerance) { double tol = GeometRi3D.Tolerance; GeometRi3D.Tolerance = tol * this.A; GeometRi3D.UseAbsoluteTolerance = true; object result = this.IntersectionWith(l); GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; return(result); } //==================================================== if (l.Direction.IsOrthogonalTo(this.Normal)) { if (l.Point.BelongsTo(new Plane3d(this.Center, this.Normal))) { // coplanar objects // Find intersection of line and ellipse (2D) // Solution from: http://www.ambrsoft.com/TrigoCalc/Circles2/Ellipse/EllipseLine.htm Coord3d local_coord = new Coord3d(this.Center, this._v1, this._v2); Point3d p = l.Point.ConvertTo(local_coord); Vector3d v = l.Direction.ConvertTo(local_coord); double a = this.A; double b = this.B; if (Abs(v.Y / v.X) > 100) { // line is almost vertical, rotate local coord local_coord = new Coord3d(this.Center, this._v2, this._v1); p = l.Point.ConvertTo(local_coord); v = l.Direction.ConvertTo(local_coord); a = this.B; b = this.A; } // Line equation in form: y = mx + c double m = v.Y / v.X; double c = p.Y - m * p.X; double amb = Math.Pow(a, 2) * Math.Pow(m, 2) + Math.Pow(b, 2); double det = amb - Math.Pow(c, 2); if (det < -GeometRi3D.Tolerance) { return(null); } else if (GeometRi3D.AlmostEqual(det, 0)) { double x = -Math.Pow(a, 2) * m * c / amb; double y = Math.Pow(b, 2) * c / amb; return(new Point3d(x, y, 0, local_coord)); } else { double x1 = (-Math.Pow(a, 2) * m * c + a * b * Sqrt(det)) / amb; double x2 = (-Math.Pow(a, 2) * m * c - a * b * Sqrt(det)) / amb; double y1 = (Math.Pow(b, 2) * c + a * b * m * Sqrt(det)) / amb; double y2 = (Math.Pow(b, 2) * c - a * b * m * Sqrt(det)) / amb; return(new Segment3d(new Point3d(x1, y1, 0, local_coord), new Point3d(x2, y2, 0, local_coord))); } } else { // parallel objects return(null); } } else { // Line intersects ellipse' plane Point3d p = (Point3d)l.IntersectionWith(new Plane3d(this.Center, this.Normal)); if (p.BelongsTo(this)) { return(p); } else { return(null); } } }
/// <summary> /// Determines whether two objects are equal. /// </summary> public override bool Equals(object obj) { if (obj == null || (!object.ReferenceEquals(this.GetType(), obj.GetType()))) { return(false); } Ellipsoid e = (Ellipsoid)obj; if (GeometRi3D.UseAbsoluteTolerance) { if (this.Center != e.Center) { return(false); } if (GeometRi3D.AlmostEqual(this.A, this.B) && GeometRi3D.AlmostEqual(this.A, this.C)) { // Ellipsoid is sphere if (GeometRi3D.AlmostEqual(e.A, e.B) && GeometRi3D.AlmostEqual(e.A, e.C)) { // Second ellipsoid also sphere return(GeometRi3D.AlmostEqual(this.A, e.A)); } else { return(false); } } else if (GeometRi3D.AlmostEqual(this.A, this.B) && GeometRi3D.AlmostEqual(e.A, e.B)) { return(GeometRi3D.AlmostEqual(this.A, e.A) && GeometRi3D.AlmostEqual(this.C, e.C) && e.SemiaxisC.IsParallelTo(this.SemiaxisC)); } else if (GeometRi3D.AlmostEqual(this.A, this.C) && GeometRi3D.AlmostEqual(e.A, e.C)) { return(GeometRi3D.AlmostEqual(this.A, e.A) && GeometRi3D.AlmostEqual(this.B, e.B) && e.SemiaxisB.IsParallelTo(this.SemiaxisB)); } else if (GeometRi3D.AlmostEqual(this.C, this.B) && GeometRi3D.AlmostEqual(e.C, e.B)) { return(GeometRi3D.AlmostEqual(this.A, e.A) && GeometRi3D.AlmostEqual(this.C, e.C) && e.SemiaxisA.IsParallelTo(this.SemiaxisA)); } else { return(GeometRi3D.AlmostEqual(this.A, e.A) && e.SemiaxisA.IsParallelTo(this.SemiaxisA) && GeometRi3D.AlmostEqual(this.B, e.B) && e.SemiaxisB.IsParallelTo(this.SemiaxisB) && GeometRi3D.AlmostEqual(this.C, e.C) && e.SemiaxisC.IsParallelTo(this.SemiaxisC)); } } else { double tol = GeometRi3D.Tolerance; GeometRi3D.Tolerance = tol * e.SemiaxisA.Norm; GeometRi3D.UseAbsoluteTolerance = true; if (this.Center != e.Center) { GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; return(false); } if (GeometRi3D.AlmostEqual(this.A, this.B) && GeometRi3D.AlmostEqual(this.A, this.C)) { // Ellipsoid is sphere if (GeometRi3D.AlmostEqual(e.A, e.B) && GeometRi3D.AlmostEqual(e.A, e.C)) { // Second ellipsoid also sphere bool res = GeometRi3D.AlmostEqual(this.A, e.A); GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; return(res); } else { GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; return(false); } } else if (GeometRi3D.AlmostEqual(this.A, this.B) && GeometRi3D.AlmostEqual(e.A, e.B)) { bool res1 = GeometRi3D.AlmostEqual(this.A, e.A) && GeometRi3D.AlmostEqual(this.C, e.C); GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; bool res2 = e.SemiaxisC.IsParallelTo(this.SemiaxisC); return(res1 && res2); } else if (GeometRi3D.AlmostEqual(this.A, this.C) && GeometRi3D.AlmostEqual(e.A, e.C)) { bool res1 = GeometRi3D.AlmostEqual(this.A, e.A) && GeometRi3D.AlmostEqual(this.B, e.B); GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; bool res2 = e.SemiaxisB.IsParallelTo(this.SemiaxisB); return(res1 && res2); } else if (GeometRi3D.AlmostEqual(this.C, this.B) && GeometRi3D.AlmostEqual(e.C, e.B)) { bool res1 = GeometRi3D.AlmostEqual(this.A, e.A) && GeometRi3D.AlmostEqual(this.C, e.C); GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; bool res2 = e.SemiaxisA.IsParallelTo(this.SemiaxisA); return(res1 && res2); } else { bool res1 = GeometRi3D.AlmostEqual(this.A, e.A) && GeometRi3D.AlmostEqual(this.B, e.B) && GeometRi3D.AlmostEqual(this.C, e.C); GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; bool res2 = e.SemiaxisA.IsParallelTo(this.SemiaxisA) && e.SemiaxisB.IsParallelTo(this.SemiaxisB) && e.SemiaxisC.IsParallelTo(this.SemiaxisC); return(res1 && res2); } } }
/// <summary> /// Calculates the point on the ellipsoid's boundary closest to given point. /// </summary> public Point3d ClosestPoint(Point3d p) { // Algorithm by Dr. Robert Nurnberg // http://wwwf.imperial.ac.uk/~rn/distance2ellipse.pdf Coord3d local_coord = new Coord3d(this.Center, this._v1, this._v2); p = p.ConvertTo(local_coord); if (GeometRi3D.AlmostEqual(p.X, 0) && GeometRi3D.AlmostEqual(p.Y, 0)) { // Center point, choose any minor-axis return(new Point3d(0, C, 0, local_coord)); } double theta = Atan2(A * p.Y, B * p.X); double phi = Atan2(p.Z, C * Sqrt((p.X * p.X) / (A * A) + (p.Y * p.Y) / (B * B))); int iter = 0; int max_iter = 100; Point3d n0 = p.Copy(); while (iter < max_iter) { iter += 1; double ct = Cos(theta); double st = Sin(theta); double cp = Cos(phi); double sp = Sin(phi); double F1 = (A * A - B * B) * ct * st * cp - p.X * A * st + p.Y * B * ct; double F2 = (A * A * ct * ct + B * B * st * st - C * C) * sp * cp - p.X * A * sp * ct - p.Y * B * sp * st + p.Z * C * cp; double a11 = (A * A - B * B) * (ct * ct - st * st) * cp - p.X * A * ct - p.Y * B * st; double a12 = -(A * A - B * B) * ct * st * sp; double a21 = -2.0 * (A * A - B * B) * ct * st * cp * sp + p.X * A * sp * st - p.Y * B * sp * ct; double a22 = (A * A * ct * ct + B * B * st * st - C * C) * (cp * cp - sp * sp) - p.X * A * cp * ct - p.Y * B * cp * st - p.Z * C * sp; double det = a11 * a22 - a12 * a21; if (det == 0) { throw new Exception("Zero determinant"); } // Calc reverse matrix B[ij] = A[ij]^-1 double b11 = a22 / det; double b12 = -a12 / det; double b21 = -a21 / det; double b22 = a11 / det; theta = theta - (b11 * F1 + b12 * F2); phi = phi - (b21 * F1 + b22 * F2); Point3d n = new Point3d(A * Cos(phi) * Cos(theta), B * Cos(phi) * Sin(theta), C * Sin(phi), local_coord); if (n0.DistanceTo(n) < GeometRi3D.Tolerance) { return(n); } n0 = n.Copy(); } return(n0); }
/// <summary> /// Intersection of two circles. /// Returns 'null' (no intersection) or object of type 'Circle3d', 'Point3d' or 'Segment3d'. /// In 2D (coplanar circles) the segment will define two intersection points. /// </summary> public object IntersectionWith(Circle3d c) { // Relative tolerance ================================ if (!GeometRi3D.UseAbsoluteTolerance) { double tol = GeometRi3D.Tolerance; GeometRi3D.Tolerance = tol * Max(this.R, c.R); GeometRi3D.UseAbsoluteTolerance = true; object result = this.IntersectionWith(c); GeometRi3D.UseAbsoluteTolerance = false; GeometRi3D.Tolerance = tol; return result; } //==================================================== if (this.Normal.IsParallelTo(c.Normal)) { if (this.Center.BelongsTo(new Plane3d(c.Center, c.Normal))) { // Coplanar objects // Search 2D intersection of two circles // Equal circles if (this.Center == c.Center && GeometRi3D.AlmostEqual(this.R, c.R)) { return this.Copy(); } double d = this.Center.DistanceTo(c.Center); // Separated circles if (GeometRi3D.Greater(d, this.R + c.R)) return null; // One circle inside the other if (d < Abs(this.R - c.R) - GeometRi3D.Tolerance) return null; // Outer tangency if (GeometRi3D.AlmostEqual(d, this.R + c.R)) { Vector3d vec = new Vector3d(this.Center, c.Center); return this.Center.Translate(this.R * vec.Normalized); } // Inner tangency if (Abs(Abs(this.R - c.R) - d) < GeometRi3D.Tolerance) { Vector3d vec = new Vector3d(this.Center, c.Center); if (this.R > c.R) { return this.Center.Translate(this.R * vec.Normalized); } else { return this.Center.Translate(-this.R * vec.Normalized); } } // intersecting circles // Create local CS with origin in circle's center Vector3d vec1 = new Vector3d(this.Center, c.Center); Vector3d vec2 = vec1.Cross(this.Normal); Coord3d local_cs = new Coord3d(this.Center, vec1, vec2); double x = 0.5 * (d * d - c.R * c.R + this.R * this.R) / d; double y = 0.5 * Sqrt((-d + c.R - this.R) * (-d - c.R + this.R) * (-d + c.R + this.R) * (d + c.R + this.R)) / d; Point3d p1 = new Point3d(x, y, 0, local_cs); Point3d p2 = new Point3d(x, -y, 0, local_cs); return new Segment3d(p1, p2); } else { // parallel objects return null; } } else { // Check 3D intersection Plane3d plane = new Plane3d(this.Center, this.Normal); object obj = plane.IntersectionWith(c); if (obj == null) { return null; } else if (obj.GetType() == typeof(Point3d)) { Point3d p = (Point3d)obj; if (p.BelongsTo(c)) { return p; } else { return null; } } else { Segment3d s = (Segment3d)obj; return s.IntersectionWith(this); } } }
/// <summary> /// Get intersection of segment with other segment. /// Returns 'null' (no intersection) or object of type 'Point3d' or 'Segment3d'. /// </summary> public object IntersectionWith(Segment3d s) { if (this == s) { return(this.Copy()); } object obj = this.ToLine.IntersectionWith(s); if (obj == null) { return(null); } if (obj.GetType() == typeof(Point3d)) { Point3d p = (Point3d)obj; if (p.BelongsTo(this) && p.BelongsTo(s)) { return(p); } else { return(null); } } else if (obj.GetType() == typeof(Segment3d)) { // Segments are collinear // Relative tolerance check ================================ double tol = GeometRi3D.Tolerance; if (!GeometRi3D.UseAbsoluteTolerance) { tol = GeometRi3D.Tolerance * Max(this.Length, s.Length); } //========================================================== // Create local CS with X-axis along segment 's' Vector3d v2 = s.ToVector.OrthogonalVector; Coord3d cs = new Coord3d(s.P1, s.ToVector, v2); double x1 = 0.0; double x2 = s.Length; double x3 = this.P1.ConvertTo(cs).X; double x4 = this.P2.ConvertTo(cs).X; if (GeometRi3D.Smaller(Max(x3, x4), x1, tol) || GeometRi3D.Greater(Min(x3, x4), x2, tol)) { return(null); } if (GeometRi3D.AlmostEqual(Max(x3, x4), x1, tol)) { return(new Point3d(x1, 0, 0, cs)); } if (GeometRi3D.AlmostEqual(Min(x3, x4), x2, tol)) { return(new Point3d(x2, 0, 0, cs)); } if (GeometRi3D.Smaller(Min(x3, x4), x1, tol) && GeometRi3D.Greater(Max(x3, x4), x2, tol)) { return(s.Copy()); } if (GeometRi3D.Greater(Min(x3, x4), x1, tol) && GeometRi3D.Smaller(Max(x3, x4), x2, tol)) { return(this.Copy()); } if (GeometRi3D.Smaller(Min(x3, x4), x1, tol)) { return(new Segment3d(new Point3d(x1, 0, 0, cs), new Point3d(Max(x3, x4), 0, 0, cs))); } if (GeometRi3D.Greater(Max(x3, x4), x2, tol)) { return(new Segment3d(new Point3d(x2, 0, 0, cs), new Point3d(Min(x3, x4), 0, 0, cs))); } return(null); } else { return(null); } }
/// <summary> /// Intersection of ellipse with plane. /// Returns 'null' (no intersection) or object of type 'Ellipse', 'Point3d' or 'Segment3d'. /// </summary> public object IntersectionWith(Plane3d s) { if (this.Normal.IsParallelTo(s.Normal)) { if (this.Center.BelongsTo(s)) { // coplanar objects return(this.Copy()); } else { // parallel objects return(null); } } else { Line3d l = (Line3d)s.IntersectionWith(new Plane3d(this.Center, this.Normal)); Coord3d local_coord = new Coord3d(this.Center, this._v1, this._v2); Point3d p = l.Point.ConvertTo(local_coord); Vector3d v = l.Direction.ConvertTo(local_coord); double a = this.A; double b = this.B; if (Abs(v.Y / v.X) > 100) { // line is almost vertical, rotate local coord local_coord = new Coord3d(this.Center, this._v2, this._v1); p = l.Point.ConvertTo(local_coord); v = l.Direction.ConvertTo(local_coord); a = this.B; b = this.A; } // Find intersection of line and ellipse (2D) // Solution from: http://www.ambrsoft.com/TrigoCalc/Circles2/Ellipse/EllipseLine.htm // Line equation in form: y = mx + c double m = v.Y / v.X; double c = p.Y - m * p.X; double amb = Math.Pow(a, 2) * Math.Pow(m, 2) + Math.Pow(b, 2); double det = amb - Math.Pow(c, 2); if (det < -GeometRi3D.Tolerance) { return(null); } else if (GeometRi3D.AlmostEqual(det, 0)) { double x = -Math.Pow(a, 2) * m * c / amb; double y = Math.Pow(b, 2) * c / amb; return(new Point3d(x, y, 0, local_coord)); } else { double x1 = (-Math.Pow(a, 2) * m * c + a * b * Sqrt(det)) / amb; double x2 = (-Math.Pow(a, 2) * m * c - a * b * Sqrt(det)) / amb; double y1 = (Math.Pow(b, 2) * c + a * b * m * Sqrt(det)) / amb; double y2 = (Math.Pow(b, 2) * c - a * b * m * Sqrt(det)) / amb; return(new Segment3d(new Point3d(x1, y1, 0, local_coord), new Point3d(x2, y2, 0, local_coord))); } } }