Today, let's look at some geometry related questions, a small enhancement of the line intersection we discussed two days ago, NURB spline and solid creation:
- Creating a NURB spline
- GeometryCreationUtilities, moving and copying solids
- Line intersection enhancements
- Built-in parameter checker for Revit 2016
- 1001 discussion forum posts
Creating a NURB Spline
This question was raised by anarchyfree in the Revit API discussion thread on a NurbSpline creation error:
Question: I'm working on a Revit plugin that in part accepts curves from Rhino that can be used to create a beam in Revit.
My code works well, when controlpoints of curve are greater than or equal to four. But with three control points arises a problem.
How can I draw three control points curve in Revit API?
List<XYZ> ctrPoints = new List<XYZ>(); ctrPoints.Add( new XYZ( Utils.mmToFeet( 72500.017337 ), Utils.mmToFeet( -5072.522765 ), Utils.mmToFeet( 0 ) ) ); ctrPoints.Add( new XYZ( Utils.mmToFeet( 105082.371745 ), Utils.mmToFeet( -748.798009 ), tils.mmToFeet( 0 ) ) ); ctrPoints.Add( new XYZ( Utils.mmToFeet( 117899.12727 ), Utils.mmToFeet( -15572.997171 ), Utils.mmToFeet( 0 ) ) ); List<double> weights = new List<double>(); weights.Add( 1 ); weights.Add( 1 ); weights.Add( 1 ); List<double> knots = new List<double>(); knots.Add( 0 ); knots.Add( 0 ); knots.Add( 0 ); knots.Add( 52464.568605 ); knots.Add( 52464.568605 ); knots.Add( 52464.568605 ); NurbSpline detailNurbSpline = NurbSpline.Create( ctrPoints, weights, knots, 3, false, true );
Answer: Congratulations on your interesting project!
I am glad to hear that it is working well.
Would you like to share some of it here? I am sure there would be a lot of interest in that.
To answer your question:
The exception thrown when this code is run explains that there must be at least degree + 1 control points.
Since the degree passed in is 3, this code violates that rule.
Revit’s version of creating splines is NurbSpline.Create([control points], [weights]).
It automatically sets the degree based on the number of points as described in this internal comment:
// Use degree 1 for two CPs, degree 2 for three CPs, // and degree 3 for four or more CPs.
GeometryCreationUtilities, Moving and Copying Solids
Question: How can I move / turn solid objects that are formed by methods of the class GeometryCreationUtilities?
Functioning code example desired.
AND
How can I make a copy of a solid object which is formed by methods of the class GeometryCreationUtilities?
Functioning code example desired.
Answer: Happy to see you are exploring the Revit API.
I hope you are aware that the Revit API is very different from many other APIs for non-parametric CAD systems:
The most important thing with Revit programming is to understand the product, user workflow and best practices before you even start thinking about programming anything.
Regarding your questions:
I assume you know that the GeometryCreationUtilities only create non-persistent in-memory objects.
They are not Revit database elements and will not be stored in the Revit model.
The remarks on the GeometryCreationUtilities class state that the shapes created by these utilities are not a part of any Revit document, but can be used in conjunction with other geometric utilities from within API applications.
As you can see from the Revit API help file RevitAPI.chm (have you installed that, and looked at it?), the objects generated by the GeometryCreationUtilities class are instances of the Solid class.
You can see what operations are possible on these objects by looking at the list of Solid class members.
I do not see any possibility to move or turn a Solid after it has been created.
Remember, in a parametric CAD system,. solids are just views of parametric elements.
Therefore, there is no provision for modifying solids. They are just a read-only representation of the parametric BIM.
You are more lucky with your second question, though.
A possibility to create a copy of an existing solid was just introduced in the Revit 2016 Geometry API additions:
Solid – copy
The new method:
- SolidUtils.Clone()
creates a new Solid which is a copy of the input Solid.
Solid – create transformed
The new method:
- SolidUtils.CreateTransformed()
creates a new Solid which is the transformation of the input Solid.
Funnily enough, we actually explored the topic of cloning a solid just three weeks ago. The comments on that post also discuss workarounds for achieving this before the advent of the SolidUtils Clone method.
Here is a rather contrived and completely untested code sample snippet demonstrating how to extract and clone the first non-empty solid encountered in a given Revit element:
Solid GetFirstSolidClone( Element e ) { Solid clone = null; Options opt = new Options(); GeometryElement geo = e.get_Geometry( opt ); foreach( GeometryObject obj in geo ) { Solid solid = obj as Solid; if( solid != null && 0 < solid.Faces.Size ) { return clone = SolidUtils.Clone( solid ); } } return clone; }
I hope this helps.
Line Intersection Enhancements
Two days ago, I discussed the line intersection suggested by Magson Leone.
While editing that text, the thought struck me:
What happens if the two given lines are:
- coincident
- parallel
In the first case, the intersection of the two is the line itself, infinitely many points.
In the second case, there is no intersection, zero points.
I do not see either of these two cases being handled properly.
Do you have any suggestions?
Do you have any test cases?
We should test for (i) collinearity and (ii) coincidence.
If we want to achieve perfection.
The way it is now, I expect an exception will simply be thrown?
Magson very kindly replies:
Eu fiz o teste que você sugeriu para ver se ocorresse algum erro nos casos de linhas coincidentes e paralelas e realmente o erro aconteceu. Então eu acrescentei algumas linhas no código para fazer esta verificação usando o método double.IsInfinity(). Com esta mudança, nos casos em que x ou y for um número infinito o ponto p5 não será criado e valor de retorno será nulo.
I tested the method as you suggested and confirmed the expected errors for coincident and parallel lines.
I added a check using double.IsInfinity to detect and handle these more gracefully.
With this enhancement, p5
is not calculated if x
or y
would be infinite, and the method simply returns null
.
/// <summary> /// Return the 2D intersection point between two /// unbounded lines defined in the XY plane by the /// start and end points of the two given curves. /// By Magson Leone. /// Return null if the two lines are coincident, /// in which case the intersection is an infinite /// line, or non-coincident and parallel, in which /// case it is empty. /// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection /// </summary> public static XYZ Intersection( Curve c1, Curve c2 ) { XYZ p1 = c1.GetEndPoint( 0 ); XYZ q1 = c1.GetEndPoint( 1 ); XYZ p2 = c2.GetEndPoint( 0 ); XYZ q2 = c2.GetEndPoint( 1 ); XYZ v1 = q1 - p1; XYZ v2 = q2 - p2; XYZ w = p2 - p1; XYZ p5 = null; double c = ( v2.X * w.Y - v2.Y * w.X ) / ( v2.X * v1.Y - v2.Y * v1.X ); if( !double.IsInfinity( c ) ) { double x = p1.X + c * v1.X; double y = p1.Y + c * v1.Y; p5 = new XYZ( x, y, 0 ); } return p5; }
I updated The Building Coder samples accordingly. The version providing the enhanced Util.Intersection method to gracefully handle parallel or coincident lines described above is release 2016.0.120.2.
Built-in Parameter Checker for Revit 2016
Remy van den Bor of ICN Solutions b.v. thanked me for migrating RevitLookup to Revit 2016 and asked whether BipChecker, the built-in parameter checker, is it still being maintained.
That prompted me to migrate it right there and then.
It was one of the easiest migrations ever.
Not one single warning ensued.
The new release 2016.0.0.0 of BipChecker for Revit 2016 is now available from the BipChecker GitHub repository.
Enjoy!
1001 discussion forum posts
In answering Remy's request for the BipChecker migration, I posted my 1001st Autodesk discussion forum post:
:-)