Create Dimension Between Column Reference (Center) and Grid

Hi all!

I created a script where you select the column and the grid and it is supposed to create a dimension line between them. Problem is: dimension element is created but does not show in the screen.


project - AutoDimension.rvt (6.8 MB)

I created a python script that gets the columns center lines (right/left and front/back), selects the reference that is parallel to the grid and then creates the dimension between them. The line for creating the dimension in the NewDimension is created as part of the script.

the IsParallel node shows that the grid and the selected reference are parallel, that is how the reference line was selected but when I make a dot product between their vectors it isn’t a round 0 (there is a e to the power of -8)

I am adding the revit file which is a simple plan with a few columns and a few grid lines, and adding the dynamo script.

I’ll appreciate any take on the issue!

Reference - Family Instance For Dynamo_Forum.dyn (98.7 KB)

Python node named ‘Get Center Reference And Matching Reference Direction’ requires these imports

clr.AddReference("RevitNodes")
import Revit

clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

I would check your references are parallel

Useful resource from Aussie BIM Guru here → Automated Revit dimensioning using Dynamo!

I get the column’s center line references in both directions. Below is the folowing code for it.

I just used the script to select two columns, find their parallel references and it created a dimension between them. Therefore, my assumption is that the issue is that the angle between the grid and the column isn’t exact to the dot despite the node “IsParallel” being true but would like to hear if anyone finds a different issue and a work around for it.

Also no issue with same script when selecting two parallel grid lines - it creates a dimension line between them.

Here is the code for getting the center references:

import clr


clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import Reference





# Unwrap the FamilyInstance (input from Dynamo)
fam = UnwrapElement(IN[0])

# Initialize lists to store the references and directions
references = []
directions = []

# Get the center reference for "Center (Left/Right)" and "Center (Front/Back)"
left_right_ref = fam.GetReferenceByName("Center (Left/Right)")
front_back_ref = fam.GetReferenceByName("Center (Front/Back)")

if left_right_ref and front_back_ref:
    # Get the transform matrix of the FamilyInstance
    transform = fam.GetTransform()
    
    # Left/Right direction (BasisX for Left/Right)
    left_right_direction = transform.BasisX  # BasisX for left/right axis
    
    # Front/Back direction (BasisY for Front/Back)
    front_back_direction = transform.BasisY  # BasisY for front/back axis

    # Append the references and their corresponding directions to the lists
    references.append(left_right_ref)
    references.append(front_back_ref)
    
    directions.append(left_right_direction.ToVector())
    directions.append(front_back_direction.ToVector())

# Output the references and directions as separate lists
OUT = (references, directions)

It is selecting the reference based on the returned bool of the IsParallel node.

Hi,

try to reduce the number of nodes if possible, here is a solution for only one column at input, based on intersection of grids

import clr
import sys
import System
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

import itertools

def get_grids_intersections():
    """find all intersection of grids"""
    data = []
    grids = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Grids).WhereElementIsNotElementType().ToElements()
    for gd_a, gd_b in itertools.combinations_with_replacement(grids, 2):
        resultArray = IntersectionResultArray()
        setComparisonResult, resultArray = gd_a.Curve.Intersect(gd_b.Curve, resultArray)
        if setComparisonResult == SetComparisonResult.Overlap:
            data.append([gd_a, gd_b, resultArray.get_Item(0).XYZPoint ])
    return data
    
def create_dimension(column, grid, offset=3):
    """create dimension beetween column Reference and the nearest grid"""
    loc_column = column.Location.Point
    grid_direction = grid.Curve.Direction.Normalize()
    # grid_ref = Reference.ParseFromStableRepresentation(doc, grid.UniqueId)
    # OR
    grid_ref = Reference(grid)
    #
    base_line = DB.Line.CreateUnbound(loc_column, grid_direction.CrossProduct(XYZ.BasisZ))
    # select the good reference plane
    for referenceType in [FamilyInstanceReferenceType.CenterLeftRight, FamilyInstanceReferenceType.CenterFrontBack]:
        for ref in column.GetReferences(referenceType):
            ref_array = ReferenceArray()
            ref_array.Append(ref)
            ref_array.Append(grid_ref)
            #
            dim = doc.Create.NewDimension(doc.ActiveView, base_line, ref_array)
            doc.Regenerate()
            if dim.Value > 0.01:
                # apply offset
                if abs(grid_direction.CrossProduct(XYZ.BasisX).GetLength()) < 0.1:
                    dim.Location.Move(XYZ.BasisX.Multiply(offset * -1))
                else:
                    dim.Location.Move(XYZ.BasisY.Multiply(offset))
                return dim
            else:
                # delete the bad dimension and continue
                doc.Delete(dim.Id)
    return None
    
column = UnwrapElement(IN[0])
data_interSect_grid = get_grids_intersections()
# get the nearest grid intersection and unpack the result
gridA, gridB, intersectPt = sorted(data_interSect_grid, key=lambda tupl_data : tupl_data[2].DistanceTo(column.Location.Point))[0]

TransactionManager.Instance.EnsureInTransaction(doc)
outdims = [create_dimension(column, g) for g in [gridA, gridB,]]
TransactionManager.Instance.TransactionTaskDone()

OUT = outdims
5 Likes

It took some time but I finally got to this.
Your script creates a reference based on the grid direction at the location point of the column? Did I understand it correctly?

For each column, we look for the nearest grid intersection, then we create dimensions in relation to the 2 grids that define this intersection (and the column).

1 Like

I’m adopting the grid intersection points distance to select the grids.

I still dont get why my script doesn’t work when I select get the Reference CenterLine that is parallel to the grid and create a dimension between them. I also adopted the line creation by what you did - using the line.CreateUnbound with direction of the cross between the Z axis and the grid vector.

So given that I use the same parallel references and the same line like you created, any idea why it wouldn’t work?

your script selects the wrong Reference

Hi,
I’m trying to create a dimension from the column’s bare lines to the centerline (I came up short in ootb).
So I tried with the API and Python, same problem.

I tried with the center of the column, it only partially works.

I don’t understand why it’s not working. I have the references (is the way to get them the right one?) in both cases.

Does this come from 2024?

The Python codes aren’t great (normal, I’m still feeling my way).

import sys
import clr
import math
import System
from System.Collections.Generic import List
clr.AddReference("RevitAPI")
import Autodesk 
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

def findgrid(te):
    stock=[]
    a=te.replace("-","*")
    b=a.replace("(","*")
    c=b.replace(")","*")
    d=c.split("*")
    for e in d:
        if "." in e or len(e)==0:
            ""
        else:
            stock.append(e)
    return stock

columns=UnwrapElement(IN[0])
v=UnwrapElement(IN[1])


lgrid=FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Grids).WhereElementIsNotElementType().ToElements()

def createdimcol(c,lgrid):
    gridHn=findgrid(c.LookupParameter("Marque d'emplacement du poteau").AsString())[0]
    gridVn=findgrid(c.LookupParameter("Marque d'emplacement du poteau").AsString())[1]
    gridH=Reference([i for i in lgrid if i.Name==gridHn][0])
    gridV=Reference([i for i in lgrid if i.Name==gridVn][0])
    opt=Options()
    opt.ComputeReferences = True
    geoe=c.get_Geometry(opt)
    sol=[g for g in geoe][0].GetInstanceGeometry()
    solid=[i for i in sol][1]
    print(solid.Volume)
    fah=[f for f in solid.Faces if f.FaceNormal.Y==1 or f.FaceNormal.Y==-1]
    fav=[f for f in solid.Faces if math.fabs(f.ComputeNormal(UV.Zero).X)==1]
    refarrayH=ReferenceArray()
    refarrayH.Append(gridH)
    refarrayH.Append(c.GetReferences(FamilyInstanceReferenceType.CenterFrontBack)[0])
    refarrayH.Append(fah[1].Reference)
    refarrayH.Append(fah[0].Reference)
    ploc=c.Location.Point
    liny=Line.CreateBound(XYZ((ploc.X)+1,ploc.Y,ploc.Z),XYZ((ploc.X)+1,(ploc.Y)+1,ploc.Z))
    dimensionv = doc.Create.NewDimension(v, liny, refarrayH)
    doc.Regenerate()
    return dimensionv,len(fah),gridH.ElementId,fah[0].Reference.ElementId,fah[1].Reference.ElementId

TransactionManager.Instance.EnsureInTransaction(doc)
dimsV = [createdimcol(c,lgrid) for c in columns]
TransactionManager.Instance.TransactionTaskDone()

OUT = dimsV

rep 30 mai N2.dyn (67.1 KB)


edit:
I get the value between the two faces (after conversion) but the dimension is not represented and the distance via the grid is anarchic (I really don’t understand anything…)
If I don’t put the reference of the center of the family, no dimension is represented but 1 value out of 2 is correct.
Sincerely,
christian.stan

faces references are sometimes FamilySymbol references, instance references are required for dimensions

create_columns_dimension

import clr
import sys
import System
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

import itertools

def get_grids_intersections():
    """find all intersection of grids"""
    data = []
    grids = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Grids).WhereElementIsNotElementType().ToElements()
    for gd_a, gd_b in itertools.combinations_with_replacement(grids, 2):
        resultArray = IntersectionResultArray()
        setComparisonResult, resultArray = gd_a.Curve.Intersect(gd_b.Curve, resultArray)
        if setComparisonResult == SetComparisonResult.Overlap:
            data.append([gd_a, gd_b, resultArray.get_Item(0).XYZPoint ])
    return data
    
