Need help creating linear dimensions

I made a button to create linear dimensions from parallel elements. It technically works, but the created dimensions are not visible in any view. RevitLookup shows a BoundingBox and a Total Length parameter of 0. The dimension’s Line property shows a reasonable Origin and Direction. What am I missing?

You can paste this into RevitPythonShell and it should run.

from Autodesk.Revit.DB import (
    Transaction, ReferenceArray, Reference,
    Line, XYZ, Options, ElementId,
    Dimension, Grid,
)
from Autodesk.Revit.UI.Selection import ObjectType, ISelectionFilter
from pyrevit import HOST_APP, forms, script

from System.Collections.Generic import List

class Parallel(ISelectionFilter):
    def __init__(self, element):
        if hasattr(element, 'Normal'):
            self.Normal = element.Normal
        elif hasattr(element, 'Curve'):
            self.Normal = element.Curve.Direction
        elif hasattr(element, 'Location'):
            self.Normal = element.Location.Curve.Direction
        else:
            raise ValueError('Element does not have known direction attribute')
        
    def AllowElement(self, element):
        if isinstance(element, Dimension):
            return False
        
        if hasattr(element, 'Normal'):
            if element.Normal.CrossProduct(self.Normal).IsAlmostEqualTo(XYZ.Zero):
                return True
            else:
                return False
        elif hasattr(element, 'Curve'):
            return element.Curve.Direction.CrossProduct(self.Normal).IsAlmostEqualTo(XYZ.Zero)
        elif hasattr(element, 'Location'):
            return element.Location.Curve.Direction.CrossProduct(self.Normal).IsAlmostEqualTo(XYZ.Zero)
        return False
                
    def AllowReference(self, reference, point):
        return False


# projection of u onto v
def project(u, v):
    return v.Multiply(u.DotProduct(v) / v.DotProduct(v))

def reject(u, v):
    return u.Subtract(project(u, v))

def get_curve(element):
    if hasattr(element, 'Curve'):
        return element.Curve
    elif hasattr(element, 'Location'):
        return element.Location.Curve
    else:
        raise ValueError('Element does not have known direction attribute')



uidoc = HOST_APP.uiapp.ActiveUIDocument
doc = uidoc.Document
sel = uidoc.Selection

base_ref = sel.PickObject(ObjectType.Element, 'Select first element to dimension')
base_element = doc.GetElement(base_ref)
try:
    refs = sel.PickObjects(ObjectType.Element, Parallel(base_element), 'Select elements to dimension', List[Reference]([base_ref]))
except Exception as e:
    print(e)
    script.exit()

if len(refs) < 2:
    forms.alert('Please select at least two elements.')
    script.exit()

elements = [(x, doc.GetElement(x)) for x in refs]

dimension_point = sel.PickPoint('Select where to place the dimensions')
view_normal = uidoc.ActiveView.UpDirection.CrossProduct(uidoc.ActiveView.RightDirection)

first_curve = get_curve(doc.GetElement(refs[0]))
r1 = reject(dimension_point.Subtract(first_curve.Origin), first_curve.Direction)

rejection = reject(r1, view_normal)

# get line along which to place dimensions
# hack to flatten into current view
# TODO: fix this so it works for elevations
z = uidoc.ActiveView.SketchPlane.GetPlane().Origin.Z
pt = XYZ(dimension_point.X, dimension_point.Y, z)
rejection = XYZ(rejection.X, rejection.Y, 0)
the_line = Line.CreateUnbound(pt, rejection)

# build reference array of elements to dimension
ref_array = ReferenceArray()
for ref, element in elements:
    try:
        if isinstance(element, Grid):
            ref_array.Append(ref)
        else:
            options = Options()
            options.ComputeReferences = True
            options.IncludeNonVisibleObjects = True
            options.View = uidoc.ActiveView
            geometry = element.get_Geometry(options)
            geometry_instance = next(iter(geometry))
            instance_geometry = geometry_instance.GetInstanceGeometry()
            ig_list = list(instance_geometry)
            ig_lines = [x for x in ig_list if isinstance(x, Line)]
            ref_array.Append(ig_lines[0].Reference)
            
            # ig = next(iter(ig_list))
            # symbol_geometry = list(ig.SymbolGeometry)
            # symbol_lines = [x for x in symbol_geometry if isinstance(x, Line)]
            # symbol_line = next(iter(symbol_lines))
            # ref_array.Append(symbol_line)
    except:
        print('error finding ', ref)
        ref_array.Append(ref)

with Transaction(doc, 'Create dimensions') as t:
    t.Start()
    the_dimension = doc.Create.NewDimension(uidoc.ActiveView, the_line, ref_array)
    t.Commit()

sel.SetElementIds(List[ElementId]([the_dimension.Id]))
print(the_dimension.Id)

I found my error. On line 105, it should use GetSymbolGeometry() instead of GetInstanceGeometry. It feels like there is still something missing, because the parts I have inspected have two Line GeometryElements. I don’t see any difference between them except for their Id and hash code. But for now changing to symbol geometry allows one to select all parallel elements and put a linear dimension on them.

1 Like