Adding Revit Windows From Proxy Centerlines in Rhino Model

Our office is using Rhino for design and Revit for documentation. I am developing a workflow to add Revit Windows in a Revit wall, using a window centerline (curve) placed across a corresponding wall in Rhino. These centerlines have user attributes (“Family Name” and “Rough Opening”) which are queried in GH to select the correct Revit window Family and Type, as well as a “sill height” attribute used to set the Sill Height parameter in Revit and a Level attribute to set the correct Revit Level. This is all mostly working properly, but I have a few key problems, described below. I am curious to hear general feedback on this approach, also.

I am running Revit 2024, Rhino 7 and latest WIP Rhino.Inside.Revit.

My project files:

For anyone who doesn’t want to download the files, I have attached screengrabs of my full GH script at the end of the post.

Window centerlines with attributes

Problem 1:
The key piece I am struggling with is the Location parameter.

I am attempting to set the Location of the Revit window using a point, generated from the intersection of the Revit wall curve and the Rhino window centerline curves placed by the designer.

This works in a simple use case but where it gets complicated is in the transform between the model space of the Revit and Rhino models. Our Revit model is in a Survey Point (shared) coordinate system (at actual elevation above sea level). There is also a project base point in the Revit model, which is equivalent to the 0,0,0 origin (Project Basepoint) of our Rhino model. So I have to use a Plane to Plane Transform whenever sending geometry back and forth, except when simply placing families, via the Add Component (Location) workflow, in which case I can just adjust the location according to the Z-difference between the two coordinate systems. See this screenshot, where Revit wall curves are coming in at 2810.786359’ above the Rhino model, while the Rhino project is down at 0,0,0 plane:

In other words, to find the intersection between the Revit wall curves–which are at three Levels starting at z=2810.786359’ and moving up–and the Rhino window centerline curves–which will be at or near z=0’–I have to project both sets of curves to the same plane. I have chosen to project both sets of curves to the 0,0,0 plane, allowing the intersections to be calculated.

Intersection of window centerline curve and wall curve, both projected to 0,0,0 plane

But now I have to send these intersection points back up to their respective Revit wall curves (at Revit Level 1, Level 2, and Level 3; 2810.786359’ and higher in the Rhino space), because Revit needs the Location point to be within a certain tolerance of Z-axis distance from the host wall curve for it to recognize it as a location for inserting hosted element. How should I handle this more elegantly? I don’t currently have a solution for automating sending each intersection point back to the wall curve it belongs to. Currently I am manually typing in the exact height for this final transform of the Location point (2810.786359’ + elevation of specific base level of wall in question).

Problem 2:
Another problem is that Revit seems to project the Location point to all walls in the list, rather than requiring that the Location point actually intersect or touch the wall curve (in the X and Y dimensions) of a potential Host. You can see that in the image below, where a single point results in a window in all four walls which would intersect with a line drawn perpendicular to the point (perpendicular to the X,Y axes). Is there any way to change or control this Revit behavior?

If there isn’t any way around this, how can I manage my data trees to pair up walls with their respective insertion location points when using the Add Component (Location) script? I have basic skill with Path Mapper and related tools, but I can’t visualize the logic needed here.

Problem 3:

I am trying to feed the Level parameter of the Add Component (Location) script by querying the Level value from the User Attribute of the Rhino window centerline element (see above). This way I will automate assigning the proper Level value to each window from the value entered by the designer in the properties of the window centerline in Rhino.

Querying the Level value from the User Attribute

The problem is I can’t understand how, using Rhino.Inside.Revit filters or whatever method of querying elements, to take this text element (e.g. “Level 3”) from the User Attribute and use it to query Level 3 in Revit and send that as a Level element to the Add Component script.
[23 1110 Rhino Model for Forum Post.3dm|attachment]

Aborted attempt to use this User Value to query a Revit Level and feed it into the Add Component (Location) script.

I can’t figure out the correct way to do this.

