/// <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> static JtLoops GetPlanViewBoundaryLoops( Element e, 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 ) { // 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 ); } 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> /// 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> /// 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; }