/// <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.Curve.GetEndPoint(0); jtloop.Add(new Point2dInt(p)); Debug.Assert(null == q || q.IsAlmostEqualTo(p), "expected last endpoint to equal current start point"); q = seg.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 } } Debug.Assert(q.IsAlmostEqualTo(p0), "expected last endpoint to equal loop start point"); jtloops.Add(jtloop); } return(jtloops); }
/// <summary> /// Return polygon loops representing the size /// and location of given sheet and all the /// viewports it contains, regardless of type. /// </summary> static JtLoops GetSheetViewportLoops( SheetModelCollections modelCollections, ViewSheet sheet) { Document doc = sheet.Document; List <Viewport> viewports = sheet .GetAllViewports() .Select <ElementId, Viewport>( id => doc.GetElement(id) as Viewport) .ToList <Viewport>(); int n = viewports.Count; modelCollections.ViewsInSheet[sheet.Id] = new List <ViewData>(n); JtLoops sheetViewportLoops = new JtLoops(n + 1); // sheet.get_BoundingBox( null ) returns (-100,-100),(100,100) BoundingBoxUV bb = sheet.Outline; // model coordinates (0,0), (2.76,1.95) JtBoundingBox2dInt ibb = new JtBoundingBox2dInt(); // millimeters (0,0),(840,...) ibb.ExpandToContain(new Point2dInt(bb.Min)); ibb.ExpandToContain(new Point2dInt(bb.Max)); JtLoop loop = new JtLoop(ibb.Corners); sheetViewportLoops.Add(loop); foreach (Viewport vp in viewports) { XYZ center = vp.GetBoxCenter(); // not used Outline outline = vp.GetBoxOutline(); ibb.Init(); ibb.ExpandToContain( new Point2dInt(outline.MinimumPoint)); ibb.ExpandToContain( new Point2dInt(outline.MaximumPoint)); loop = new JtLoop(ibb.Corners); sheetViewportLoops.Add(loop); ViewData d = new ViewData(); d.Id = vp.ViewId; d.ViewportBoundingBox = loop.BoundingBox; modelCollections.ViewsInSheet[sheet.Id].Add( d); } return(sheetViewportLoops); }
/// <summary> /// Unite two collections of boundary /// loops into one single one. /// </summary> public static JtLoops operator+(JtLoops a, JtLoops b) { int na = a.Count; int nb = b.Count; JtLoops sum = new JtLoops(na + nb); sum.AddRange(a); sum.AddRange(b); return(sum); }
/// <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> /// Upload the selected rooms and the furniture /// they contain to the cloud database. /// </summary> public static void UploadRoom( Document doc, Room room) { BoundingBoxXYZ bb = room.get_BoundingBox(null); if (null == bb) { Util.ErrorMsg(string.Format("Skipping room {0} " + "because it has no bounding box.", Util.ElementDescription(room))); return; } JtLoops roomLoops = GetRoomLoops(room); ListLoops(room, roomLoops); List <Element> furniture = GetFurniture(room); // Map symbol UniqueId to symbol loop Dictionary <string, JtLoop> furnitureLoops = new Dictionary <string, JtLoop>(); // List of instances referring to symbols List <JtPlacement2dInt> furnitureInstances = new List <JtPlacement2dInt>( furniture.Count); int nFailures; foreach (FamilyInstance f in furniture) { FamilySymbol s = f.Symbol; string uid = s.UniqueId; if (!furnitureLoops.ContainsKey(uid)) { nFailures = 0; JtLoops loops = GetPlanViewBoundaryLoops( f, ref nFailures); if (0 < nFailures) { Debug.Print("{0}: {1}", Util.ElementDescription(f), Util.PluralString(nFailures, "extrusion analyser failure")); } ListLoops(f, loops); if (0 < loops.Count) { // Assume first loop is outer one furnitureLoops.Add(uid, loops[0]); } } furnitureInstances.Add( new JtPlacement2dInt(f)); } IWin32Window revit_window = new JtWindowHandle( ComponentManager.ApplicationWindow); string caption = doc.Title + " : " + doc.GetElement(room.LevelId).Name + " : " + room.Name; Bitmap bmp = GeoSnoop.DisplayRoom(roomLoops, furnitureLoops, furnitureInstances); GeoSnoop.DisplayImageInForm(revit_window, caption, false, bmp); DbUpload.DbUploadRoom(room, furniture, roomLoops, furnitureLoops); }
/// <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 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 = new Plane(XYZ.BasisX, XYZ.BasisY, XYZ.Zero); 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); }
public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements) { UIApplication uiapp = commandData.Application; UIDocument uidoc = uiapp.ActiveUIDocument; Application app = uiapp.Application; Document doc = uidoc.Document; //IWin32Window revit_window // = new JtWindowHandle( // ComponentManager.ApplicationWindow ); // pre-2020 IWin32Window revit_window = new JtWindowHandle(uiapp.MainWindowHandle); // 2020 if (null == doc) { Util.ErrorMsg("Please run this command in a valid" + " Revit project document."); return(Result.Failed); } // Interactive sheet selection. FrmSelectSheets form = new FrmSelectSheets(doc); if (DialogResult.OK == form.ShowDialog( revit_window)) { List <ViewSheet> sheets = form.GetSelectedSheets(); int n = sheets.Count; string caption = Util.PluralString( n, "Sheet") + " Selected"; string msg = string.Join(", ", sheets.Select <Element, string>( e => Util.SheetDescription(e))) + "."; // Determine all floor plan views displayed // in the selected sheets. Dictionary <View, int> views = new Dictionary <View, int>( new ElementEqualityComparer()); int nFloorPlans = 0; foreach (ViewSheet sheet in sheets) { foreach (View v in sheet.GetAllPlacedViews() .Select <ElementId, View>(id => doc.GetElement(id) as View)) { if (!views.ContainsKey(v)) { if (IsFloorPlan(v)) { ++nFloorPlans; } views.Add(v, 0); } ++views[v]; } } msg += (1 == n) ? "\nIt contains" : "\nThey contain"; n = views.Count; msg += string.Format( " {0} including {1}: ", Util.PluralString(n, "view"), Util.PluralString(nFloorPlans, "floor plan")); msg += string.Join(", ", views.Keys.Select <Element, string>( e => e.Name)) + "."; Util.InfoMsg2(caption, msg, false); // Determine all categories occurring // in the views displayed by the sheets. List <Category> categories = new List <Category>( new CategoryCollector(views.Keys).Keys); // Sort categories alphabetically by name // to display them in selection form. categories.Sort( delegate(Category c1, Category c2) { return(string.Compare(c1.Name, c2.Name)); }); // Interactive category selection. FrmSelectCategories form2 = new FrmSelectCategories(categories); if (DialogResult.OK == form2.ShowDialog( revit_window)) { categories = form2.GetSelectedCategories(); n = categories.Count; caption = Util.PluralString(n, "Category") + " Selected"; msg = string.Join(", ", categories.Select <Category, string>( e => e.Name)) + "."; Util.InfoMsg2(caption, msg, false); // Convert category list to a dictionary for // more effective repeated lookup. // //Dictionary<ElementId, Category> catLookup = // categories.ToDictionary<Category, ElementId>( // c => c.Id ); // // No, much better: set up a reusable element // filter for the categories of interest: ElementFilter categoryFilter = new LogicalOrFilter(categories .Select <Category, ElementCategoryFilter>( c => new ElementCategoryFilter(c.Id)) .ToList <ElementFilter>()); // Instantiate a container for all // cloud data repository content. SheetModelCollections modelCollections = new SheetModelCollections( DbUpload.GetProjectInfo(doc).Id); foreach (ViewSheet sheet in sheets) { // Define preview form caption. caption = "Sheet and Viewport Loops - " + Util.SheetDescription(sheet); // This is currently not used for anything. ListSheetAndViewTransforms(sheet); // Determine the polygon loops representing // the size and location of given sheet and // the viewports it contains. JtLoops sheetViewportLoops = GetSheetViewportLoops( modelCollections, sheet); // Determine graphics for family instances, // their symbols and other BIM parts. GetBimGraphics(modelCollections, sheet, categoryFilter); // Display sheet and viewports with the // geometry retrieved in a temporary GeoSnoop // form generated on the fly for debugging // purposes. Bitmap bmp = GeoSnoop.DisplaySheet( sheet.Id, sheetViewportLoops, modelCollections); GeoSnoop.DisplayImageInForm( revit_window, caption, false, bmp); // Upload data to the cloud database. DbUpload.DbUploadSheet(sheet, sheetViewportLoops, modelCollections); } DbUpdater.SetLastSequence(); } } return(Result.Succeeded); }
/// <summary> /// Determine the visible elements belonging to the /// specified categories in the views displayed by /// the given sheet and return their graphics and /// instance placements. /// Ignore all but the first geometry loop retrieved. /// </summary> /// <param name="modelCollections">Data container</param> /// <param name="sheet">The view sheet</param> /// <param name="categoryFilter">The desired categories</param> static void GetBimGraphics( SheetModelCollections modelCollections, ViewSheet sheet, ElementFilter categoryFilter) { bool list_ignored_elements = false; Document doc = sheet.Document; Autodesk.Revit.Creation.Application creapp = doc.Application.Create; Options opt = new Options(); // There is no need and no possibility to set // the detail level when retrieving view geometry. // An attempt to specify the detail level will // cause writing the opt.View property to throw // "DetailLevel is already set. When DetailLevel // is set view-specific geometry can't be // extracted." // //opt.DetailLevel = ViewDetailLevel.Coarse; Debug.Print(sheet.Name); foreach (ViewPlan v in sheet.GetAllPlacedViews() .Select <ElementId, View>(id => doc.GetElement(id) as View) .OfType <ViewPlan>() .Where <ViewPlan>(v => IsFloorPlan(v))) { Debug.Print(" " + v.Name); modelCollections.BimelsInViews.Add( v.Id, new List <ObjData>()); opt.View = v; JtBoundingBox2dInt bimelBb = new JtBoundingBox2dInt(); FilteredElementCollector els = new FilteredElementCollector(doc, v.Id) .WherePasses(categoryFilter); foreach (Element e in els) { GeometryElement geo = e.get_Geometry(opt); FamilyInstance f = e as FamilyInstance; if (null != f) { LocationPoint lp = e.Location as LocationPoint; // Simply ignore family instances that // have no location point or no location at // all, e.g. panel. // No, we should not ignore them, but // treat tham as non-transformable parts. if (null == lp) { if (list_ignored_elements) { Debug.Print(string.Format( " ... {0} has no location", e.Name)); } f = null; geo = geo.GetTransformed( Transform.Identity); } else { FamilySymbol s = f.Symbol; if (modelCollections.Symbols.ContainsKey(s.Id)) { if (list_ignored_elements) { Debug.Print(" ... symbol already handled " + e.Name + " --> " + s.Name); } // Symbol already defined, just add instance JtPlacement2dInt placement = new JtPlacement2dInt(f); // Expand bounding box around all BIM // elements, ignoring the size of the // actual geometry, assuming is is small // in comparison and the insertion point // lies within it. bimelBb.ExpandToContain( placement.Translation); InstanceData d = new InstanceData(); d.Id = f.Id; d.Symbol = f.Symbol.Id; d.Placement = placement; modelCollections.BimelsInViews[v.Id] .Add(d); continue; } // Retrieve family instance geometry // transformed back to symbol definition // coordinate space by inverting the // family instance placement transformation Transform t = Transform.CreateTranslation( -lp.Point); Transform r = Transform.CreateRotationAtPoint( XYZ.BasisZ, -lp.Rotation, lp.Point); geo = geo.GetTransformed(t * r); } } int nEmptySolids = 0; int nNonEmptySolids = 0; int nCurves = 0; int nOther = 0; foreach (GeometryObject obj in geo) { // This was true before calling GetTransformed. //Debug.Assert( obj is Solid || obj is GeometryInstance, "expected only solids and instances" ); // This was true before calling GetTransformed. //Debug.Assert( ( obj is GeometryInstance ) == ( e is FamilyInstance ), "expected all family instances to have geometry instance" ); Debug.Assert(obj is Solid || obj is Line || obj is Arc, "expected only solids, lines and arcs after calling GetTransformed on instances"); // Todo: handle arcs, e.g. tessellate Debug.Assert(Visibility.Visible == obj.Visibility, "expected only visible geometry objects"); Debug.Assert(obj.IsElementGeometry, "expected only element geometry"); //bool isElementGeometry = obj.IsElementGeometry; Solid solid = obj as Solid; if (null != solid) { if (0 < solid.Edges.Size) { ++nNonEmptySolids; } else { ++nEmptySolids; } } else if (obj is Curve) { ++nCurves; } else { ++nOther; } } Debug.Print(" {0}: {1} non-emtpy solids, " + "{2} empty, {3} curves, {4} other", e.Name, nNonEmptySolids, nEmptySolids, nCurves, nOther); JtLoops loops = null; if (1 == nNonEmptySolids && 0 == nEmptySolids + nCurves + nOther) { int nFailures = 0; loops = CmdUploadRooms .GetPlanViewBoundaryLoopsGeo( creapp, geo, ref nFailures); } else { double z = double.MinValue; bool first = true; foreach (GeometryObject obj in geo) { // Do we need the graphics style? // It might give us horrible things like // colours etc. ElementId id = obj.GraphicsStyleId; //Debug.Print( " " + obj.GetType().Name ); Solid solid = obj as Solid; if (null == solid) { #region Debug code to ensure horizontal co-planar curves #if DEBUG Debug.Assert(obj is Line || obj is Arc, "expected only lines and arcs here"); Curve c = obj as Curve; if (first) { z = c.GetEndPoint(0).Z; Debug.Assert(Util.IsEqual(z, c.GetEndPoint(1).Z), "expected a plan view with all Z values equal"); first = false; } else { Debug.Assert(Util.IsEqual(z, c.GetEndPoint(0).Z), "expected a plan view with all Z values equal"); Debug.Assert(Util.IsEqual(z, c.GetEndPoint(1).Z), "expected a plan view with all Z values equal"); } Debug.Print(" {0} {1}", obj.GetType().Name, Util.CurveEndpointString(c)); #endif // DEBUG #endregion // Debug code to ensure horizontal co-planar curves } else if (1 == solid.Faces.Size) { Debug.Print( " solid with 1 face"); foreach (Face face in solid.Faces) { #region Debug code to print out face edges #if DEBUG foreach (EdgeArray loop in face.EdgeLoops) { foreach (Edge edge in loop) { // This returns the curves already // correctly oriented: Curve c = edge .AsCurveFollowingFace(face); Debug.Print(" {0}: {1} {2}", edge.GetType().Name, c.GetType().Name, Util.CurveEndpointString(c)); } } #endif // DEBUG #endregion // Debug code to print out face edges if (null == loops) { loops = new JtLoops(1); } loops.Add(CmdUploadRooms.GetLoop( creapp, face)); } } else { #region Debug code for exceptional cases #if DEBUG_2 Debug.Assert(1 >= solid.Faces.Size, "expected at most one visible face in plan view for my simple solids"); int n = solid.Edges.Size; if (0 < n) { Debug.Print( " solid with {0} edges", n); Face[] face2 = new Face[] { null, null }; Face face = null; foreach (Edge edge in solid.Edges) { if (null == face2[0]) { face2[0] = edge.GetFace(0); face2[1] = edge.GetFace(1); } else if (null == face) { if (face2.Contains <Face>(edge.GetFace(0))) { face = edge.GetFace(0); } else if (face2.Contains <Face>(edge.GetFace(1))) { face = edge.GetFace(1); } else { Debug.Assert(false, "expected all edges to belong to one face"); } } else { Debug.Assert(face == edge.GetFace(0) || face == edge.GetFace(1), "expected all edges to belong to one face"); } Curve c = edge.AsCurve(); // This returns the curves already // correctly oriented: //Curve curve = e.AsCurveFollowingFace( // face ); Debug.Print(" {0}: {1} {2}", edge.GetType().Name, c.GetType().Name, Util.CurveEndpointString(c)); } } #endif // DEBUG #endregion // Debug code for exceptional cases } } } // Save the part or instance and // the geometry retrieved for it. // This is where we drop all geometry but // the first loop. if (null != loops) { GeomData gd = new GeomData(); gd.Loop = loops[0]; if (null == f) { // Add part with absolute geometry gd.Id = e.Id; modelCollections.BimelsInViews[v.Id].Add( gd); // Expand bounding box around all BIM // elements. bimelBb.ExpandToContain( gd.Loop.BoundingBox); } else { // Define symbol and add instance JtPlacement2dInt placement = new JtPlacement2dInt(f); InstanceData id = new InstanceData(); id.Id = f.Id; id.Symbol = f.Symbol.Id; id.Placement = placement; modelCollections.BimelsInViews[v.Id].Add( id); gd.Id = f.Symbol.Id; modelCollections.Symbols.Add( f.Symbol.Id, gd); // Expand bounding box around all BIM // elements. JtBoundingBox2dInt bb = gd.Loop.BoundingBox; Point2dInt vtrans = placement.Translation; bimelBb.ExpandToContain(bb.Min + vtrans); bimelBb.ExpandToContain(bb.Max + vtrans); } } } // Set up BIM bounding box for this view modelCollections.ViewsInSheet[sheet.Id].Find( v2 => v2.Id.IntegerValue.Equals( v.Id.IntegerValue)).BimBoundingBox = bimelBb; } }
/// <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); }
/// <summary> /// Upload model, level, room and furniture data /// to an IrisCouch hosted CouchDB data repository. /// </summary> static public void DbUploadRoom( Room room, List <Element> furniture, JtLoops roomLoops, Dictionary <string, JtLoop> furnitureLoops) { CouchDatabase db = new RoomEditorDb().Db; Document doc = room.Document; Element projectInfo = GetProjectInfo(doc); DbModel dbModel = GetDbModel(db, projectInfo); Element level = doc.GetElement(room.LevelId); string uid = level.UniqueId; DbLevel dbLevel; if (db.DocumentExists(uid)) { dbLevel = db.GetDocument <DbLevel>(uid); Debug.Assert( dbLevel.Id.Equals(level.UniqueId), "expected equal ids"); dbLevel.Description = Util.ElementDescription( level); dbLevel.Name = level.Name; dbLevel.ModelId = projectInfo.UniqueId; dbLevel = db.UpdateDocument <DbLevel>( dbLevel); } else { dbLevel = new DbLevel(uid); dbLevel.Description = Util.ElementDescription( level); dbLevel.Name = level.Name; dbLevel.ModelId = projectInfo.UniqueId; dbLevel = db.CreateDocument <DbLevel>( dbLevel); } uid = room.UniqueId; DbRoom dbRoom; if (db.DocumentExists(uid)) { dbRoom = db.GetDocument <DbRoom>(uid); Debug.Assert( dbRoom.Id.Equals(room.UniqueId), "expected equal ids"); dbRoom.Description = Util.ElementDescription( room); dbRoom.Name = room.Name; dbRoom.LevelId = level.UniqueId; dbRoom.Loops = roomLoops.SvgPath; dbRoom.ViewBox = roomLoops.BoundingBox.SvgViewBox; dbRoom = db.UpdateDocument <DbRoom>(dbRoom); } else { dbRoom = new DbRoom(uid); dbRoom.Description = Util.ElementDescription( room); dbRoom.Name = room.Name; dbRoom.LevelId = level.UniqueId; dbRoom.Loops = roomLoops.SvgPath; dbRoom.ViewBox = roomLoops.BoundingBox.SvgViewBox; dbRoom = db.CreateDocument <DbRoom>(dbRoom); } foreach (KeyValuePair <string, JtLoop> p in furnitureLoops) { uid = p.Key; Element e = doc.GetElement(uid); if (db.DocumentExists(uid)) { DbSymbol symbol = db.GetDocument <DbSymbol>( uid); symbol.Description = Util.ElementDescription(e); symbol.Name = e.Name; symbol.Loop = p.Value.SvgPath; symbol = db.UpdateDocument <DbSymbol>(symbol); } else { DbSymbol symbol = new DbSymbol(uid); symbol.Description = Util.ElementDescription(e); symbol.Name = e.Name; symbol.Loop = p.Value.SvgPath; symbol = db.CreateDocument <DbSymbol>(symbol); } } foreach (FamilyInstance f in furniture) { uid = f.UniqueId; if (db.DocumentExists(uid)) { DbFurniture dbf = db.GetDocument <DbFurniture>( uid); dbf.Description = Util.ElementDescription(f); dbf.Name = f.Name; dbf.RoomId = room.UniqueId; dbf.Properties = Util.GetElementProperties(f); dbf.SymbolId = f.Symbol.UniqueId; dbf.Transform = new JtPlacement2dInt(f) .SvgTransform; dbf = db.UpdateDocument <DbFurniture>(dbf); } else { DbFurniture dbf = new DbFurniture(uid); dbf.Description = Util.ElementDescription(f); dbf.Name = f.Name; dbf.RoomId = room.UniqueId; dbf.Properties = Util.GetElementProperties(f); dbf.SymbolId = f.Symbol.UniqueId; dbf.Transform = new JtPlacement2dInt(f) .SvgTransform; dbf = db.CreateDocument <DbFurniture>(dbf); } } }
/// <summary> /// Upload model, sheet, views it contains and /// their BIM elements to a CouchDB data repository. /// </summary> static public void DbUploadSheet( ViewSheet sheet, JtLoops sheetViewportLoops, SheetModelCollections modelCollections) { bool pre_existing = false; RoomEditorDb rdb = new RoomEditorDb(); CouchDatabase db = rdb.Db; // Sheet Document doc = sheet.Document; Element e = GetProjectInfo(doc); DbModel dbModel = GetDbModel(db, e); DbSheet dbSheet = rdb.GetOrCreate <DbSheet>( ref pre_existing, sheet.UniqueId); dbSheet.Description = Util.SheetDescription(sheet); dbSheet.Name = sheet.Name; dbSheet.ModelId = e.UniqueId; dbSheet.Width = sheetViewportLoops[0].BoundingBox.Width; dbSheet.Height = sheetViewportLoops[0].BoundingBox.Height; dbSheet = pre_existing ? db.UpdateDocument <DbSheet>(dbSheet) : db.CreateDocument <DbSheet>(dbSheet); // Symbols Dictionary <ElementId, GeomData> geometryLookup = modelCollections.Symbols; foreach (KeyValuePair <ElementId, GeomData> p in geometryLookup) { ElementId id = p.Key; e = doc.GetElement(id); DbSymbol symbol = rdb.GetOrCreate <DbSymbol>( ref pre_existing, e.UniqueId); symbol.Description = Util.ElementDescription(e); symbol.Name = e.Name; symbol.Loop = p.Value.Loop.SvgPath; symbol = pre_existing ? db.UpdateDocument <DbSymbol>(symbol) : db.CreateDocument <DbSymbol>(symbol); } // Views and BIM elements List <ViewData> views = modelCollections .ViewsInSheet[sheet.Id]; View view; DbView dbView; DbBimel dbBimel; DbInstance dbInstance = null; DbPart dbPart = null; JtBoundingBox2dInt bbFrom; JtBoundingBox2dInt bbTo; foreach (ViewData viewData in views) { ElementId vid = viewData.Id; if (!modelCollections.BimelsInViews .ContainsKey(vid)) { // This is not a floor plan view, so // we have nothing to display in it. continue; } view = doc.GetElement(vid) as View; dbView = rdb.GetOrCreate <DbView>( ref pre_existing, view.UniqueId); dbView.Description = Util.ElementDescription(view); dbView.Name = view.Name; dbView.SheetId = dbSheet.Id; bbFrom = viewData.BimBoundingBox; bbTo = viewData.ViewportBoundingBox; dbView.X = bbTo.Min.X; dbView.Y = bbTo.Min.Y; dbView.Width = bbTo.Width; dbView.Height = bbTo.Height; dbView.BimX = bbFrom.Min.X; dbView.BimY = bbFrom.Min.Y; dbView.BimWidth = bbFrom.Width; dbView.BimHeight = bbFrom.Height; dbView = pre_existing ? db.UpdateDocument <DbView>(dbView) : db.CreateDocument <DbView>(dbView); // Retrieve the list of BIM elements // displayed in this view. List <ObjData> bimels = modelCollections .BimelsInViews[vid]; foreach (ObjData bimel in bimels) { e = doc.GetElement(bimel.Id); InstanceData inst = bimel as InstanceData; if (null != inst) { dbInstance = rdb.GetOrCreate <DbInstance>( ref pre_existing, e.UniqueId); dbInstance.SymbolId = doc.GetElement( inst.Symbol).UniqueId; dbInstance.Transform = inst.Placement .SvgTransform; dbBimel = dbInstance; } else { Debug.Assert(bimel is GeomData, "expected part with geometry"); dbPart = rdb.GetOrCreate <DbPart>( ref pre_existing, e.UniqueId); dbPart.Loop = ((GeomData)bimel).Loop .SvgPath; dbBimel = dbPart; } dbBimel.Description = Util.ElementDescription(e); dbBimel.Name = e.Name; JtUidSet uids = new JtUidSet(dbBimel.ViewIds); uids.Add(view.UniqueId); dbBimel.ViewIds = uids.Uids; dbBimel.Properties = Util.GetElementProperties(e); if (null != inst) { dbInstance = pre_existing ? db.UpdateDocument <DbInstance>(dbInstance) : db.CreateDocument <DbInstance>(dbInstance); } else { dbPart = pre_existing ? db.UpdateDocument <DbPart>(dbPart) : db.CreateDocument <DbPart>(dbPart); } } } }