Dimension ByReferences not working for edges & faces

Hi @Alban_de_Chasteigner

I’m using GeniusLoci (v2022.8.5) to create dimensions by references. The references are a list of list. It works well when all the elements are face references or when all the elements are edge references. However, when the list of list contains a mix of face and edge references, it doesn’t always create the dimension.

The code looks correct so not sure why this is happening.

#Alban de Chasteigner 2019
#twitter : @geniusloci_bim
#geniusloci.bim@gmail.com

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

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

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

def tolist(obj1):
    if hasattr(obj1,"__iter__"): return obj1
    else: return [obj1]

views = tolist(UnwrapElement(IN[0]))
lines = tolist(IN[1])
if any(isinstance(el, list) for el in IN[2]) : referenceList =  IN[2]
else : referenceList = [(IN[2])]
dimensionType = tolist(UnwrapElement(IN[3]))[0]
listRef,dimList = [], []

#Thanks to Gavin Crump for the list structure improvement
for references in referenceList :
	elementsRef = ReferenceArray()
	for reference in references :
		elementsRef.Append(reference)
	listRef.append(elementsRef)
	
TransactionManager.Instance.EnsureInTransaction(doc)
for view in views:
	dims = []
	for line,refer in zip(lines,listRef) :
		if doc.IsFamilyDocument == True :
			if IN[3]== None: 
				dims.append(doc.FamilyCreate.NewDimension(view, line.ToRevitType(), refer).ToDSType(True))
			else:
				dims.append(doc.FamilyCreate.NewDimension(view, line.ToRevitType(), refer, dimensionType).ToDSType(True))
		else:
			if IN[3]== None: 
				dims.append(doc.Create.NewDimension(view, line.ToRevitType(), refer).ToDSType(True))
			else:
				dims.append(doc.Create.NewDimension(view, line.ToRevitType(), refer, dimensionType).ToDSType(True))
	dimList.append(dims)
TransactionManager.Instance.TransactionTaskDone()

if isinstance(IN[0], list): OUT = dimList
else: OUT = dimList[0]

Any suggesions?

An alternative approach is to only use edges (and no faces). But the elements in question are structural framing and the Compound Edge References node only seems to return edges if coping has been applied. (I’m aware it was designed for wall, floors, etc and not structural framing).

Can you post the graph and a simple reproducible case? Hard to identify the root cause otherwise as the code looks right (as you noted), and there are a LOT of possible reasons these methods can fail partially or in their entirety.

@jacob.small so this is driving me insanely crazy. The surface/edge mix doesn’t seem to be the problem. The node works as expected. What I think is the issue is that setting the dimension type changes the dimension from linear to aligned in certain circumstances.

The node is using this method which should return a LINEAR dimension. But becuase of this known Revit bug, I am having to get edges instead of surfaces. In this scenario below, the edges happen to be diagonally opposite. With the default dimension style, I get a linear dimension as expected.

However, IN REVIT, if I swap this dimension to a different LINEAR dimension, it turns into am ALIGNED dimension. So the Dynamo node is just replicating Revit’s behaviour.

The problem therefore seems to be with Revit but I have no idea what is causing it. Switching an element’s type shouldn’t change its creation method.

Dim bug.dyn (41.0 KB)
Project1.rvt (1.6 MB)

Huh… this is a new one for me… and it does seem to be a Revit thing not necessarily a Dynamo one. I’ll dig and let you know what I find, and if I can’t find anything I’ll let you know so we can escalate to the next level.

Offhand it feels like the QA teams for Revit, Revit API, Dynamo for Revit and Dynamo are all gonna get a workout on this one.

This isn’t an error with either Dynamo or the Revit API.

Its a problem with your script - or to be specific, your dimension lines. Dimensions created via the API are a pain: you need to create dimensions relative to the family symbol geometry - not relative to the family instance geometry.

The reason why your dimension is behaving so weirdly is because the line you are providing is out-of-plane relative to the symbol geometry face (imagine you wanted to create a dimension in elevation but created a line in a plan view - that’s basically what you’re doing), so the API cant do anything with it and does a best fit from some arbitrary point on your line and cant workout whether you want an aligned or linear dimension, so it defaults to linear.

Solution - extract the symbol geometry to get your bearings (its easier to see what you’re working with). Create your dimensions lines relative to the symbol geometry.

6 Likes

One caveat: the above doesn’t apply to system types, just family types that have a symbol geometry and instance geometry.

2 Likes

Great catch Thomas!

1 Like

@Thomas_Mahon I don’t understand your response. Either I’ve not understood or the question wasn’t clear.

Agree that dimensions via API are a pain. To clarify, all the dimensions in the images were created with Dynamo. 99% of them work just fine. For some reason, this condition fails.

