Not all location relevant properties of viewports are currently accessible through the Revit API, which makes it slightly challenging to precisely define their position.
Let us look at a clever trick to align viewports exactly that works around that limitation, sheds some light on their location calculation and the modification of it that was made between Revit 2013 and 2014.
After that, I would like to briefly present a non-Revit-API BIM topic, a single-page MSc thesis overview on completely conceptual design automation, and mention the new Graitec technology acqusition.
Question: I want to programmatically place views on a sheet and align them exactly with each other. In Revit 2013, my code works perfectly. I calculate the position of the view on the sheet from its crop box.
When I use the same calculation in Revit 2014, the insertion point is no longer correct.
The view is placed centred according to the viewport instead of the view. The viewport location differs from the view one, because it depends on shown annotations and grid lines which extend the viewport, so my views are no longer aligned due to the different placing.
Here is the code to reproduce and demonstrate this behaviour using hard-coded element ids, so it will obviously only work in the specific test model:
public class ViewSheetTest1 : IExternalCommand { public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements ) { var doc = commandData.Application .ActiveUIDocument.Document; test2( doc ); return Result.Succeeded; } private static void test2( Document doc ) { FamilySymbol typ = doc.GetElement( new ElementId( 143899 ) ) as FamilySymbol; ViewPlan go1 = doc.GetElement( new ElementId( 205387 ) ) as ViewPlan; ViewPlan vg1 = doc.GetElement( new ElementId( 205424 ) ) as ViewPlan; var vSheet = ViewSheet.Create( doc, typ.Id ); vSheet.Name = "test2"; ElementId vid = vSheet.Id; var p = XYZ.Zero; var v12 = Viewport.Create( doc, vid, go1.Id, p ); var v22 = Viewport.Create( doc, vid, vg1.Id, p ); } }
Here is the correct result produced in Revit 2013:
In Revit 2014, the viewport position is offset, as you can see from the highlighted hatch overlaps and gaps:
How can I solve this, please?
Is it possible to retrieve the coordinates of the view within the viewport, or is there some other possible solution for this?
Answer: The Viewport.Create method was fixed in 2014 to behave the way that was always documented – placement point being at the centre of the viewport. This was fixed because it was very difficult to lay out multiple views on a sheet where the viewport locations were determined by world coordinates and the size of the related view.
Unfortunately, you have encountered a drawback to this change – because many viewports will have a different centre relative to model coordinates, aligning them related to model coordinates is more difficult.
There is currently a gap in the API related to converting between model and sheet coordinates.
In the meantime, here is a workaround:
Here is a function that does this for the two views in the original code:
void test4( Document doc ) { FamilySymbol typ = doc.GetElement( new ElementId( 143899 ) ) as FamilySymbol; ViewPlan go1 = doc.GetElement( new ElementId( 205387 ) ) as ViewPlan; ViewPlan vg1 = doc.GetElement( new ElementId( 205424 ) ) as ViewPlan; // Save current crop box values BoundingBoxXYZ savedBox1 = null; if( go1.CropBoxActive ) savedBox1 = go1.CropBox; BoundingBoxXYZ savedBox2 = null; if( vg1.CropBoxActive ) savedBox2 = vg1.CropBox; if( savedBox1 != null ) { // Set crop box for 2nd view = 1st vg1.CropBox = savedBox1; vg1.CropBoxActive = true; } else { // Set both views to semi-random // but large crop box BoundingBoxXYZ newBox = new BoundingBoxXYZ(); newBox.set_MinEnabled( 0, true ); newBox.set_MinEnabled( 1, true ); newBox.set_MinEnabled( 2, true ); newBox.Min = new XYZ( -2000, -2000, 0 ); newBox.set_MaxEnabled( 0, true ); newBox.set_MaxEnabled( 1, true ); newBox.set_MaxEnabled( 2, true ); newBox.Max = new XYZ( 2000, 2000, 0 ); vg1.CropBox = newBox; go1.CropBox = newBox; doc.Regenerate(); vg1.CropBoxActive = true; go1.CropBoxActive = true; } doc.Regenerate(); // Create sheets and viewports var vSheet = ViewSheet.Create( doc, typ.Id ); vSheet.Name = "test3"; ElementId vid = vSheet.Id; var p = XYZ.Zero; var v12 = Viewport.Create( doc, vid, go1.Id, p ); var v22 = Viewport.Create( doc, vid, vg1.Id, p ); doc.Regenerate(); // Align lower left - works // because crop boxes are same Outline outline1 = v12.GetBoxOutline(); Outline outline2 = v22.GetBoxOutline(); XYZ min1 = outline1.MinimumPoint; XYZ min2 = outline2.MinimumPoint; XYZ diffToMove = min1 - min2; ElementTransformUtils.MoveElement( doc, v22.Id, diffToMove ); doc.Regenerate(); // Restore view crop boxes if( savedBox1 == null ) { go1.CropBoxActive = false; } else { go1.CropBox = savedBox1; go1.CropBoxActive = true; go1.CropBoxVisible = false; } if( savedBox2 == null ) { vg1.CropBoxActive = false; } else { vg1.CropBox = savedBox2; vg1.CropBoxActive = true; vg1.CropBoxVisible = false; } }
Response: The suggested workaround works like a charm. :-)
I still have a question regarding your statement "... because many viewports will have a different centre relative to model coordinates": how does Revit internally calculate the centre of a viewport? Is that controlled by the CropBox property value?
Answer: The viewport size is governed by both the annotation crop box and the model crop box at different times, depending on which is larger. Currently, the API only provides access to the model crop box.
Therefore, making the model crop box large enough (where 'enough' is not a very precise term) seems to help. When it is too small, the annotations in one of the views causes the mismatch you observed.
Response: I am happy to inform you that I have tested your workaround in a couple of scenarios in our main application and it continues to work
Ritchie Jackson of the Adaptive Architecture and Computation programme at UCL, the University College London, provided several valuable contributions to this blog in the past and now completed his MSc. thesis on Conceptual Design Automation including this single-page complete overview:
It was implemented using the Unity game engine, C# and a neural network to facilitate the automation of the conceptual design process, so with no Revit API involvement.
Here is a short Q&A between me and Ritchie to gain better understanding:
Questions: After reading the text, I do not understand where the neural network enters into the picture, or what has been optimised or learned by the machine in the process. To me, it looks like random input and random output.
Answers:
Questions:
Answers:
In addition: –
Many thanks to Ritchie for sharing this, and especially for the nice wording of the final conclusion :-)
To close, a piece of breaking news: Autodesk has signed an agreement to acquire Graitec technology, including the Advance Steel and Advance Concrete product lines, and associated employees, thus expanding its BIM portfolio for structural fabrication and detailing.
Addendum: Here are new links to the new Advance Steel and Advance Concrete product centers: