Align objects with a 3d curve

Hello,

I want to be able to get a 3d curve from a Revit file. On this curve I want to place equally distanced objects, with the ZAxis always vertical and the XAxis aligned with the tangent of the curve in the point.

I’ve tried out several things: horizontal frame at parameter, point at parameter, tangent at parameter. So far I am able to place the objects at parameter. The next thing I need is that the object is aligned with the curve. I want the XAxis aligned at the tangent of the curve at the point, and the ZAxis needs to be ‘always vertical’.

I’ve read through the forums for solutions but I guess I’m missing something here. How can I align the XAxis of the revit element to the XAxis to the tangent of the curve at the point? Anyone can help or give me a few pointers?

a few screenshots:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2 Likes

Pics surely behave strange…

align object along curve workspace

align object along curve 3d

align object along curve Revit

1 Like

Try this. It will get you the perpendicular (from which you can get the tangent). Then you can extract that data to create a vector with the vertical Z.

What version of Dynamo are you using? The “Curve.HorizontalFrameAtParameter” node seems to be misbehaving. It’s working as expected on the latest daily build:

2015-07-24_11-42-03

Hey Guys,

thanks for the replies. Maybe my explanation was a bit poor or not clear enough. If you look at 2nd screenshot I posted, I can make use of the “Curve.HorizontalFrameAtParameter” node and it’s working as expected and in the same way as Dimitar posted. So maybe I’m halfway there and the question is more:

How to use these horizontal frames to place and align Revit Elements on? So that the Z-axis of the Revit object is always vertical (just as the Z-axis of the horizontal frames) and the X-axis and Y-axis are also aligned with the horizontal frames coordinatesystem: X-axis is tangential to curve and horizontal and Y-axis is perpendicular to curve and horizontal:

align object along curve 3d_2

1 Like

Hi, I am not sure if I fully understand what you want to achieve, but anyway….

“Curve.HorizontalFrameAtParameter” is giving you the coordinate system you want, because:

  1. The X Axis is going to be horizontal and contained in the vertical plane that included the normal direction ( that means that in a plan view the X Axis will be apparently tangent to the curve )

  2. the Y Axis is going to be horizontal and perpendicular to the curve

  3. Being X and Y horizontal , Z is obviously always vertical

Maybe you can have the issue of no consistent orientation of the X and Y Axis, that imply that the objects you align may flip the orientation.

01

I think that this is a little bug that will be solved at some moment. In the meantime you can avoid this problem, making directly the vectors and the coordinate systems, instead of using “Curve.HorizontalFrameAtParameter”

02

This is the code

vecNormal=cu.TangentAtParameter(par);
vecYAxis=vecNormal.Cross(Vector.ZAxis());
vecXAxis=vecYAxis.Cross(Vector.ZAxis());

CoordinateSystem.ByOriginVectors(
cu.PointAtParameter(par),
vecXAxis,vecYAxis);

Well, you have now your coordinate systems to align the objects. So …how to do this exactly?..depends if you are talking about populate dynamo geometry or revit family instances.

The first option is really straightforward. Just build the object related to a coordinate system convenient to work with (normally the default one), thinking the origin and default X and Y will be the equivalent ones in the curve. And later transform the object from this coordinate system to the ones in the curve.

The second option is a bit more tricky, because the family instance are thought (normally) to be hosted in a level with an offset with just an insertion point. As far as I know there is not a direct way to populate conventional family instances controlling the 3d orientation. This is the way a manage to do it (probably other dynamo users are finding other ways):

-The object I want to populate is a normal family, for example a generic model

-I create and adaptive component with just three points that will be the origin and the orientation of X and Y axis

-The conventional family is nested in the adaptive component, inserted in a way that will follow the orientation drived by the three points

-In Dynamo I use the coordinate systems in the curve for making groups of three points (for example by Cartesian coordinates 0,0,0 ….1,0,0…and …0,1,0)

-In Dynamo I create adaptive components by points. So the result is that you will have the family instances populated following the desired 3d orientation .

This “intermediate” adaptive component with three points will be always the same, so the next time you need to make something similar you will jut reload the nested family for another one.

Anyway, this kind of workflows in Dynamo makes sense when you need to orientate the family instance with precise NO VERTICAL orientation. If you want just vertical orientation maybe you can find your way using directly “divide” and “repeat” in Revit without using Dynamo at all.

2 Likes

Joop,

There is no simple way to align a point based family instance to a Dynamo CS. (the same way you have to take a few steps inside the Revit UI: place, offset, rotate)

Luckily for you, Dynamo 0.81 introduced a new node called “FamilyInstance.SetRotation”. Prior to that it was necessary to access the API every time you wanted to rotate something.

With its help and by splitting the process into steps, you can try something along the lines of this:

2015-07-24_17-47-07

2015-07-24_17-48-54

Eduardo,

now we’re getting close. I used your code to place a generic model (nested in an adaptive component of three points) along the curve. I made the three coordinate groups that are used to place resp. point 1, 2 and 3 of the adaptive component.

So now I have three lists containing: adaptive points[1], adaptive points[2] and adaptive points[3].

Next problem I have is I want to make a new list that uses the pairs of these lists something like this:
<table style=“height: 148px;” width=“489”>
<tbody>
<tr>
<td>adaptive component nr.</td>
<td>placement point 1</td>
<td>placement point 2</td>
<td>placement point 3</td>
</tr>
<tr>
<td>1</td>
<td>list A[0]</td>
<td>list B[0]</td>
<td>list C[0]</td>
</tr>
<tr>
<td>2</td>
<td>list A[1]</td>
<td>list B[1]</td>
<td>list C[1]</td>
</tr>
<tr>
<td>3</td>
<td>list A[2]</td>
<td>list B[2]</td>
<td>list C[2]</td>
</tr>
<tr>
<td>4</td>
<td>list A[3]</td>
<td>list B[3]</td>
<td>list C[3]</td>
</tr>
</tbody>
</table>
The placement works when I test it wit a get item at index. But I’m quite new to dynamo and the list operations that it offers for different tasks. I want to be able to make new sublists of the rows in the above table. {(listA[0], listB[0], listC[0], listA[1], listB[1], listC[1], etc, etc)}.

Here’s what I have so far:

It’s messy I know :).

align object along curve 3d_3

 

 

 

 

 

 

 

And the placement of one object with the get item at index operation:

align object along curve 3d_5

align object along curve 3d_4

 

 

 

 

 

 

 

@Dimitar I guess if I get your solution to work that would be more ‘elegant’ but I first started with Eduardo’s idea just to get some more practice in Dynamo. After that I will study your ideas.

B.T.W. just for the information. I wanna see if I can use the divide and repeat function of Revit in a more robust way (clicking a curve and placing objects along them instantly), and without the limitations the work planes give me sometimes doing it in the traditional manner. For this example I’m trying to place crossties along a imagenary and exaggerated curve representing the railroad alignment.

Thanks in advance!

The workflow pointed by Dimitar is quite interesting. “FamilyInstance.SetRotation” has called my attention, but I havent used until now.

Hopefully more users will share here how they deal with this.

I will explain a bit more in detail the options for aligning elements mentioned before. First with Dynamo geometry:

Sample_01

I am just importing a SAT file with solid geometry that will became Dynamo solids. In this case I am not using always vertical orientation of the CS. I want the X axis to be tangent and the perpendicular Y axis, always horizontal. This is the code for archiving this:

vecXAxis=cu.TangentAtParameter(par);
vecYAxis=Vector.ZAxis().Cross(vecXAxis);

CoordinateSystem.ByOriginVectors(
cu.PointAtParameter(par),
vecXAxis,vecYAxis);

Notice that the imported geometry is in the center of the grid and how I am using a transform that only request the target CS (it is implied that I am using the default CS as the reference one). It is very easy and Dynamo is impressive fast to populate solids like this. But…