The dimension in question is to a floor (system family). If I get faces, the script is fine, presumably because the faces are parallel. It gets weired when I need to use edges - in this case it is the vertical (floor thickness) at points 1 and 2. As per the files I uploaded, the linear dimension can be created ONLY IF the default dimension type is used. Specifying another dimension type (even if a duplicate of the default type) causes it to change into an ALIGNED dimension. This makes no sense to me.

Dimensions are view specific and reference 3D elements so they are always going to be ‘out of plane’. The ‘line’ input is on the World XY plane and the dimensions are being created in the Level 0 floor plan view. So I can’t see an issue with the ‘line’ input.

If you were referring to the dimension ‘references’ being out of plane, the API method is creating a LINEAR dimension, so it shouldn’t matter. That is what a linear dimension is mean to accommodate. What am I misunderstanding?

The GeniusLoci node I’m using to get the edge references is as follows:

#Alban de Chasteigner 2019
#twitter : @geniusloci_bim
#geniusloci.bim@gmail.com

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

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)
from Revit.Elements import *

items = UnwrapElement(IN[0]) if isinstance(IN[0],list) else [UnwrapElement(IN[0])]

def split_half(a):
	half = len(a) >> 1
	#return a[:half], a[half:]
	return a[half:]

allHorizEdges,allHorizReferences,allVertEdges,allVertReferences=[],[],[],[]

opt = Options()
opt.ComputeReferences = True
#opt.IncludeNonVisibleObjects = True
#opt.View = doc.ActiveView
for item in items:
	for obj in item.get_Geometry(opt):
		if isinstance(obj, Solid):
			gline = obj
			horizEdges,horizReferences,vertEdges,vertReferences=[],[],[],[]
			for edges in gline.Edges:
				if round(edges.AsCurve().GetEndPoint(0).ToPoint().Z,10) != round(edges.AsCurve().GetEndPoint(1).ToPoint().Z,10):
					vertEdges.append(edges.AsCurve().ToProtoType())
					vertReferences.append(edges.Reference)
				else:
					horizEdges.append(edges.AsCurve().ToProtoType())
					horizReferences.append(edges.Reference)
			if isinstance(item, Autodesk.Revit.DB.CeilingAndFloor):
				allHorizEdges.append(split_half(horizEdges))
				allHorizReferences.append(split_half(horizReferences))
			else:
				allHorizEdges.append(horizEdges)
				allHorizReferences.append(horizReferences)
			allVertEdges.append(vertEdges)
			allVertReferences.append(vertReferences)
		
if isinstance(IN[0], list): OUT = filter(None, allHorizEdges),filter(None, allHorizReferences),filter(None, allVertEdges),filter(None, allVertReferences)
else: OUT = filter(None, allHorizEdges)[0],filter(None, allHorizReferences)[0],filter(None, allVertEdges)[0],filter(None, allVertReferences)[0]

Unless you are saying that becuase these references are vertical (Z axis), that is why they are out of plane. If so, is there a way to get a vertex’s reference and use that for dimensions?

To me it seems like the issue is with Revit, specifically when a dim type is subsituted, it defaults to

This is file I uploaded, which shows the issue.

I think that in this case the issue is that you’re trying to substitute the vertical face with a vertical line, which will not be as accurate in terms of location, referencing, or sequence due to the multiple conversions from Revit to Dynamo and back again.

Jumping straight to the API likely would resolve the issue, but that would entail writing up a not insignificant amount of code to do so.

Perhaps moving to a situation where to work with the horizontal lines on the edge faces would be more consistent? The faces themselves would certainly be best though.

Not sure if it would work, but at the recent Dynamo Office Hour I presented some Python which may allow you to directly access the edge faces (assuming they are all vertical, as typically is the case with floors like this) of the floor by circumventing the error, at a minimum it would give you a head start on working around the conversion issue. You can check out the recording here: The Stairry Story - Using Dynamo for stairs in Revit - #42 - Community Conversations - YouTube

1 Like

@jacob.small I think you’ve misunderstood. I’m using the references from (vertical) edges already. There is no reason this shouldn’t be possible.

I can’t use horizontal edges as the edges(in plan) will not always be perpeindular to the dimension line.

Families have symbol geometry and instance geometry. To dimension families you need to create dim lines relative to the symbol geometry which is fixed at file centre. So the view/plane you would use to create dim lines for families have nothing to do with the plane of the view you want to place dimensions in. If you notice inconsistent dim placement, e.g. if this is ignored, you might notice dim placement works successfully in some views and not others, its simply because the view plane happens to align with the side of the symbol geometry requiring dimensions.

Anyway, you don’t have this problem as you’re dim’ing a system family. The cause is a lot more simpler to diagnose: vertical edge references in a plan view will always be used as points internally; as you’ve only provided two edges (and therefore two point constraints) there is insufficient information for Revit to be able to determine what type of dimension to use, so I guess it defaults to one or the other using a best-fit interpretation.

