Happy New Year!
I had a great break over Christmas and New Year and hope you did as well.
I'll begin the new year by discussing a bunch of topics addressing various aspects of DirectShape
elements:
- Updated DirectShapeFromFace
- Improve loft form creation speed using
DirectShape
- Create
DirectShape
instead of conceptual mass - Create surface from face
- Create
DirectShape
from solid - Control
DirectShape
colour and material - Improve family performance suppressing
DirectShape
generation - Create
DirectShape
from room
Updated DirectShapeFromFace
One useful little DirectShape
utility is
the DirectShapeFromFace add-in.
It creates a DirectShape
element from a selected element face.
We initially discussed it in 2015, in the article on DirectShape From Face and Sketch Plane Reuse.
It demonstrates several approaches, including
a simpler access to the picked face using GetTotalTransform
.
Now I migrated it from Revit 2016 to Revit 2018, replacing a couple of obsolete API calls.
Improve Loft Form Creation Speed using DirectShape
One of the most important features of direct shapes is the ability to generate geometry directly in place in the model without needing to create a separate family definition and instantiate that, resulting in a vast speed improvement.
For example, a developer was creating a new mass family and loading it in the project, taking so much time to create a loft form that it ended up unusable.
The form was generated in a standard way to create families, using Document.FamilyCreate.NewLoftForm
with a ReferenceArray
of CurveByPoint
or ModelCurve
elements.
It takes about 4 minutes to create a family in this manner.
Question: Is there any work-around to create a loft form faster?
Answer: Depending on the kind of geometry you want to create, you might be able to use a DirectShape
element instead:
- DirectShape performance and minimum size
- DirectShape versus families, category and texture
- From Hack to App – OBJ Mesh Import to DirectShape
Create DirectShape Instead of Conceptual Mass
I the same vein,
the Revit API discussion forum thread
on creating a surface through Revit API suggests
using a DirectShape
instead of a conceptual mass element and also introduces
the new topic group #5.50 dedicated to
the DirectShape
element.
Create Surface from Face
Another discussion also deals with creating a surface from a picked element face:
Question: I'm working on a function that creates surfaces from selected faces. It really does not matter what type of element the resulting surface is as long as it gets exported to Navisworks.
What I have been able to do is to collect the faces with the PickObjects
method.
I have also succeeded creating a mass family representing a surface.
This I have achieved by collecting the edges of the face and then I have created the surface object by using NewFormByCap
.
But this approach does not work on all faces, especially if one of the edges is a HermiteSpline
.
I have tried to create a mesh out of the face and then create a tiny surface of each of the triangles using the NewFormByCap
method. This do sort of works, but it takes way too long – hours for just a single face.
Do you have any idea how I can solve this issue? It does not have to be a surface; it can also be a very thin solid. The function should work on all possible faces in Revit. Any help is appreciated.
Answer: Surprisingly, yes, I do have a viable suggestion for you.
It is even significantly easier and simpler and more efficient than what you have been trying so far, and I think it can work for all surfaces.
In Revit 2015, the DirectShape element was introduced as a possibility to generate faceted geometry directly in the project space.
All previous methods to define custom geometry required a family definition, like the method you describe.
Here is my simple suggestion:
- Grab the surface that you are interested in and tessellate it. You now have a bunch of triangles.
- Create a
DirectShape
based on these triangles.
This discussion ultimately gave rise to the DirectShapeFromFace project discussed above.
Create DirectShape from Solid
Another question on creating direct shapes was raised in
the Revit API discussion forum thread
on whether Hermite and Nurb Spline need to be created on a plane.
It also compares NewLoftForm
with DirectShape
, and then ends up asking:
Question: You state that you can "... create a DirectShape element using the 'solid' geometry created above." Where can I find code which shows how to do that?
Answer: You just have to package the solid in a GeometryObject
array, then DirectShape.CreateElement
works, as described in
the knowledge base article on DirectShape
.
Control DirectShape Colour and Material
Let's round off with some aspects of controlling DirectShape
colour and material, e.g.,
the Revit API discussion forum thread
on using API to change color and/or material of DirectShape:
Question: When importing my IFC file, all elements get converted to DirectShape
elements.
But they now get colored fully black when viewing in shaded mode, and there seems to be no way to change this.
Only workaround is to overwrite graphics per view, but I would rather work to a more solid solution.
Is there some way to change the color of the DirectShape
elements? From what I understood, a material could not be applied to a DirectShape
.
When I import more IFC's from the same source with different export settings, it seems I sometimes get different color results.
So, I can conclude that the color is stored somewhere within these DirectShape
elements. I still cannot find where, and whether it can be changed though API.
Answer: The best way to control the colour of a direct shape element is probably by setting an appropriate category or subcategory on it.
As you presumably know, there are many ways to control the material and colour of a Revit element
If you prefer setting the colour explicitly, you can look at
Here is some code for that using OverrideGraphicSettings
to change the colour of an extrusion object:
Color color = new Color( 0, 0, 0 ); // RGB (0, 255, 255) OverrideGraphicSettings ogs = new OverrideGraphicSettings(); ogs.SetProjectionLineColor( color ); ogs.SetProjectionFillColor( color ); ogs.SetCutFillColor( color ); ogs.SetCutLineColor( color ); doc.ActiveView.SetElementOverrides( fiProfile.Id, ogs );
However, controlling the material and colour via the direct shape category is much more BIMish!
If your IFC DirectShapes do not provide Material
parameters you can set, and if you cannot change their categories, then you could try to:
- Read their geometry
- Create new
DirectShape
elements using this geometry - Delete the original IFC
DirectShape
elements
The advantage is that you have more control over the DirectShape
properties at creation time.
Improve Family Performance Suppressing DirectShape Generation
Question: I use DirectShape
to accelerate creating shapes in my family definition.
It includes so many shapes now that Revit exhibits slow performance.
Can I improve this?
Is it possible to temporarily suppress the representation and drawing of DirectShape regeneration?
Answer: Drawing of elements can be suppressed by hiding the associated category in the view, however it is unlikely that the impact of excessive element creation will be limited to creation. Having too many elements in a family will slow down family load and update times. Further I expect that performance problems may be encountered in the project environment as well. There is likely a better, more scalable solution to the problem they are trying to solve that doesn’t involve creation of too many elements.
When you add an element to a family definition, you can always specify its visibility using the FamilyElementVisibility class.
This includes both view direction and level of detail:
- Fine / medium / coarse
- 2D front / top / back
- 3D
This applies to all geometry in a family definition.
Therefore, you can simply define the visibility for all complex direct shapes to be visible only in 3D views and only in fine level of detail.
I would expect that to solve the issue effectively.
Please note that unlike other elements, it is not yet possible to associate DirectShape
elements in family definitions with visibility controls.
It should apply to all geometric elements, but the operation is actually element based, and has not been enabled for DirectShapes yet.
There doesn’t seem to be a Revit Ideas item filed for this functionality, although the team is already aware of the gap.
Happily, this can be worked around making use of the fact that visibility can be set for a family instance nested in the family definition document and creating a FamilyInstance
containing the DirectShape
inside the family document.
Create DirectShape from Room
Finally, to round off this collectio0n of DirectShape topics, yet another Revit API discussion forum thread on creating direct shape from room and using the spatial geometry calculator versus room boundary to achieve that:
Question: How can I create DirectShape
from rooms with holes?
I read many discussion and post but I wasn't able to solve the problem!
More info and source in this GitHub issue:
Last reading:
Many thanks for help!
Answer: If you want the room with holes, then there will be nothing left.
The room is a hole.
What else is a room except a hole?
Do you mean that you would like to recreate the shape of all the walls with their openings?
Do you mean that you have a room whose plan view includes holes?
Let's assume the latter.
In that case, if your walls are all vertical, this is absolutely trivial.
You can collect the room boundary edges and assemble them into the outer and inner wall loops.
This is demonstrated very effectively by the 2D room editor, which retrieves this information, converts it to a simplified 2D view in SVG, and displays the room outline in a web browser:
Here is a demo recording and lots of other supporting material.
If you want a 3D direct shape instead of a 2D flat version, you will obviously need to determine the ceiling height and location and take that into account as well.
Update:
OK, I now went and read your problem description.
:-)
Very clear. Thank you for that.
Thank you also for your appreciation!
That issue is indeed addressed by the RoomEditorApp that I already pointed to.
When you retrieve the room boundary, the edges may not be contiguous in the right order.
You may need to order them yourself.
There are several ways of achieving that:
DIY. I love do it yourself. Search for SortCurvesContiguous
.
ExporterIFCUtils.ValidateCurveLoops. Maybe. The Edge.AsCurveFollowingFace method. Maybe. But read on through the following, and note the note in the last of the discussions listed below.
For the former, here is some more reading material:
- 2D Polygon Areas and Outer Loop
- Curve Following Face and Bounding Box Implementation
- Retrieving Plan View Room Boundary Polygon Loops
- Extrusion Analyser and Plan View Boundaries
- Room and Furniture Loops Using Symbols
- ContiguousCurveSorter
- Sorting Face Loop Edges
I have explored and written lots more on this topic, as you can see if you follow the various links in the discussions listed above.
I am confident that this will give you all you need.
I see that what you are after is basically the floor slabs under the rooms.
I assume that you are not interested in querying the slabs for their geometry?
You want to create these shapes from the room boundaries?
Well, I think you have all you need now for that.
Response: You ask:
"Do you mean that you have a room whose plan view includes holes?"
Yes, I would like to create the volume of the room. If the room has holes (Because walls), I would also like to subtract the volume of the hole.
In short, I feel ... that you translated perfectly my strange English !
"You want to create these shapes from the room boundaries?"
Yes, exactly. For the elevation, I need only a fixed height (very simple).
Update – here is the solution:
It was necessary to add a call in the boundary loop: curveLoopList.Add(curveLoop);
foreach( IList<BoundarySegment> b in boundaries ) { List<Curve> profile = new List<Curve>(); ++iBoundary; iSegment = 0; foreach( BoundarySegment s in b ) { ++iSegment; Curve curve = s.GetCurve(); profile.Add( curve ); } try { CurveLoop curveLoop = CurveLoop.Create( profile ); curveLoopList.Add( curveLoop ); } catch( Exception ex ) { Debug.WriteLine( ex.Message ); } }
Full code/demo in the Revit3Drooms GitHub repository.
Answer: Maybe I'm missing something here, but why can't you use the SpatialElementGeometryCalculator
?
That gives you the solid you require for the DirectShape
immediately; I've used this for quite a few implementations.
Does this not take holes into account?
Response: The SpatialElementGeometryCalculator
works for rooms?
For a general case this works well but I did not succeed for rooms. Have you already tried ?
Answer: I haven't read through all your material, so I'm not sure about if your specific case needs something special, but it can extract solid geometry and I haven't seen any drawbacks.
The Room
type directly inherits SpatialElement
, so yes, it sure works.
Here is a sample from the documentation:
// Calculate a room's geometry and find its boundary faces SpatialElementGeometryCalculator calculator = new SpatialElementGeometryCalculator( doc ); // Compute the room geometry SpatialElementGeometryResults results = calculator.CalculateSpatialElementGeometry( room ); // Get the solid representing the room geometry Solid roomSolid = results.GetGeometry(); foreach( Face face in roomSolid.Faces ) { double faceArea = face.Area; // Get the sub-faces for the face of the room IList<SpatialElementBoundarySubface> subfaceList = results.GetBoundaryFaceInfo( face ); foreach( SpatialElementBoundarySubface subface in subfaceList ) { // There are multiple sub-faces that define the face if( subfaceList.Count > 1 ) { double subfaceArea = subface.GetSubface().Area; // get the area of each sub-face // sub-faces exist in situations such as // when a room-bounding wall has been // horizontally split and the faces of each // split wall combine to create the // entire face of the room } } }
Response: Thank you very much! That seems really good!
The only special thing: I must have a cut to 1 ft from the floor.
Answer: The BooleanOperationsUtils
class gives you some options where I think the CutWithHalfSpace
seems like the most useful one.
However, I mean, if your code works, then go for it. I was just hinting at a more easy solution for future use.
OK, that is more than enough on direct shapes for now, isn't it?