–If you are just copying the element (all the instances are the same but with different orientation) you are ending with several different solids bodys that are indeed exactly the same. So it will be extremely inefficient when to want to do something with this outside of Dynamo

–If in the future you want to change a little bit the element that is repeated. You will have to run again the full thing in Dynamo always even if the position and orientation of the every element is exactly the same.

So even it looks really easy, for populating the same elements just in a different position and orientation, it is not very practical in the long term. It is far smarter to populate instances of Revit families . So now we start with the second option:

-This is a conventional (not adaptive and no conceptual mass) family.

Sample_06

-Now you nested it in an adaptive component with three points.

Sample_05

You can download this sample here (just replace the nested family with yours).

Family_with_3D_orientation

If you have curiosity how it works….

1.The first point drives the origin

2.The second point drive the X Axis and is hosted in the correspondent plane of the first point with an offset.

3.The third point drives the Y axis and again is hosted in the correspondent plane of the first point with an offset .

  1. The nested family is hosted in the horizontal plane of the first point

We create in dynamo the group of three points using the CS in the curve

Sample_02

And finally we can create the adaptive components…

Sample_03

Sample_04

After this, I can change the original simple family and everything will update without having to use Dynamo at all.

Joop,

I didn’t see your answer until I finished to write mine. Probably you can find there the solution to the issues you mention. Let me know if something is not clear to you in the workflows I propose.

About divide and repeat I don’t understand exactly what you mean with “without the limitations the work planes give me sometimes doing it in the traditional manner”. I personally find very fast and convenient to divide a curve using Revit “divided path” . The problems start when you want to place an element following that division with precise control of the 3d orientation, although if it is always vertical you can manage more or less.

@Eduardo:

thanks for your reply. I allready made it to point 4 you mentioned. I got the family in an adaptive component with the 3 points. I was struggling with the cartesian coordinates but your explanation makes it more clear. I have to become familier with the code blocks and list operations since I’m just starting with Dynamo.

About the Revit divide and repeat we always struggle with the orientation of the adaptive components. Since you have to nest families a few times and each family can have options like: Always Vertical - Work plane based, and adaptive points all have orientation options like automatically from host (or something like that, can’t look it up right now), always vertical, vertical on placement etc. it seems hard to control the orientation within Revit.

The Revit adaptive components give us trouble sometimes with the orientation. Maybe you (or other readers) have had these problems within Revit too and have solutions to this? I would be glad to hear about them.

So I switched to Dynamo to see if it gives me more control. Doing googles on it didn’t really give me straight answers but this topic seems to bring some real answers and it seems to bring more control for the operations I want to do. Thanks for that! I’m at home now (not at my office) so I can’t try it out right now but I’m confident your explanation will work for my example.

@Dimitar: I did manage to try out your solution and it seemed to work pretty neat. Thanks, the colored blocks also made it more understandable! I’ll report more on it on Monday :).

Just a moral question or maybe a more practical one for you both. Between the two solutions, is there a better / more efficient one of the two? I’m guessing in some time this could really be useful for some of the tasks we need to do on a regular basis and I guess I should use the most effecient one.

Again thanks to both, and have a nice weekend!

 

Joop,

I have the same problems with controlling the orientation of the elements following the path. According to the Revit documentation “Global Z and host XY” will give the result of always vertical and tangent in plan. But in practice this is only working as expected when the path is flat. If the path is a 3d curve, the orientation of XY is not the expected one.

So if the path is a 3d curve, it is only practical for me to use divide and repeat if the element to populate have always the same orientation (“Global XYZ”). For anything else I have always to spend too much time for making it to work, so nowadays I rather prefer to use Dynamo and having full geometrical control without any unexpected behavior.