You can test this theory: try manually picking vertical edges in a plan view, its not possible. Only points can be selected. With the points you pick, an aligned dimension is angled; you’re just striking lucky in the first instance where its essentially linear. The Revit API in 99% of cases doesn’t allow you to do anything more than what’s possible manually. Your solution then is to pick the outermost (horizontal) edges so Revit has sufficient information to create the dimension constraint.

1 Like

@Thomas_Mahon I don’t think this is the issue at all. If it were, it wouldn’t have created the linear dimension in the first place. To clarify, eveything is the same (reference, line, view), the only thing that is different is the dim type. Just check the files I uploaded. The issue is happening when the dimension type is being changed. This sounds like a bug.

Both linear and aligned can work with points so it does seem plausible that you can see both from the same inputs. In your script you have two dim styles, one linear and one aligned. Aligned seems to work, until you try moving it, then it switches to a dim that aligns with the direction between the points. Aligned dims can be off-axis whereas linear cant, so I guess the line input forces the aligned dim to be placed in the orientation you desire, but it must just be used for creation only then discarded, otherwise it wouldn’t flip orientation if moved or if the dim type is changed.

The only way you’re going to solve this is to provide the right constraints that limit interpretation, i.e. edge references that only allow one orientation for the dimension instead of two.

This is another great point. By providing one face you force a direction, which in turn ensures things are always aligned to the axis you are after.

Thinking further on this, incorporating a single grid line should also do the trick, and would also give you the benefit of tying the dimension string to a reference in the overall project.

@jacob.small So I believe the issue is due to edges vs vertex as the dim references. I don’t know why it is doing what it is doing but here is a summary of the issue:

  • I can’t use ‘the right constraints’ as @Thomas_Mahon suggested. The element could be any shape and finding two edges aligned isn’t possible.

  • Revit’s linear dimension is after multiple points (vertexs). I’m feeding in reference edges and maybe there is some inbuilt conversion happening to get the vertex which allows it to work sometimes.

  • The solution I believe is to reference vertices, not edges. Is this possible via the API? Using Typology > Vertex doesn’t work. It needs to be an Autodesk.Revit.DB.Reference.

  • If I manually create a linear dimension (using opposite vertices as the references) and snoop I can see the reference array

But looking into the references, I can’t see to which part of the element it is referencing - face, vertex, etc.

GeniusLoci has an Element Reference node. Below I’ve selected the manually created dimension so I could reuse the references to see if that would work.

Dynamo creates a linear dimension

But if you try to move the dimension once created, the alignment changes even though the references are the same. I don’t undertand this as both dimensions (manual and Dynamo) are using the same references.

Maybe it is due to how the node is getting the references from the element, but it looks OK.

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

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument

elements = UnwrapElement(IN[0]) if isinstance(IN[0],list) else [UnwrapElement(IN[0])]

ref=[]
for element in elements:
	if isinstance (element, Autodesk.Revit.DB.Dimension):
		ref.append(element.References)
	elif isinstance (element, Autodesk.Revit.DB.ReferencePlane):
		ref.append(element.GetReference())
	elif isinstance (element, Autodesk.Revit.DB.Grid) and element.Document.Title != doc.Title :
		ref.append(element.Curve.Reference)
	else : ref.append(Reference(element))

if isinstance(IN[0], list): OUT = ref
else: OUT = ref[0]

Is there a way to get/define the vertex references to be used? I’m thinking something similar to Clockwork’s RevitFaceReference.FromDynamo surface node:

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

faces = UnwrapElement(IN[0])
elementlist = list()
for face in faces:
	try:
		ref = face.Tags.LookupTag("RevitFaceReference")
		elementlist.append(ref)
	except:
		elementlist.append(list())
OUT = elementlist

I think that all you need is one edge/face reference which is perpendicular to the line you want to draw.

This reference could be say, the set of structural grids (or one thereof) which you are tying the dimensions to.

@jacob.small no this won’t. Edges have no direction (as we’ve discovered). And faces won’t work in say a trapezoidal shape.

I was thinking the edge reference perpendicular to the dimension line not a vertical one, which could be possible for many if not most designs.

A grid will certainly work in these cases - which is likely a requirement to have a functional dimension.

@jacob.small I can’t modify the design just becuase of a Revit/Dynamo limitation.

I need to create a LINEAR dimension picking up the maximum extends of an element. The element is an assembly view. There are no grid lines to dimension to. Depending on the shape, the extermity edges/faces will not be perpendicular to the dimension line. This is the whole reason for using a linear dimension, instead of an alignment dimension. Potentially a reference plane could be created and dimension to that but this is far from ideal.

What is needed is a dimension to vertices. Is this possible?