//public void CreateModelLine( XYZ p, XYZ q ) //{ // if( p.IsAlmostEqualTo( q ) ) // { // throw new ArgumentException( // "Expected two different points." ); // } // Line line = Line.CreateBound( p, q ); // if( null == line ) // { // throw new Exception( // "Geometry line creation failed." ); // } // _credoc.NewModelCurve( line, // NewSketchPlanePassLine( line ) ); //} /// <summary> /// Return a new sketch plane containing the given curve. /// Update, later: please note that the Revit API provides /// an overload of the NewPlane method taking a CurveArray /// argument, which could presumably be used instead. /// </summary> SketchPlane NewSketchPlaneContainCurve( Curve curve) { XYZ p = curve.GetEndPoint(0); XYZ normal = GetCurveNormal(curve); //Plane plane = _creapp.NewPlane( normal, p ); // 2016 Plane plane = Plane.CreateByNormalAndOrigin(normal, p); // 2017 #if DEBUG if (!(curve is Line)) { //CurveArray a = _creapp.NewCurveArray(); //a.Append( curve ); //Plane plane2 = _creapp.NewPlane( a ); // 2016 List <Curve> a = new List <Curve>(1); a.Add(curve); CurveLoop b = CurveLoop.Create(a); Plane plane2 = b.GetPlane(); // 2017 Debug.Assert(Util.IsParallel(plane2.Normal, plane.Normal), "expected equal planes"); Debug.Assert(Util.IsZero(plane2.SignedDistanceTo( plane.Origin)), "expected equal planes"); } #endif // DEBUG //return _credoc.NewSketchPlane( plane ); // 2013 return(SketchPlane.Create(_doc, plane)); // 2014 }
/// <summary> /// Project given 3D XYZ point onto plane. /// </summary> public static XYZ ProjectOnto( this Plane plane, XYZ p) { double d = plane.SignedDistanceTo(p); XYZ q = p + d * plane.Normal; Debug.Assert( Util.IsZero(plane.SignedDistanceTo(q)), "expected point on plane to have zero distance to plane"); return(q); }
Transform GetTransformToZ(XYZ v) { Transform t; double a = XYZ.BasisZ.AngleTo(v); if (Util.IsZero(a)) { t = Transform.Identity; } else { XYZ axis = Util.IsEqual(a, Math.PI) ? XYZ.BasisX : v.CrossProduct(XYZ.BasisZ); //t = Transform.get_Rotation( XYZ.Zero, axis, a ); // 2013 t = Transform.CreateRotation(axis, a); // 2014 } return(t); }
/// <summary> /// Determine the plane that a given curve resides in and return its normal vector. /// Ask the curve for its start and end points and some point in the middle. /// The latter can be obtained by asking the curve for its parameter range and /// evaluating it in the middle, or by tessellation. In case of tessellation, /// you could iterate through the tessellation points and use each one together /// with the start and end points to try and determine a valid plane. /// Once one is found, you can add debug assertions to ensure that the other /// tessellation points (if there are any more) are in the same plane. /// In the case of the line, the tessellation only returns two points. /// I once heard that that is the only element that can do that, all /// non-linear curves return at least three. So you could use this property /// to determine that a line is a line (and add an assertion as well, if you like). /// Update, later: please note that the Revit API provides an overload of the /// NewPlane method taking a CurveArray argument. /// </summary> XYZ GetCurveNormal(Curve curve) { IList <XYZ> pts = curve.Tessellate(); int n = pts.Count; Debug.Assert(1 < n, "expected at least two points " + "from curve tessellation"); XYZ p = pts[0]; XYZ q = pts[n - 1]; XYZ v = q - p; XYZ w, normal = null; if (2 == n) { Debug.Assert(curve is Line, "expected non-line element to have " + "more than two tessellation points"); // For non-vertical lines, use Z axis to // span the plane, otherwise Y axis: double dxy = Math.Abs(v.X) + Math.Abs(v.Y); w = (dxy > Util.TolPointOnPlane) ? XYZ.BasisZ : XYZ.BasisY; normal = v.CrossProduct(w).Normalize(); } else { int i = 0; while (++i < n - 1) { w = pts[i] - p; normal = v.CrossProduct(w); if (!normal.IsZeroLength()) { normal = normal.Normalize(); break; } } #if DEBUG { XYZ normal2; while (++i < n - 1) { w = pts[i] - p; normal2 = v.CrossProduct(w); Debug.Assert(normal2.IsZeroLength() || Util.IsZero(normal2.AngleTo(normal)), "expected all points of curve to " + "lie in same plane"); } } #endif // DEBUG } return(normal); }
/* * /// <summary> * /// Return the average of a list of values. * /// Prerequisite: the underlying class T must supply * /// operator*(double) and operator+(const T &). * /// </summary> * T Average<T>( List<T> a ) * { * T result; * bool first = true; * foreach( T x in a ) * { * if( first ) * { * result = x; * } * else * { * result += x; * } * } * return result * ( 1.0 / a.Count ); * } * * XYZ Sum( List<XYZ> a ) * { * XYZ sum = XYZ.Zero; * foreach( XYZ x in a ) * { * sum += x; * } * return sum; * } * * XYZ Average( List<XYZ> a ) * { * return Sum( a ) * (1.0 / a.Count); * } * * XYZ TriangleCenter( List<XYZ> pts ) * { * Debug.Assert( 3 == pts.Count, "expected three points in triangle" ); * return Average( pts ); * } */ /// <summary> /// Return the plane properties of a given polygon, /// i.e. the plane normal, area, and its distance /// from the origin. Cf. also GetSignedPolygonArea. /// </summary> internal static bool GetPolygonPlane( List <XYZ> polygon, out XYZ normal, out double dist, out double area) { normal = XYZ.Zero; dist = area = 0.0; int n = (null == polygon) ? 0 : polygon.Count; bool rc = (2 < n); if (3 == n) { // the general case returns a wrong result for the triangle // ((-1 -1 -1) (1 -1 -1) (-1 -1 1)), so implement specific // code for triangle: XYZ a = polygon[0]; XYZ b = polygon[1]; XYZ c = polygon[2]; XYZ v = b - a; normal = v.CrossProduct(c - a); dist = normal.DotProduct(a); } else if (4 == n) { // more efficient code for 4-sided polygons XYZ a = polygon[0]; XYZ b = polygon[1]; XYZ c = polygon[2]; XYZ d = polygon[3]; normal = new XYZ( (c.Y - a.Y) * (d.Z - b.Z) + (c.Z - a.Z) * (b.Y - d.Y), (c.Z - a.Z) * (d.X - b.X) + (c.X - a.X) * (b.Z - d.Z), (c.X - a.X) * (d.Y - b.Y) + (c.Y - a.Y) * (b.X - d.X)); dist = 0.25 * (normal.X * (a.X + b.X + c.X + d.X) + normal.Y * (a.Y + b.Y + c.Y + d.Y) + normal.Z * (a.Z + b.Z + c.Z + d.Z)); } else if (4 < n) { // general case for n-sided polygons XYZ a; XYZ b = polygon[n - 2]; XYZ c = polygon[n - 1]; XYZ s = XYZ.Zero; for (int i = 0; i < n; ++i) { a = b; b = c; c = polygon[i]; normal = new XYZ( normal.X + b.Y * (c.Z - a.Z), normal.Y + b.Z * (c.X - a.X), normal.Z + b.X * (c.Y - a.Y)); s += c; } dist = s.DotProduct(normal) / n; } if (rc) { // the polygon area is half of the length // of the non-normalized normal vector of the plane: double length = normal.GetLength(); rc = !Util.IsZero(length); Debug.Assert(rc); if (rc) { normal /= length; dist /= length; area = 0.5 * length; } } return(rc); }
/// <summary> /// Determine the elevation boundary profile /// polygons of the exterior vertical planar /// face of the given wall solid. /// </summary> /// <param name="polygons">Return polygonal boundary /// loops of exterior vertical planar face, i.e. /// profile of wall elevation incl. holes</param> /// <param name="solid">Input solid</param> /// <param name="w">Vector pointing along /// wall centre line</param> /// <param name="w">Vector pointing towards /// exterior wall face</param> /// <returns>False if no exterior vertical /// planar face was found, else true</returns> static bool GetProfile( List <List <XYZ> > polygons, Solid solid, XYZ v, XYZ w) { double d, dmax = 0; PlanarFace outermost = null; FaceArray faces = solid.Faces; foreach (Face f in faces) { PlanarFace pf = f as PlanarFace; if (null != pf && Util.IsVertical(pf) && Util.IsZero(v.DotProduct(pf.FaceNormal))) { d = pf.Origin.DotProduct(w); if ((null == outermost) || (dmax < d)) { outermost = pf; dmax = d; } } } if (null != outermost) { XYZ voffset = _offset * w; XYZ p, q = XYZ.Zero; bool first; int i, n; EdgeArrayArray loops = outermost.EdgeLoops; foreach (EdgeArray loop in loops) { List <XYZ> vertices = new List <XYZ>(); first = true; foreach (Edge e in loop) { IList <XYZ> points = e.Tessellate(); p = points[0]; if (!first) { Debug.Assert(p.IsAlmostEqualTo(q), "expected subsequent start point" + " to equal previous end point"); } n = points.Count; q = points[n - 1]; for (i = 0; i < n - 1; ++i) { XYZ a = points[i]; a += voffset; vertices.Add(a); } } q += voffset; Debug.Assert(q.IsAlmostEqualTo(vertices[0]), "expected last end point to equal" + " first start point"); polygons.Add(vertices); } } return(null != outermost); }