About comparing Dimitar solution with the intermediate adaptive component one…depends in a lot of factors. Dimitar one is limited to vertical orientation but if it is the case, maybe it is better always to avoid unnecessary layers of complexity if possible. Anyway if you are going to use the intermediate adaptive component for vertical orientation I would only use two points instead of three. One for the origin and one, for example, the perpendicular Y axis.

In resume, what I will do if I have to repeat elements following a 3d path:

1-If the element have always the same orientation ---- directly Revit with divide and repeat

2-If the element is always vertical and tangent in plan ---- Dynamo with Dimitar solution or intermediate adaptive component with 2 points

3-If the element have 3d orientation – Dynamo with intermediate adaptive component with 3 points

If you prefer an universal solution, only the third one will always work. But it adds unnecessary complexity in 2. And in 1 is totally overkill.

I completely agree with Eduardo’s suggestion. Nesting the family inside the AC is the most robust approach.

Also the method above was just a quick cobbled up suggestion. Revit seems to have some very specific notions about “Euler” angles’ so to cover all our bases when calculating the angle of rotation, we’ll most likely need an imperative solution. Today I played some more with the “FamilyInstance.SetRotation” node and came to the following observations:

rotation in revit

Based on that, this is what I suggest for vertical instance rotation:

2015-07-27_16-26-08

Here’s the DS node snippet:

def RvtAngleAtParam(crv, par)
{
t1 = crv.TangentAtParameter(par);
vx = t1.X;
vy = t1.Y;
alpha = t1.AngleBetween(Vector.XAxis());
return = [Imperative]
{
if (vx >= 0 && vy >= 0)
{
return = - alpha;
}
elseif (vx < 0 && vy < 0)
{
return = - alpha;
}
elseif (vx >= 0 && vy < 0)
{
return = alpha;
}
else
{
return = alpha;
}
}
};

This should be further improved. Hope it helps. Some Revit trees:

2015-07-27_16-25-44

Hi Dimitar,

I’m experiencing some problems too with larger more complex curves. It seems sometimes the operator had to be > and the other time it needed to be <.

I’m looking at your last example and the first thing that doesn’t work for me is the code block: rel.Curves;

What does this do and why my Dynamo doesn’t recognize this?

For info: I try to click a 3d Autocad line of a drawing that is linked in the project. I want to test it on an imported cad line since these are the types of lines we mostly are dependent of.

Hi Joop,

That’s the DS code for the “Element.Curves” node. If it’s failing with it, try using “Element.Geometry” instead. The “Element.Curves” node should be a bit faster at the price of being more limited in the types of curves it can extract.

If you manage to improve the above code, please share! :slight_smile:

2015-07-28_15-16-21

Gave it another spin. I think the selection of the 3d cad line gives me some problems compared to the creation of a 3d line in Dynamo.

The objects want to ‘flip’ at a certain point. I created a if check so that once they flip I create another angle to turn them 180 degrees. I guess the workspace could be a lot cleaner then in my screenshot. Still it’s working it seems but I have the feeling that in another project this could again give me errors or objects that aren’t rotated correctly.

align object along curve 3d_7

Hello!

I’m facing the same problem. I am using Dynamo 0.8.2.2392 and want to be able to place equally distanced objects on a 3d curve (3d spline from points) and those objects should align to the curve. I’ve tried out Joop’s solution but it’s not working and I can’t find the reason for it. The objects do not turn in the right direction and there are different warnings :frowning:

I would be very thankful for some help!

align objects to 3d line_V3_prob

align objects to 3d line_V3_prob_model

1 Like

EDUARDO P. ROCA i only get instance of the family using your code not one at each point can you help with this

@Dimitar_Venkov, I know this is an older thread, but any chance you can get me an enlarged version of the image? I think it is exactly what I am looking for on another topic I posted because I can’t ready what in here.

Thanks in advance.

You’ll need to go to the old forum to see the images:
http://dynamobim.org/forums/topic/align-objects-with-a-3d-curve/#post-18878

Alternatively, the new “Vector.AngleAboutAxis” node should be able to give you a more accurate angle representation.