Thanks in advance

Full script:

Hi Brian, Thanks for posting a thorough explanation and files. I’ll take a look.

Thank you @Japhy
We appreciate your taking a look.

for Question 3.

You can get the Levels by Name this way…

Question 1.

You can use the Project Basepoint to transform your geometry. Then use its inverse to put it back.

Really only one transform is needed.

Question 2

You have all the walls in the project going into the host input, so its trying to put a window in each one.

Your center lines will have the Element ID that you can pair with the appropriate point.

In this example something like this would work. I created a cull pattern from the point intersection list & removed all the walls not hit.

Perfect, thank you

Using Project Basepoint to transform my geometry is an improvement over my workflow. However, what this doesn’t account for is that the wall curves are coming in at three different levels, but then I am flattening all that to find the intersections, down at 0,0,0. So the problem actually is how to send each intersection point back up to one of the three levels, not all back up the same amount of distance.

I have set up something (see screenshot) which moves the window location point a few feet higher after the reverse transformation sends it back up to Revit coordinate system, to account for the difference between the base Level of each wall and the project base point.

I am thinking of some way I can automate the pink panel, which is the difference in elevation between the associated Level and Project Base Point. Probably can calculate this elevation difference by querying the level of the appropriate wall, getting Level elevation from Level Identification, subtracting the elevation of Project Base Point, then sending this number to Pink panel.

I have successfully automated the pink panel:

Everything is working correctly. The new question is: How can this work for multiple window centerlines? Is it possible to match data trees from start to finish, and maintain separate properties for each window added? So far, it seems to work, except for the Cull Index method of selecting the correct host wall for each window insertion point. Does anyone have any thoughts on this?

Here is my script at this point:

Below you can see I have successfully added two windows from two centerlines, each with a different sill height, for example:

When I add a third window centerline in Rhino, overlapping a different wall from the other two, the script that filters the Revit walls (Cull Index method) cycles through to the wall associated with the third centerline only. (I apologize for my low-level GH skills, and am working on improving those. Not asking anyone to do my learning for me; hoping it will be useful for someone else if I share my process publicly.) Would appreciate any pointers from anyone.

I was able to manually select the walls using List Item, which did successfully associate the correct host wall with each location point sent to the Add Component script, See below:

Now I just need to automate this selection of host walls.

I got the selection of host walls running using a Python script, attached below, along with screenshot of GH.
23 1114 Query Revit wall ElementIDs from intersection points of wall curves and rhino window centerlines.txt (4.4 KB)

Script is handling multiple windows in multiple walls, with varied sill heights

Highlighted modules are the new scripts

Python script receives all ElementIDs of all Revit walls, receives list of wall curves used in intersection, and list of resulting intersection points. It associates the wall curves with the corresponding Revit wall ElementID, in a dictionary. It also associates the intersection points with their corresponding Revit wall ElementID numbers, by testing proximity of each point to each line to confirm which wall curve, and therefore wall, they correspond to. It then outputs a list of wall ElementIDs matching the list of intersection points. I know there is a more elegant way to do this but I was having trouble accessing the RevitAPI from inside the GhPython environment so I externalized a lot of the referencing into Grasshopper and used the test of point proximity to lines as a proxy for directly referencing the Revit walls, grabbing their curves, and intersecting them with the centerlines in Python, which would be better I think.

The cull pattern makes sense, and works. (Please see my Python solution to how to query ElementIDs for intersection points with multiple walls. I am curious if you @Japhy or anyone else has a better way to handle this.)

Also, @Japhy I’m not sure what you mean when you say that my centerline will have the Element ID of the wall in its properties. “Your center lines will have the Element ID that you can pair with the appropriate point.” Do you mean the wall curves? Or my Rhino window centerlines?

Just saying that these are a 1 to 1 relationship with the Revit Elements (Wall = Centerline) and you can maintain the properties if need be.

Understood, thanks