def get_vertical_faces(c):
    """get vertical faces"""
    opt=Options()
    opt.ComputeReferences = True
    for geo in c.get_Geometry(opt):
        if isinstance(geo, GeometryInstance):
            for geoI in geo.GetInstanceGeometry():
                if isinstance(geoI, DB.Solid) and geoI.Volume > 0:
                    return [f for f in geoI.Faces if abs(f.FaceNormal.Z) < 0.01]
    return []
    
def fix_Instance_Reference(reference):
    """ fix dimension for dimension"""
    owner_elem = doc.GetElement(reference)
    print(f"{owner_elem=}")
    if isinstance(owner_elem, DB.FamilySymbol):
        curent_stableRef = reference.ConvertToStableRepresentation(doc)
        if ":INSTANCE:" in curent_stableRef:
            full_ref = reference
        else:
            full_stableRef = f"{column.UniqueId}:0:INSTANCE:{curent_stableRef}"
            full_ref = DB.Reference.ParseFromStableRepresentation(doc, full_stableRef)
        return full_ref
    else:
        return reference
    
def create_dimension(column, grid, offset=3):
    """create dimension beetween column Reference and the nearest grid"""
    loc_column = column.Location.Point
    grid_direction = grid.Curve.Direction.Normalize()
    # grid_ref = Reference.ParseFromStableRepresentation(doc, grid.UniqueId)
    # OR
    grid_ref = Reference(grid)
    #
    base_line = DB.Line.CreateUnbound(loc_column, grid_direction.CrossProduct(XYZ.BasisZ))
    #
    lst_parallal_Faces = [f for f in get_vertical_faces(column) if abs(grid_direction.DotProduct(f.FaceNormal)) < 0.01]
    # select the good reference plane
    for referenceType in [FamilyInstanceReferenceType.CenterLeftRight, FamilyInstanceReferenceType.CenterFrontBack]:
        for ref in column.GetReferences(referenceType):
            ref_array = ReferenceArray()
            ref_array.Append(fix_Instance_Reference(ref))
            ref_array.Append(fix_Instance_Reference(grid_ref))
            for f in lst_parallal_Faces:
                ref_array.Append(fix_Instance_Reference(f.Reference))
            #
            dim = doc.Create.NewDimension(doc.ActiveView, base_line, ref_array)
            doc.Regenerate()
            # check if dimension is correct
            if all(s.Value > 0.01 for s in dim.Segments):
                # apply offset
                if abs(grid_direction.CrossProduct(XYZ.BasisX).GetLength()) < 0.1:
                    dim.Location.Move(XYZ.BasisX.Multiply(offset * -1))
                else:
                    dim.Location.Move(XYZ.BasisY.Multiply(offset))
                return dim
            else:
                # delete the bad dimension and continue
                print("deleted")
                doc.Delete(dim.Id)
    return None
    
column = UnwrapElement(IN[0])
data_interSect_grid = get_grids_intersections()
# get the nearest grid intersection and unpack the result
gridA, gridB, intersectPt = sorted(data_interSect_grid, key=lambda tupl_data : tupl_data[2].DistanceTo(column.Location.Point))[0]

TransactionManager.Instance.EnsureInTransaction(doc)
outdims = [create_dimension(column, g) for g in [gridA, gridB,]]
TransactionManager.Instance.TransactionTaskDone()

OUT = outdims
1 Like

Hi, I cheated by playing with the family (fixing the names of the Top and Bottom references).
edit

    ref_array = ReferenceArray()
    ref_array.Append(c.GetReferences(FamilyInstanceReferenceType.Top)[0])
    ref_array.Append(c.GetReferences(FamilyInstanceReferenceType.Bottom)[0])
    ref_array.Append(gridH)
    ploc=c.Location.Point
    liny=Line.CreateBound(XYZ((ploc.X)+1,ploc.Y,ploc.Z),XYZ((ploc.X)+1,(ploc.Y)+1,ploc.Z))
    dimensionv = doc.Create.NewDimension(v, liny, ref_array)
    doc.Regenerate()
    return dimensionv,ref_array

I couldn’t manage the fix_Instance_Reference function with multiple columns and didn’t really understand it. I’ll look into it in more detail in July.
But thanks for your feedback.
Sincerely,
Christian.stan

1 Like

Thanks for the response c.poupin.
The reference line is selected based on its vector being parallel to the grid curve direction (matching vectors circled in red). Furthermore, I created a line in the direction of the selected reference line and it shows that it is parallel to the grid curve…

Are you sure that it is selecting the wrong reference?

Reference selection if parallel to grid curve:

Line created parallel to curve:

you can try to reverse the result, like this


and check your family

2 Likes

Shit it worked…
Still don’t understand how it filters the reference that is parallel to the grid line direction and works with the reference that doesn’t pass and shows that it is perpendicular…
Now I just have to figure that out…

I appreciate for the effort!

1 Like