/// <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> /// List all the loops retrieved /// from the given element. /// </summary> static void ListLoops( Element e, JtLoops loops ) { int nLoops = loops.Count; Debug.Print( "{0} has {1}{2}", Util.ElementDescription( e ), Util.PluralString( nLoops, "loop" ), Util.DotOrColon( nLoops ) ); int i = 0; foreach( JtLoop loop in loops ) { Debug.Print( " {0}: {1}", i++, loop.ToString() ); } }
/// <summary> /// Retrieve all plan view boundary loops from /// all solids of given element. This initial /// version passes each solid encountered in the /// given element to the ExtrusionAnalyzer one /// at a time, which obviously results in multiple /// loops, many of which are contained within the /// others. An updated version unites all the /// solids first and then uses the ExtrusionAnalyzer /// once only to obtain the true outside shadow /// contour. /// </summary> static JtLoops GetPlanViewBoundaryLoopsMultiple( Element e, ref int nFailures) { Autodesk.Revit.Creation.Application creapp = e.Document.Application.Create; JtLoops loops = new JtLoops( 1 ); //int nSolids = 0; Options opt = new Options(); GeometryElement geo = e.get_Geometry( opt ); if( null != geo ) { Document doc = e.Document; if( e is FamilyInstance ) { geo = geo.GetTransformed( Transform.Identity ); } //GeometryInstance inst = null; foreach( GeometryObject obj in geo ) { AddLoops( creapp, loops, obj, ref nFailures ); //inst = obj as GeometryInstance; } //if( 0 == nSolids && null != inst ) //{ // geo = inst.GetSymbolGeometry(); // foreach( GeometryObject obj in geo ) // { // AddLoops( creapp, loops, obj, ref nFailures ); // } //} } return loops; }
/// <summary> /// Add all plan view boundary loops from /// given solid to the list of loops. /// The creation application argument is used to /// reverse the extrusion analyser output curves /// in case they are badly oriented. /// </summary> /// <returns>Number of loops added</returns> static int AddLoops( Autodesk.Revit.Creation.Application creapp, JtLoops loops, GeometryObject obj, ref int nExtrusionAnalysisFailures) { int nAdded = 0; Solid solid = obj as Solid; if( null != solid && 0 < solid.Faces.Size ) { //Plane plane = new Plane(XYZ.BasisX, // XYZ.BasisY, XYZ.Zero); // 2016 Plane plane = Plane.CreateByOriginAndBasis( XYZ.Zero, XYZ.BasisX, XYZ.BasisY ); // 2017 ExtrusionAnalyzer extrusionAnalyzer = null; try { extrusionAnalyzer = ExtrusionAnalyzer.Create( solid, plane, XYZ.BasisZ ); } catch( Autodesk.Revit.Exceptions .InvalidOperationException ) { ++nExtrusionAnalysisFailures; return nAdded; } Face face = extrusionAnalyzer .GetExtrusionBase(); loops.Add( GetLoop( creapp, face ) ); ++nAdded; } return nAdded; }
/// <summary> /// Retrieve all plan view boundary loops from /// all solids of the given element geometry /// united together. /// </summary> internal static JtLoops GetPlanViewBoundaryLoopsGeo( Autodesk.Revit.Creation.Application creapp, GeometryElement geo, ref int nFailures) { Solid union = null; Plane plane = Plane.CreateByOriginAndBasis( XYZ.Zero, XYZ.BasisX, XYZ.BasisY ); foreach( GeometryObject obj in geo ) { Solid solid = obj as Solid; if( null != solid && 0 < solid.Faces.Size ) { // Some solids, e.g. in the standard // content 'Furniture Chair - Office' // cause an extrusion analyser failure, // so skip adding those. try { ExtrusionAnalyzer extrusionAnalyzer = ExtrusionAnalyzer.Create( solid, plane, XYZ.BasisZ ); } catch( Autodesk.Revit.Exceptions .InvalidOperationException ) { solid = null; ++nFailures; } if( null != solid ) { if( null == union ) { union = solid; } else { try { union = BooleanOperationsUtils .ExecuteBooleanOperation( union, solid, BooleanOperationsType.Union ); } catch( Autodesk.Revit.Exceptions .InvalidOperationException ) { ++nFailures; } } } } } JtLoops loops = new JtLoops( 1 ); AddLoops( creapp, loops, union, ref nFailures ); return loops; }
/// <summary> /// Display sheet, the views it contains, the BIM /// parts and family instances they display in a /// temporary form generated on the fly. /// </summary> /// <param name="owner">Owner window</param> /// <param name="caption">Form caption</param> /// <param name="modal">Modal versus modeless</param> /// <param name="roomLoops">Sheet and viewport boundary loops</param> /// <param name="geometryLoopss">Family symbol and part geometry</param> /// <param name="familyInstances">Family instances</param> public static Bitmap DisplaySheet( ElementId sheetId, JtLoops sheetViewportLoops, SheetModelCollections modelCollections ) { // Source rectangle. JtBoundingBox2dInt bbFrom = sheetViewportLoops .BoundingBox; // Adjust target rectangle height to the // displayee loop height. int width = _form_width; int height = (int) ( width * bbFrom.AspectRatio + 0.5 ); // Specify transformation target rectangle // including a margin. int top = 0; int left = 0; int bottom = height - ( _margin + _margin ); Point[] parallelogramPoints = new Point[] { new Point( left + _margin, bottom ), // upper left new Point( left + width - _margin, bottom ), // upper right new Point( left + _margin, top + _margin ) // lower left }; // Transform from native loop coordinate system // (sheet) to target display coordinates form). Matrix transformSheetBbToForm = new Matrix( bbFrom.Rectangle, parallelogramPoints ); Bitmap bmp = new Bitmap( width, height ); Graphics graphics = Graphics.FromImage( bmp ); graphics.Clear( System.Drawing.Color.White ); // Display sheet and viewport rectangles. DrawLoopsOnGraphics( graphics, sheetViewportLoops.GetGraphicsPathLines(), transformSheetBbToForm ); // Iterate over the views and display the // elements for each one appropriately // scaled and translated to fit. List<ViewData> views = modelCollections .ViewsInSheet[sheetId]; Dictionary<ElementId, GeomData> geometryLookup = modelCollections.Symbols; Matrix transformBimToViewport; JtBoundingBox2dInt bbTo; JtLoop loop; foreach( ViewData view in views ) { ElementId vid = view.Id; if( !modelCollections.BimelsInViews .ContainsKey( vid ) ) { // This is not a floor plan view, so // we have nothing to display in it. continue; } // Determine transform from model space in mm // to the viewport associated with this view. bbFrom = view.BimBoundingBox; bbTo = view.ViewportBoundingBox; Debug.Print( view.ToString() ); // Adjust target rectangle height to the // displayee loop height. //height = (int) ( width * bbFrom.AspectRatio + 0.5 ); // Specify transformation target rectangle // including a margin, and center the target // rectangle vertically. top = bbTo.Min.Y + _margin2; left = bbTo.Min.X + _margin2; bottom = bbTo.Max.Y - _margin2; width = bbTo.Width - (_margin2 + _margin2); parallelogramPoints = new Point[] { new Point( left, top ), // upper left new Point( left + width, top ), // upper right new Point( left, bottom ) // lower left }; // Transform from native loop coordinate system // (sheet) to target display coordinates form). transformBimToViewport = new Matrix( bbFrom.Rectangle, parallelogramPoints ); // Retrieve the list of BIM elements // displayed in this view. List<ObjData> bimels = modelCollections .BimelsInViews[vid]; List<Point[]> loops = new List<Point[]>( 1 ); loops.Add( new Point[] { } ); Matrix placement = new Matrix(); foreach( ObjData bimel in bimels ) { placement.Reset(); InstanceData inst = bimel as InstanceData; if( null != inst ) { loop = geometryLookup[inst.Symbol].Loop; Point2dInt v = inst.Placement.Translation; placement.Rotate( inst.Placement.Rotation ); placement.Translate( v.X, v.Y, MatrixOrder.Append ); } else { Debug.Assert( bimel is GeomData, "expected part with geometry" ); loop = ((GeomData) bimel).Loop; } loops[0] = loop.GetGraphicsPathLines(); placement.Multiply( transformBimToViewport, MatrixOrder.Append ); placement.Multiply( transformSheetBbToForm, MatrixOrder.Append ); DrawLoopsOnGraphics( graphics, loops, placement ); } } return bmp; }
/// <summary> /// Display room and furniture in a temporary form /// generated on the fly. /// </summary> /// <param name="roomLoops">Room boundary loops</param> /// <param name="geometryLoops">Family symbol geometry</param> /// <param name="familyInstances">Family instances</param> public static Bitmap DisplayRoom( JtLoops roomLoops, Dictionary<string, JtLoop> geometryLoops, List<JtPlacement2dInt> familyInstances ) { JtBoundingBox2dInt bbFrom = roomLoops.BoundingBox; // Adjust target rectangle height to the // displayee loop height. int width = _form_width; int height = (int) (width * bbFrom.AspectRatio + 0.5); //SizeF fsize = new SizeF( width, height ); //SizeF scaling = new SizeF( 1, 1 ); //PointF translation = new PointF( 0, 0 ); //GetTransform( fsize, bbFrom, // ref scaling, ref translation, true ); //Matrix transform1 = new Matrix( // new Rectangle(0,0,width,height), // bbFrom.GetParallelogramPoints()); //transform1.Invert(); // the bounding box fills the rectangle // perfectly and completely, inverted and // non-uniformly distorted: //Point2dInt pmin = bbFrom.Min; //Rectangle rect = new Rectangle( // pmin.X, pmin.Y, bbFrom.Width, bbFrom.Height ); //Point[] parallelogramPoints = new Point [] { // new Point( 0, 0 ), // upper left // new Point( width, 0 ), // upper right // new Point( 0, height ) // lower left //}; // the bounding box fills the rectangle // perfectly and completely, inverted and // non-uniformly distorted: // Specify transformation target rectangle // including a margin. int bottom = height - (_margin + _margin); Point[] parallelogramPoints = new Point[] { new Point( _margin, bottom ), // upper left new Point( width - _margin, bottom ), // upper right new Point( _margin, _margin ) // lower left }; // Transform from native loop coordinate system // to target display coordinates. Matrix transform = new Matrix( bbFrom.Rectangle, parallelogramPoints ); Bitmap bmp = new Bitmap( width, height ); Graphics graphics = Graphics.FromImage( bmp ); graphics.Clear( System.Drawing.Color.White ); DrawLoopsOnGraphics( graphics, roomLoops.GetGraphicsPathLines(), transform ); if( null != familyInstances ) { List<Point[]> loops = new List<Point[]>( 1 ); loops.Add( new Point[] { } ); foreach( JtPlacement2dInt i in familyInstances ) { Point2dInt v = i.Translation; Matrix placement = new Matrix(); placement.Rotate( i.Rotation ); placement.Translate( v.X, v.Y, MatrixOrder.Append ); placement.Multiply( transform, MatrixOrder.Append ); loops[0] = geometryLoops[i.SymbolId] .GetGraphicsPathLines(); DrawLoopsOnGraphics( graphics, loops, placement ); } } return bmp; }