Today I look at interactively picking sheets to export, instead of views.
First, however, I must mention the list of April Fool's posts compiled by Stephen Preston, including my own visionary idea to recycle waste as insulation and curtain wall material around the building producing it. Personally, I like Stephen's own post on the Autodesk Toilet Design Suite product much better :-)
Back to picking sheets...
This is the third instalment of implementing the new RoomEditorApp features required for my generic 2D simplified BIM editor Tech Summit presentation, including a change of plan and some additional complexity, as we shall see below.
Inspired by input from interested developers, I opted to base the simplified BIM cloud database and mobile device visualisation on sheets instead of floor plans.
Steps 1, 2 and 3 in the previous tentative workflow refer to plan views, and actually floor plans, to be specific.
I switch those to select sheets instead.
Additional complexity is thus introduced, since each sheet can contain and display multiple views, and each view will have its own location and scale within the sheet.
It will be interesting to see how to accurately represent the multiple nested transformations required to get from the Revit coordinate system to the views, sheets, cloud database, SVG editor final destination and all the way back again.
Tentative Workflow
Meanwhile, here is the updated plan:
- Launch the RoomEditorApp export sheets command.
- Display a list of view sheets in a popup window.
- Select sheets to be exported and click OK.
- Determine all the floor plan views displayed in the selected sheets.
- Determine all the categories used in those views.
- Display a list of those categories in a popup window.
- Select categories to be exported and click OK.
- Determine location and scaling of the floor plan views within the selected sheets.
- Store relevant graphical and non-graphical information in a cloud database.
- Navigate and display simplified model on mobile device.
- Edit graphical and non-graphical information.
- Update Revit BIM either interactively or real-time.
Here and now, I'll discuss the new steps 1-4.
The steps 5, 6 and 7 correspond to the steps 4 and 5 in the previous tentative workflow, were addressed selecting visible categories from a set of views, and can easily be adapted to the new workflow.
Here are the new methods and classes required:
- ElementEqualityComparer – an equality comparer ensuring that elements with the same element id compare equal.
- FrmSelectSheets – interactive sheet view selection form.
- IsSameOrSubclassOf – check whether class is subclass of OR equal to the class itself.
- CmdUploadSheets – drive the sheet and category selection and list the results.
ElementEqualityComparer
After selecting the sheets, I want to determine which floor plan views they contain.
I stuff all the views as keys into a dictionary.
To ensure that the views compare properly with each other, I need to supply a comparison operator that compares their element ids, not the objects themselves.
This is obviously very similar to the CategoryEqualityComparer introduced in the last instalment. The similarity lies in the fact that categories also have an Id property, but are not derived from the Revit Element class, so that property represents a category id, not an element id.
FrmSelectSheets
I edited the previous FrmSelectViews to select view sheets instead of floor plan views, e.g., replacing ViewPlan by ViewSheet, ViewType.FloorPlan by ViewType.DrawingSheet, etc.
The selection result is now obviously a list of sheets, from which the floor plan views need to be extracted.
IsSameOrSubclassOf
I initially tried to use the .NET IsSubclassOf method to check for views of type ViewPlan in the sheets, only to notice once again that this method only returns true for true non-equal subclasses.
I therefore defined a new utility method IsSameOrSubclassOf to do what I really want, as suggested by Lasse V. Karlsen in stackoverflow.com:
/// <summary> /// Return true if the type b is either a /// subclass of OR equal to the base class itself. /// IsSubclassOf returns false if the two types /// are the same. It only returns true for true /// non-equal subclasses. /// </summary> public static bool IsSameOrSubclassOf( Type a, Type b ) { return a.IsSubclassOf( b ) || a == b; }
CmdUploadSheets
The previous CmdUploadViews is also renamed, and this is obviously the place where the interesting changes need to be applied.
This is still the skeleton of the final command, currently just driving the sheet and category selection and listing the results.
The most interesting new part is the determination of the views displayed within the sheets.
For details on the rest, please refer to the initial CmdUploadViews description.
Here is the entire implementation of the external command Execute method:
IWin32Window revit_window = new JtWindowHandle( ComponentManager.ApplicationWindow ); UIApplication uiapp = commandData.Application; UIDocument uidoc = uiapp.ActiveUIDocument; Application app = uiapp.Application; Document doc = uidoc.Document; 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 = string.Format( "{0} Sheet{1} Selected", n, Util.PluralSuffix( n ) ); string msg = string.Join( ", ", sheets.Select<Element, string>( e => e.Name ) ) + "."; // Determine all 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.Views ) { if( !views.ContainsKey( v ) ) { if( Util.IsSameOrSubclassOf( v.GetType(), typeof( ViewPlan ) ) && v.CanBePrinted && ViewType.FloorPlan == v.ViewType ) { ++nFloorPlans; } views.Add( v, 0 ); } ++views[v]; } } msg += ( 1 == n ) ? "\nIt contains" : "\nThey contain"; n = views.Count; msg += string.Format( " {0} view{1} including {2} floor plan{3}: ", n, Util.PluralSuffix( n ), nFloorPlans, Util.PluralSuffix( nFloorPlans ) ); msg += string.Join( ", ", views.Keys.Select<Element, string>( e => e.Name ) ) + "."; Util.InfoMsg2( caption, msg ); // 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 = string.Format( "{0} Categor{1} Selected", n, Util.PluralSuffixY( n ) ); msg = string.Join( ", ", categories.Select<Category, string>( e => e.Name ) ) + "."; Util.InfoMsg2( caption, msg ); } } return Result.Succeeded;
I ran the command in a super simple model with the following views and sheets:
The three sheets are offered for selection in the check box list:
Simply hitting OK selects all three. They are listed together with the views and count of floor plans they contain:
3 Sheets Selected: Sheet view of Level 0 and 1, 3D, Level 0 Duplicate. They contain 4 views including 3 floor plans: Level 0, Level 1, {3D}, Dependent on Level 0.
The three sheets are offered for selection in the check box list:
Simply hitting OK selects all five, and they are listed:
Selected 5 categories from 4 views displaying 1692 elements, 821 with HasMaterialQuantities=true 5 Categories Selected: Curtain Panels, Doors, Furniture, Structural Columns, Walls.
Download
For the complete source code, Visual Studio solution and add-in manifest, please refer to the RoomEditorApp GitHub repository.
The version discussed above is stored as release 2014.0.2.3.