Drawing walls (and there layers) with detail lines

I have the interesting goal of illustrating all wall types that are used within the project onto a wall legend, as i feel the legend component for walls is very crude and doesn’t allow for snapping of certain extra details like studs or insulation.

I have made in my opinion great progress and have already grabbed all wall types used within the project and got a basic foundation for the illustrating system going. The main issue stopping me now is properly drawing the walls and their individual layers with lines.

The system I have currently is to start from an origin (0,0) then using GeniusLoci to get the thickness of each wall layer and creating the top left point of the wall so (0,T) T meaning thickness, then having an input slider for wall length for example 3 feet, create the top right point (3,T) then the bottom right point (3,0). Now this would work perfectly if all walls were a single layer. But since there are multiple layers to walls each layer is at a different y from the origin (bottom left most point of the wall cut) and I need to find a way to increase the starting origin and thickness by the already existing layers.


(a 6 inches stud wall with what should be 5/8 inch GWB on each side of the wall)

What can be seen is the rectangles are made but all layers start from the same origin so the smaller 5/8 inch layers are atop each other and inside of the 6 inch stud layer of the wall. Below is what I am planning to accomplish or at least what I can make up right now.

So my current fix for the system is finding a way for any layer past the first to increase its starting origin and its own thickness by the thickness of all previous layers. If anyone has any ideas on how I could accomplish this I would GREATLY appreciate it or even any other ideas on ways to do the same task.

I have little to no python experience and I’ve been purely creating this with the visual playground. I will attach what I have done so far. Some key things from it that should be known is I just want to get this system working with one wall before tackling multiple walls, and that there also might be some random useless nodes that are only there for information and don’t actually do anything.


wall_legend.dyn (43.1 KB)

We can’t really tell what your graph is doing without seeing the data. Make sure you always pin the node preview bubbles for your screenshots so we can see what’s going on.

It sounds like your two options are:

  1. Cumulative Sum: Sum the current layer thickness with all previous layer thicknesses to generate a rectangle with the overall depth of the wall so far. This causes overlap but gives the correct visual. I think this is what you’ve attempted in your graph so far.
  2. Cumulative Offset: Offset the current layer by the total of all layer thicknesses so far. This would keep each layer at the correct individual thickness and placement.

Turns out they’re both essentially the same thing. Cumulative Sum starts with the initial value and incrementally adds the value before to the new total. Cumulative Offset starts at 0 and adds each cumulative value to the total.

1 Like

Start by taking the total sum at each step of the list of thicknesses. List.DropItems with a range from - the length of the list to zero (-List.Count(n)..0; in design script). Sun the resulting sublists - the first value in those lists should be 0 and the last is the thickness of the wall.

Then offset your initial line by those distances. The result of that will be a list of lines. Drop the last and first with another List.DropItems node (watch your lacing) and use a List.Transpose on the results. There should be a set of pairs of lines, each adjacent to each other.

Use a Surface.ByLoft to build a surface from each layer of the wall. You can use this to generate a filled region, or place isolines to find coordinate systems to place nested components, or otherwise build out the rest of your toolset.

I think this is the wrong approach.

I think you would be better served by creating tools that would align and snap your detail components to the legend wall component.

Personally, I’d grab the wall legend element and get the edges from the geometry. This is much simpler as you don’t have to wade through the layers in the wall as I recall. Then just place reference planes on those edges. Way less complex.

  • You have your snap points.
  • The object remains live in the project if there are any updates.
  • You can still use the annotation tools for live notes.
1 Like

How would you go about getting all the edges of the wall legend, I’ve been trying to figure out how and yet all i get stuck with is a singular solid wall with no individual layers.

The whole shebang in python. Quite straightforward.
I ran this in Revit Python Shell so it is missing some of the typical dynamo boilerplate for imports and all. Add that and it needs no in’s or outs. Just pop - done. All wall plan LegendComponets.

Of course this would be best in pyRevit. (Which is where I’ll be adding it.)
Note, I slid the walls over so you could see the ref planes created. Otherwise they just sit under the wall. Will need a tweak for wall types in section. This is for plan display.
No error trapping if you’re in the wrong view or there are no elements found. pyRevit has the whole Context tag to help with that.

elements = FilteredElementCollector(doc, doc.ActiveView.Id).OfCategory(BuiltInCategory.OST_LegendComponents).ToElements()

