Things have slowed down a bit in the discussion forum recently, like in many other parts of the world. I am very glad I have a garden to go out and sit in, and springtime and sunshine to enjoy. I hope you are doing well and remaining healthy also! Topics for today:
- Transforming symbol geometry to instance placement
- Importing and displaying satellite images
- Free time? Learn! Free code camp!
Transforming Symbol Geometry to Instance Placement
A fundamental question reappeared in a new form in
the Revit API discussion forum thread
on using PlanarFace
GetEdgesAsCurveLoops
and FilledRegion
Create
together,
easily resolved by applying the instance placement transformation to transform the symbol geometry from the family definition coordinate system to the real-world instance placement in the project space:
Question: I'm trying to create a plugin that will allow me to create filled regions just by selecting a face.
I use Selection.PickObject( ObjectType.Face ), convert that reference to a PlanarFace
type, and then use GetEdgesAsCurveLoops
to get the collection of CurveLoops
and pass that to FilledRegion
Create
to create my filled region.
However, it's resulting in unexpected behaviour. If I click on the face of a roof surface, it creates the filled region exactly over the face like I want it to. When I click the face of a family instance, it still creates the filled region – but instead of creating it over the face of the family instance, it creates the instance at the project origin of the document.
Here are images illustrating the two scenarios:
Highlighting the roof face and selecting it applies the FilledRegion like expected:
However, when I click this electrical equipment face...
It creates the filled region over at the project origin:
Here is the relevant code snippet:
Reference faceRef = sel.PickObject( ObjectType.Face ); GeometryObject geoObj = doc.GetElement( faceRef ) .GetGeometryObjectFromReference( faceRef ); PlanarFace moduleFace = geoObj as PlanarFace; IList<CurveLoop> faceEdges = moduleFace.GetEdgesAsCurveLoops(); FilledRegion fillRegion = FilledRegion.Create( doc, stringFillType.Id, currentView.Id, faceEdges );
Does anyone know how to get around this? Thanks!
Answer: The family instance reuses the geometry defined for the family symbol by applying a transform to it.
The symbol geometry is generally close to the origin.
Every instance needs a different transformation, depending on where the instance is placed in the model.
You need to apply the family instance transformation to the geometry to transform it from the family symbol definition coordinates to the project model coordinates where the instance has been placed:
The roof element is different, since it is modelled in place using a sketch.
Response: Awesome! This worked! Thank you much Jeremy!
For anyone interested, here is how I edited the code based on Jeremy's suggestion:
Reference faceRef = sel.PickObject( ObjectType.Face ); GeometryObject geoObj = doc.GetElement( faceRef ) .GetGeometryObjectFromReference( faceRef ); Instance moduleInstance = doc.GetElement( faceRef ) as Instance; Transform moduleTransform = moduleInstance.GetTotalTransform(); PlanarFace moduleFace = geoObj as PlanarFace; IList<CurveLoop> faceEdges = moduleFace.GetEdgesAsCurveLoops(); foreach( CurveLoop loop in faceEdges ) { loop.Transform( moduleTransform ); } FilledRegion fillRegion = FilledRegion.Create( doc, stringFillType.Id, currentView.Id, faceEdges );
Many thanks to Chris for raising the issue and confirming the solution!
Importing and Displaying Satellite Images
Revitalizer came up with a very nice suggestion for importing and displaying satellite images:
Question: I'm building an add-in for Revit and I would like to be able to import and display third-party satellite imagery in order to place buildings in their 'real' position. I would like to be able to do this in a 3D view, but I don't know how.
The user workflow for my add-in is this:
- A user opens the add-in and is prompted to input a location through a WPF window.
- Once a location is confirmed, a number of things are created/imported into the active project to make it look as close as possible to its actual real-life location. One of these things is the satellite image I'm seeking to import here.
Essentially, my question is exactly the same as Google maps image in 3D view, but instead doing that programmatically/automatically through an add-in. In that thread, a suggestion is made to create a decal with the desired image, but this does not seem to be supported through the API.
Another approach I found is
to use PostCommand
to
create and place decals, but these commands are apparently
only executed after exiting the API context and
only one at a time.
As my add-in aims to perform a whole bunch of functionalities in one go, this seems ill-suited for my use case.
It seems to be possible to chain a bunch of PostCommands, but this is a little 'hacky' and not recommended, especially for commercial use.
Am I overlooking some existing functionality? Is my use case just not supported in current Revit? I'm new to programming for Revit, so it's very possible I've missed something.
I'm running / programming for Revit 2019 on Windows 10.
Answer: What about creating a new material, setting its texture path, then making a TopoSurface
and assigning the material to it, cf. modifying material visual appearance?
I don't know how to adjust the UV
mapping for the TopoSurface
, but if it worked, you would see your satellite image in 3D.
Response: Thanks to all for the replies! It took some time to try out the proposed solution (accessing AppearanceElements is convoluted!), so that's why it took me this long to reply.
In the end, though I had to work around some weird quirks with the API, adding the image as a texture to a topography through a material works great.
Thanks again for the suggestion, I couldn't have made it work without it.
Many thanks to Harm for raising this and to Revitalizer for the good suggestion.
Free Time? Learn Things! Free Code Camp!
I have repeatedly pointed out the incredible programming training resources offered by Quincy Larson and freecodecamp.org.
In case you happen to have more time on your hands than usual these days, you might want to see it as an opportunity to learn to code from home with the Coronavirus quarantine developer skill handbook.
By the way, I very much enjoyed some of Quincy's recent quotes of the week:
- A computer once beat me at chess, but it was no match for me at kickboxing – Emo Philips
- The greatest obstacle to discovery is not ignorance, but the illusion of knowledge – Daniel Boorstin
- Good judgment comes from experience. Experience comes from bad judgment – unknown
- Optimism is an occupational hazard of programming. Testing is the treatment – Kent Beck
- Act in haste and repent at leisure. Code too soon and debug forever – Dr. Raymond Kennington