/// <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; } }