def draw_reference(element, elemtype):
    width = element.LookupParameter("Host Length").AsDouble()
    struct = elemtype.GetCompoundStructure().GetLayers()
    layerwidths = [layer.Width for layer in struct]

    location = element.BoundingBox[doc.ActiveView]
    startpt = location.Max
    endpt = XYZ(startpt.X - width, startpt.Y, startpt.Z)
    bubble_dir = XYZ(0, 0, 1)
    doc.Create.NewReferencePlane(startpt, endpt, bubble_dir, doc.ActiveView)
    for d in layerwidths:
        if d > 0:
            startpt =  XYZ(startpt.X, startpt.Y - d, startpt.Z)
            endpt = XYZ(endpt.X, endpt.Y - d, endpt.Z)
            doc.Create.NewReferencePlane(startpt, endpt, bubble_dir, doc.ActiveView)

t = Transaction(doc, "Draw Reference")
t.Start()
for element in elements:
    typeid = element.LookupParameter("Component Type").AsElementId()
    elemtype = doc.GetElement(typeid)
    if isinstance(elemtype, WallType):
        draw_reference(element, elemtype)
t.Commit()

Width some spit and polish and dab of error checking.
Now does both plans and section wall views.
Easily adaptable to floors or ceilings if needed.

import math
from Autodesk.Revit.DB import *

elements = FilteredElementCollector(doc, doc.ActiveView.Id) \
    .OfCategory(BuiltInCategory.OST_LegendComponents) \
    .WhereElementIsNotElementType() \
    .ToElements()

def draw_reference(element, elemtype, view_direction):
    angle = -math.pi / 2
    bubble_dir = XYZ(0, 0, 1)

    width_param = element.LookupParameter("Host Length")
    if not width_param or not width_param.HasValue:
        return
    width = width_param.AsDouble()

    compound_structure = elemtype.GetCompoundStructure()
    if not compound_structure:
        return
    layer_widths = [layer.Width for layer in compound_structure.GetLayers() if layer.Width > 0]

    bbox = element.BoundingBox[doc.ActiveView]
    if not bbox:
        return
    max_pt = bbox.Max
    axis = Line.CreateBound(max_pt, max_pt + XYZ(0, 0, 1))

    start_pt = max_pt
    end_pt = XYZ(start_pt.X - width, start_pt.Y, start_pt.Z)
    start_plane = doc.Create.NewReferencePlane(start_pt, end_pt, bubble_dir, doc.ActiveView)

    if view_direction == "Section":
        ElementTransformUtils.RotateElement(doc, start_plane.Id, axis, angle)
        ElementTransformUtils.MoveElement(doc, start_plane.Id, XYZ(0, -width, 0))

    for layer_width in layer_widths:
        start_pt = XYZ(start_pt.X, start_pt.Y - layer_width, start_pt.Z)
        end_pt = XYZ(end_pt.X, end_pt.Y - layer_width, end_pt.Z)
        ref_plane = doc.Create.NewReferencePlane(start_pt, end_pt, bubble_dir, doc.ActiveView)

        if view_direction == "Section":
            ElementTransformUtils.RotateElement(doc, ref_plane.Id, axis, angle)
            ElementTransformUtils.MoveElement(doc, ref_plane.Id, XYZ(0, -width, 0))


t = Transaction(doc, "Draw Reference Planes")
t.Start()
for element in elements:
    try:
        type_id = element.LookupParameter("Component Type").AsElementId()
        elemtype = doc.GetElement(type_id)

        if isinstance(elemtype, WallType):
            view_dir_param = element.LookupParameter("View Direction")
            if view_dir_param and view_dir_param.HasValue:
                view_direction = view_dir_param.AsValueString()
                draw_reference(element, elemtype, view_direction)
    except Exception as e:
        print(f"Error processing element {element.Id}: {e}")
t.Commit()

While I preferred to keep the process visually within dynamo the amount of people who just default to straight python code overwhelms those who use generic dynamo so I guess its time to set it up.

After spending time creating the UI with pyRevit and inputting the script you helpfully developed it is returning an error:

IronPython Traceback:
invalid syntax (line 60) 
unexpected token 'Error processing element {element.Id}: {e}' (line 60) 
invalid syntax (line 60) 
unexpected token 'Error processing element {element.Id}: {e}' (line 60) 
unexpected token ')' (line 60) 
unexpected EOF while parsing (line 61)

What could the issue possibly be?

Just noticed I had a “-” after t.,Commit(). Keyboard slip.
I updated the above.

Also note that this works for basic walls. Need to add some more logic in for walls that have sweeps and have split regions.

1 Like