/// <summary> /// Retrieve the room plan view boundary /// polygon loops and convert to 2D integer-based. /// For optimisation and consistency reasons, /// convert all coordinates to integer values in /// millimetres. Revit precision is limited to /// 1/16 of an inch, which is abaut 1.2 mm, anyway. /// </summary> static JtLoops GetRoomLoops(Room room) { SpatialElementBoundaryOptions opt = new SpatialElementBoundaryOptions(); opt.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Center; // loops closed //SpatialElementBoundaryLocation.Finish; // loops not closed IList <IList <BoundarySegment> > loops = room. GetBoundarySegments(opt); int nLoops = loops.Count; JtLoops jtloops = new JtLoops(nLoops); foreach (IList <BoundarySegment> loop in loops) { int nSegments = loop.Count; JtLoop jtloop = new JtLoop(nSegments); XYZ p0 = null; // loop start point XYZ p; // segment start point XYZ q = null; // segment end point foreach (BoundarySegment seg in loop) { // Todo: handle non-linear curve. // Especially: if two long lines have a // short arc in between them, skip the arc // and extend both lines. p = seg.GetCurve().GetEndPoint(0); jtloop.Add(new Point2dInt(p)); Debug.Assert(null == q || q.IsAlmostEqualTo(p), "expected last endpoint to equal current start point"); q = seg.GetCurve().GetEndPoint(1); if (_debug_output) { Debug.Print("{0} --> {1}", Util.PointString(p), Util.PointString(q)); } if (null == p0) { p0 = p; // save loop start point } } Debug.Assert(q.IsAlmostEqualTo(p0), "expected last endpoint to equal loop start point"); jtloops.Add(jtloop); } return(jtloops); }
/// <summary> /// Convert Clipper polygons to JtLoops /// </summary> JtLoops ConvertToLoops(Polygons union) { JtLoops loops = new JtLoops(union.Count); JtLoop loop = new JtLoop(union.First <Polygon>().Count); foreach (Polygon poly in union) { loop.Clear(); foreach (IntPoint p in poly) { loop.Add(new Point2dInt( (int)p.X, (int)p.Y)); } loops.Add(loop); } return(loops); }
/// <summary> /// Retrieve all plan view boundary loops from /// all solids of given element united together. /// If the element is a family instance, transform /// its loops from the instance placement /// coordinate system back to the symbol /// definition one. /// If no geometry can be determined, use the /// bounding box instead. /// </summary> internal static JtLoops GetSolidPlanViewBoundaryLoops( Element e, bool transformInstanceCoordsToSymbolCoords, ref int nFailures) { Autodesk.Revit.Creation.Application creapp = e.Document.Application.Create; JtLoops loops = null; Options opt = new Options(); GeometryElement geo = e.get_Geometry(opt); if (null != geo) { Document doc = e.Document; if (e is FamilyInstance) { if (transformInstanceCoordsToSymbolCoords) { // Retrieve family instance geometry // transformed back to symbol definition // coordinate space by inverting the // family instance placement transformation LocationPoint lp = e.Location as LocationPoint; Transform t = Transform.CreateTranslation( -lp.Point); Transform r = Transform.CreateRotationAtPoint( XYZ.BasisZ, -lp.Rotation, lp.Point); geo = geo.GetTransformed(t * r); } else { Debug.Assert( 1 == geo.Count <GeometryObject>(), "expected as single geometry instance"); Debug.Assert( geo.First <GeometryObject>() is GeometryInstance, "expected as single geometry instance"); geo = (geo.First <GeometryObject>() as GeometryInstance).GetInstanceGeometry(); } } loops = GetPlanViewBoundaryLoopsGeo( creapp, geo, ref nFailures); } if (null == loops || 0 == loops.Count) { Debug.Print( "Unable to determine geometry for " + Util.ElementDescription(e) + "; using bounding box instead."); BoundingBoxXYZ bb; if (e is FamilyInstance) { bb = (e as FamilyInstance).Symbol .get_BoundingBox(null); } else { bb = e.get_BoundingBox(null); } JtLoop loop = new JtLoop(4); loop.Add(new Point2dInt(bb.Min)); loop.Add(new Point2dInt(bb.Max.X, bb.Min.Y)); loop.Add(new Point2dInt(bb.Max)); loop.Add(new Point2dInt(bb.Min.X, bb.Max.Y)); loops.Add(loop); } return(loops); }
/// <summary> /// Return a closed loop of integer-based points /// scaled to millimetres from a given Revit model /// face in feet. /// </summary> internal static JtLoop GetLoop( Autodesk.Revit.Creation.Application creapp, Face face) { JtLoop loop = null; foreach (EdgeArray a in face.EdgeLoops) { int nEdges = a.Size; List <Curve> curves = new List <Curve>(nEdges); XYZ p0 = null; // loop start point XYZ p; // edge start point XYZ q = null; // edge end point // Test ValidateCurveLoops //CurveLoop loopIfc = new CurveLoop(); foreach (Edge e in a) { // This requires post-processing using // SortCurvesContiguous: Curve curve = e.AsCurve(); if (_debug_output) { p = curve.GetEndPoint(0); q = curve.GetEndPoint(1); Debug.Print("{0} --> {1}", Util.PointString(p), Util.PointString(q)); } // This returns the curves already // correctly oriented: curve = e.AsCurveFollowingFace( face); if (_debug_output) { p = curve.GetEndPoint(0); q = curve.GetEndPoint(1); Debug.Print("{0} --> {1} following face", Util.PointString(p), Util.PointString(q)); } curves.Add(curve); // Throws an exception saying "This curve // will make the loop not contiguous. // Parameter name: pCurve" //loopIfc.Append( curve ); } // We never reach this point: //List<CurveLoop> loopsIfc // = new List<CurveLoop>( 1 ); //loopsIfc.Add( loopIfc ); //IList<CurveLoop> loopsIfcOut = ExporterIFCUtils // .ValidateCurveLoops( loopsIfc, XYZ.BasisZ ); // This is no longer needed if we use // AsCurveFollowingFace instead of AsCurve: CurveUtils.SortCurvesContiguous( creapp, curves, _debug_output); q = null; loop = new JtLoop(nEdges); foreach (Curve curve in curves) { // Todo: handle non-linear curve. // Especially: if two long lines have a // short arc in between them, skip the arc // and extend both lines. p = curve.GetEndPoint(0); Debug.Assert(null == q || q.IsAlmostEqualTo(p, 1e-04), string.Format( "expected last endpoint to equal current start point, not distance {0}", (null == q ? 0 : p.DistanceTo(q)))); q = curve.GetEndPoint(1); if (_debug_output) { Debug.Print("{0} --> {1}", Util.PointString(p), Util.PointString(q)); } if (null == p0) { p0 = p; // save loop start point } int n = -1; if (_tessellate_curves && _min_tessellation_curve_length_in_feet < q.DistanceTo(p)) { IList <XYZ> pts = curve.Tessellate(); n = pts.Count; Debug.Assert(1 < n, "expected at least two points"); Debug.Assert(p.IsAlmostEqualTo(pts[0]), "expected tessellation start equal curve start point"); Debug.Assert(q.IsAlmostEqualTo(pts[n - 1]), "expected tessellation end equal curve end point"); if (2 == n) { n = -1; // this is a straight line } else { --n; // skip last point for (int i = 0; i < n; ++i) { loop.Add(new Point2dInt(pts[i])); } } } // If tessellation is disabled, // or curve is too short to tessellate, // or has only two tessellation points, // just add the start point: if (-1 == n) { loop.Add(new Point2dInt(p)); } } Debug.Assert(q.IsAlmostEqualTo(p0, 1e-05), string.Format( "expected last endpoint to equal current start point, not distance {0}", p0.DistanceTo(q))); } return(loop); }