Auto-Dimension from Walls

After installing Orchid, I seem to have found a replacement/equivalent for all of them except for one: Wall.GetWallType :confounded:

So close, I hope it’s worth the effort :stuck_out_tongue:
Chynamo & Synthesize both have that one.

Already getting some errors…

I’m afraid that Dynamo is developing so quickly that any old graph will likely need to be reworked.

Hopefully I’ll get the Python approach fully resolved and you can use that… Otherwise, if you work through the error messages you’re getting you can likely work it out…

If you read through the code above you’ll see where faces are extracted from the wall, you could OUT the list ‘frontFaceEdges’ I think?

It’s not going to be easy I’m afraid.

Hi @Mark.Ackerley
Im trying to open it in Dynamo2.0.2 Revit 2018.3
Crashes whenever I open it. :face_with_raised_eyebrow:

@Laura_POLO whats your revit version?

I’m using the latest Revit 2019

When I started up this morning, the dynamo just keeps running, doesn’t stop…

before topology.faces Use element.geometry :wink:

@Mark.Ackerley
This is how far I’ve gotten…
I really want it to work, everyone at the office is getting excited!

Hi Laura,

ok… Right… So in the intervening year, I’ve messed around with various versions when I’ve had time & the inclination.

Who knew it would be so hard?!

Obviously I’m not feeling that there is a perfect version yet, but it does depend quite what you’re after.

As noted above, I went down a different route due to the shear number of pointy buildings I was getting… If it only works for rectangles it seems like a dead end.

However, it that’s good enough for you, here’s a version which works on my PC this morning… Dynamo 2.03 & Revit 2019.2 You will note that it isn’t perfect, as noted above, we need to include references from intersecting walls… But we’ll get there! :smiley:

Happy for anyone to chip in and give assistance.

Cheers,

Mark

Dim Walls In Active Plan View.dyn (241.1 KB)

Hi @Mark.Ackerley Thanks for sharing updated version. :slightly_smiling_face::slightly_smiling_face:

I have installed all packages which you had mentioned.

Dimension.getreferenceElements and elements.delete not working:
“Warning: Node of type ‘Unresolved’ (C:\Program Files\Dynamo\Dynamo Core\2\DynamoCore.dll) cannot be resolved” same error came for both nodes.

Hey,

So the pink group… if you want to manually select some walls, rather than get every single wall in the view, you can use that… the graph will run fine if nothing is selected and the ‘auto’ boolean is set to true.

Dimension.GetReferenceElements is in Rhythm. Elements.Delete is in Archi-lab, if you already have those packages just search again in your browser and you should be able to re-find them.

So, I’m nearly there with a Python solution…

Just one iritation left…

I don’t want this reference…
image

So, to recap… I’m using the edges which intersect the front face… this allows me to get the point. Unfortunately the front face recognises this edge. So I want to find a way of filtering it out, but I can’t think of anything unique about it. It is a front face edge bounding a non-front face intersecting wall face? Perhaps? @Alban_de_Chasteigner you’re good at references, would you mind helping? :smiley:

Thanks,

Mark

image

 #thanks for all the help everyone
import clr

# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *
import System

clr.AddReference("ProtoGeometry")
from Autodesk.DesignScript import Geometry as geom

# Import Element wrapper extension methods
clr.AddReference("RevitNodes")
import Revit
from Autodesk.Revit.DB import *

# Import ToProtoType, ToRevitType geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)

clr.AddReference("RevitAPIUI")
from Autodesk.Revit.UI.Selection import ObjectType

clr.AddReference("ProtoGeometry")
from Autodesk.DesignScript import Geometry as geom

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument


def isParallel(v1, v2):
    #it needs two vectors
    return v1.CrossProduct(v2).IsAlmostEqualTo(XYZ(0, 0, 0))

def isAlmostEqualTo(v1, v2):
    return v1.IsAlmostEqualTo(v2)

def isPerpendicular(v1, v2):
    if v1.DotProduct(v2)== 0:
        return True
    else:
        return False

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

def CurveToVector(crv):
	vec = geom.Vector.ByTwoPoints(crv.StartPoint,crv.EndPoint)
	return vec

#set the wall orientation, depending on whether exterior or interior is selected
def wallNormal(wall, extOrint):
    if extOrint:
        wallNormal = wall.Orientation.ToVector()
    else:
        wallNormal = wall.Orientation.Negate().ToVector()
    return wallNormal

#this takes the wall location line, shifts it up the to the view cut plane
#and moves it out to the external edge of the wall
def locToCutCrv(wall, wallNormal):
	wallLn = wall.Location.Curve
	wallCrv = wallLn.ToProtoType()
	zOf = doc.ActiveView.GenLevel.Elevation-wallCrv.StartPoint.Z
	doc.ActiveView.GetViewRange().GetOffset(PlanViewPlane.CutPlane) #the CutPlane enumeration is like a property of PBP?    	
	wallCrv = geom.Geometry.Translate(wallCrv, geom.Vector.ByCoordinates(0,0, zOf+1000))
	wallwidthMM = UnitUtils.ConvertFromInternalUnits(wall.Width, DisplayUnitType.DUT_MILLIMETERS)
	wallCrv = geom.Geometry.Translate(wallCrv, wallNormal, (wallwidthMM/2))	

	#get wall curve info
	wallvec = CurveToVector(wallCrv)
	wallorig = geom.Curve.PointAtParameter(wallCrv,0.5)
	walldir1 = geom.Vector.ByTwoPoints(wallorig, wallCrv.StartPoint)
	walldir2 = geom.Vector.ByTwoPoints(wallorig, wallCrv.EndPoint)

	#move points
	ptMvSt = geom.Geometry.Translate(wallCrv.StartPoint, walldir1, 500)
	ptMvEnd = geom.Geometry.Translate(wallCrv.EndPoint, walldir2, 500)

	#create new line based on extended points
	#this is not a model line! that requires another method
	lineAtExternalEdgeAtCutPlaneHeight = geom.Line.ByStartPointEndPoint(ptMvSt, ptMvEnd).ToRevitType()
	return lineAtExternalEdgeAtCutPlaneHeight

offDist = IN[0]
extOrInt = IN[1]

#User Input
ref = uidoc.Selection.PickObject(ObjectType.Element, 'Select A Wall')
#define Targe Wall
targetWall = doc.GetElement(ref)


#let's go get the walls for finding references
intersectedWalls = []
#start by adding our target wall
intersectedWalls.append(targetWall)
#then get all the other walls in the view and test if they intersect the Target Wall
collectedWalls = FilteredElementCollector(doc, doc.ActiveView.Id).OfClass(Wall).ToElements()
for collectedWall in collectedWalls:
    if targetWall.Location.Curve.Intersect(collectedWall.Location.Curve) == SetComparisonResult.Overlap:
        intersectedWalls.append(collectedWall)

#Target Wall external line for intersect check
exLi = locToCutCrv(targetWall, wallNormal(targetWall, extOrInt))

#Curve where the dimension will be located
offCrv = geom.Geometry.Translate(exLi.ToProtoType(), wallNormal(targetWall, extOrInt), (offDist))

#lets get the wall edges we want
#only get edges intersecting target side? no this is misleading... we want any reference hitting our external wall
#the problem is actually that the face itsetlf is registering the intersections we don't want e.g. the wrapping ends
vertEdges = []
vertEdgesLoc = []
for wallInt in intersectedWalls:
    opts = Options()
    #without compute references, none of this works
    opts.ComputeReferences = True
    opts.IncludeNonVisibleObjects = True
    opts.View = doc.ActiveView
    for obj in wallInt.get_Geometry(opts): 
        #walls also contain line geometry    
        if isinstance(obj, Solid):           
            for edge in obj.Edges:
                #get edges which intersect
                edgeC = edge.AsCurve()
                edgeCNorm = edgeC.Direction.Normalize()
                #if front face edge and edge intersects line and edge is vertical up or vertical down add to list
                if edgeC.Intersect(exLi) != SetComparisonResult.Disjoint and (edgeCNorm.IsAlmostEqualTo(XYZ(0,0,1)) or edgeCNorm.IsAlmostEqualTo(XYZ(0,0,-1))):                    
                    vertEdges.append(edge)					
                    #if we wanted to use this on sections we'd want to use z value
                    #we will get the location to use as sorting values
                    vertEdgesLoc.append(edgeC.GetEndPoint(0).X + edgeC.GetEndPoint(0).Y)

#sort the edges using the combined XY location value
vertEdgeSorted = []
vertEdgesLocSorted = []
vertEdgesSorted = [x for _,x in sorted(zip(vertEdgesLoc,vertEdges))]
vertEdgesLocSorted = sorted(vertEdgesLoc)

#only add uniquely located references
#this is awkward because we test 1 list, then add to the other list
#so we need the Temp list, so we know what should be added to the Sub
vertEdgeUniTemp = []
vertEdgeUniLocTemp = []
vertEdgeSub = ReferenceArray()
for eL, e in zip(vertEdgesLocSorted, vertEdgesSorted):
    if eL not in vertEdgeUniLocTemp:
        vertEdgeUniLocTemp.append(eL)
        vertEdgeSub.Append(e.Reference)
                            
#we want to pair up the references to create unique dims for the brick dim checker
#convoluted code, ref arrays seem their own beast!
outRefs = []
#define the overall list length
for i in range(vertEdgeSub.Size-1):
    #create the array here so the list nesting is correct
    vertEdgeAr = ReferenceArray()
    #define the sub list length
    while vertEdgeAr.Size < 2:
        #only get add 2 indices for each sub list
        vertEdgeAr.Append(vertEdgeSub[i])
        vertEdgeAr.Append(vertEdgeSub[i+1])
    outRefs.append(vertEdgeAr)

#start transaction
TransactionManager.Instance.EnsureInTransaction(doc)
#create dimensions for each pair of referenes
for ref in outRefs:    
    OUT = doc.Create.NewDimension(doc.ActiveView, offCrv.ToRevitType(), ref), outRefs, 
#finish transaction    
TransactionManager.Instance.TransactionTaskDone()
7 Likes

Wow, looks amazing! I’m going to give it a go later this week!

Thanks Laura,

Final version… :slight_smile:

1 Click Dimension Wall.dyn (16.9 KB)

#thanks for all the help everyone
import clr

# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *
import System

clr.AddReference("ProtoGeometry")
from Autodesk.DesignScript import Geometry as geom

# Import Element wrapper extension methods
clr.AddReference("RevitNodes")
import Revit
from Autodesk.Revit.DB import *

# Import ToProtoType, ToRevitType geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)

clr.AddReference("RevitAPIUI")
from Autodesk.Revit.UI.Selection import ObjectType

clr.AddReference("ProtoGeometry")
from Autodesk.DesignScript import Geometry as geom

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument


def isParallel(v1, v2):
    #it needs two vectors
    return v1.CrossProduct(v2).IsAlmostEqualTo(XYZ(0, 0, 0))

def isAlmostEqualTo(v1, v2):
    return v1.IsAlmostEqualTo(v2)

def isPerpendicular(v1, v2):
    if v1.DotProduct(v2)== 0:
        return True
    else:
        return False


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

def CurveToVector(crv):
	vec = geom.Vector.ByTwoPoints(crv.StartPoint,crv.EndPoint)
	return vec

#set the wall orientation, depending on whether exterior or interior is selected
def wallNormal(wall, extOrint):
    if extOrint:
        wallNormal = wall.Orientation.ToVector()
    else:
        wallNormal = wall.Orientation.Negate().ToVector()
    return wallNormal

#this takes the wall location line, shifts it up the to the view cut plane
#and moves it out to the external edge of the wall
def locToCutCrv(wall, wallNormal, lineEndExtend):
	wallLn = wall.Location.Curve
	wallCrv = wallLn.ToProtoType()
	#take the level height, subtract the height of the wall base
	zOf = doc.ActiveView.GenLevel.Elevation-wallCrv.StartPoint.Z
	#get the cut plane offset of the view
	cPlaneH = doc.ActiveView.GetViewRange().GetOffset(PlanViewPlane.CutPlane) #the CutPlane enumeration is like a property of PBP?
	cPlaneHiMM = UnitUtils.ConvertFromInternalUnits(cPlaneH, DisplayUnitType.DUT_MILLIMETERS)	
	wallCrv = geom.Geometry.Translate(wallCrv, geom.Vector.ByCoordinates(0,0, zOf+cPlaneHiMM))
	wallwidthMM = UnitUtils.ConvertFromInternalUnits(wall.Width, DisplayUnitType.DUT_MILLIMETERS)
	wallCrv = geom.Geometry.Translate(wallCrv, wallNormal, (wallwidthMM/2))	

	#get wall curve info
	wallvec = CurveToVector(wallCrv)
	wallorig = geom.Curve.PointAtParameter(wallCrv,0.5)
	walldir1 = geom.Vector.ByTwoPoints(wallorig, wallCrv.StartPoint)
	walldir2 = geom.Vector.ByTwoPoints(wallorig, wallCrv.EndPoint)

	#move points
	ptMvSt = geom.Geometry.Translate(wallCrv.StartPoint, walldir1, lineEndExtend)
	ptMvEnd = geom.Geometry.Translate(wallCrv.EndPoint, walldir2, lineEndExtend)

	#create new line based on extended points
	#this is not a model line! that requires another method
	lineAtExternalEdgeAtCutPlaneHeight = geom.Line.ByStartPointEndPoint(ptMvSt, ptMvEnd).ToRevitType()
	return lineAtExternalEdgeAtCutPlaneHeight

offDist = IN[0]
extOrInt = IN[1]

#if the wall is exterior we need to extend the intersect line
#beyond the exterior face to pick up the intersecting walls
#if the wall is interior, we don't want to extend as far
if extOrInt:
    intersectLineEndExtend = 500
else:
    intersectLineEndExtend = 0

#User Input
ref = uidoc.Selection.PickObject(ObjectType.Element, 'Select A Wall')
#define Targe Wall
targetWall = doc.GetElement(ref)


#let's go get the walls for finding references
intersectedWalls = []
#start by adding our target wall
intersectedWalls.append(targetWall)
#then get all the other walls in the view and test if they intersect the Target Wall
collectedWalls = FilteredElementCollector(doc, doc.ActiveView.Id).OfClass(Wall).ToElements()
for collectedWall in collectedWalls:
    if targetWall.Location.Curve.Intersect(collectedWall.Location.Curve) == SetComparisonResult.Overlap:
        intersectedWalls.append(collectedWall)

#Target Wall external line for intersect check
exLi = locToCutCrv(targetWall, wallNormal(targetWall, extOrInt), intersectLineEndExtend)

#Curve where the dimension will be located
offCrv = geom.Geometry.Translate(exLi.ToProtoType(), wallNormal(targetWall, extOrInt), (offDist))

#lets get the wall edges we want
#only get edges intersecting target side? no this is misleading... we want any reference hitting our external wall
#the problem is actually that the face itsetlf is registering the intersections we don't want e.g. the wrapping ends
frontFaceIW = []
vertEdges = []

opts = Options()
#without compute references, none of this works
opts.ComputeReferences = True
opts.IncludeNonVisibleObjects = True
opts.View = doc.ActiveView
    
for wallInt in intersectedWalls:
    for obj in wallInt.get_Geometry(opts):
        #walls also contain line geometry    
        if isinstance(obj, Solid): 
            for face in obj.Faces:
                #if face is normal is equal to wall normal it is the external face
                if isAlmostEqualTo(wallInt.Orientation, face.ComputeNormal(UV(0.5,0.5))):
                    frontFaceIW.append(face)
                                   
            for edge in obj.Edges:
                #get edges which intersect
                edgeC = edge.AsCurve()
                edgeCNorm = edgeC.Direction.Normalize()
                #if front face edge and edge intersects line and edge is vertical up or vertical down add to list
                if edgeC.Intersect(exLi) != SetComparisonResult.Disjoint and (edgeCNorm.IsAlmostEqualTo(XYZ(0,0,1)) or edgeCNorm.IsAlmostEqualTo(XYZ(0,0,-1))):                    
                    vertEdges.append(edge)					

#so we use the X+Y values as a unique identifier of location (we're less interested in
#the actual unique reference, there may be 2 in the same place)
#we will use these as filtering and sorting values
#if we wanted to use this on sections we'd want to use z value?
vertEdgesLoc = []
for v in vertEdges:
    vLoc = v.AsCurve().GetEndPoint(0).X + v.AsCurve().GetEndPoint(0).Y
    #getting some revit rounding errors, 7dp should be enough!
    vertEdgesLoc.append(round(vLoc,7))


#trying to remove stray intersect edges from adjoining walls
#to identify them, they are not on an intersecting wall front face
#their faces are not both on the target wall

#so we need all the target wall faces
for obj in targetWall.get_Geometry(opts):
    #walls also contain line geometry    
    if isinstance(obj, Solid): 
        faceTW = obj.Faces

#create a holding list containing everything in vertEdges
strayEdges = []        
for v in vertEdges:
    strayEdges.append(v)

#start removing things from holding list to leave only the stray ones
for faIW in frontFaceIW:
    #if edge face 0&1 are both target wall faces we don't want to remove them
    i = 0
    length = len(strayEdges)
    while (i < length):
        if strayEdges[i].GetFace(0) in faceTW:
            strayEdges.Remove(strayEdges[i])
            length = length - 1
        elif strayEdges[i].GetFace(1) in faceTW:
            strayEdges.Remove(strayEdges[i])
            length = length - 1            
        #if our wall is external.... #if edge face is an intersecting wall's front face, we don't want it                
        elif strayEdges[i].GetFace(0) == faIW and extOrInt == True:
            strayEdges.Remove(strayEdges[i])            
            length = length - 1 
        elif strayEdges[i].GetFace(1) == faIW and extOrInt == True:
            strayEdges.Remove(strayEdges[i])
            length = length - 1 
#            strayEdges.Remove(ed)
        #or if the edge reference is to a non-wall
            continue
        i = i+1

#if the wall is exterior, we want to remove references to internal wall edge
if extOrInt == True:
    i=0
    length = len(vertEdgesLoc)
    strayCLoc2 = []
    while (i < length):
        for stray in strayEdges:
            stLoc = stray.AsCurve().GetEndPoint(0).X + stray.AsCurve().GetEndPoint(0).Y
            #getting eroneous values, Revit accuracy not good enough? round is built in method
            if round(vertEdgesLoc[i],7) == round(stLoc,7):
                vertEdges.Remove(vertEdges[i])
                vertEdgesLoc.Remove(vertEdgesLoc[i])
                length = length - 1    
                continue
        i = i+1

#sort the edges using the combined XY location value
vertEdgesSorted = [x for _,x in sorted(zip(vertEdgesLoc,vertEdges))]
vertEdgesLocSorted = sorted(vertEdgesLoc)

#only add uniquely located references
#this is awkward because we test 1 list, then add to the other list
#we need the Temp list, so we know what should be added to the Sub list
vertEdgeUniLocTemp = []
vertEdgeSub = ReferenceArray()
for eL, e in zip(vertEdgesLocSorted, vertEdgesSorted):
    if eL not in vertEdgeUniLocTemp:
        vertEdgeUniLocTemp.append(eL)
        vertEdgeSub.Append(e.Reference)


#we want to pair up the references to create unique dims for the brick dim checker
#convoluted code, ref arrays seem their own beast!
outRefs = []
#define the overall list length
for i in range(vertEdgeSub.Size-1):
    #create the array here so the list nesting is correct
    vertEdgeAr = ReferenceArray()
    #define the sub list length
    while vertEdgeAr.Size < 2:
        #only get add 2 indices for each sub list
        vertEdgeAr.Append(vertEdgeSub[i])
        vertEdgeAr.Append(vertEdgeSub[i+1])
    outRefs.append(vertEdgeAr)

#OUT = len(strayEdges)
#start transaction
TransactionManager.Instance.EnsureInTransaction(doc)
#create dimensions for each pair of referenes
for ref in outRefs:    
    dim = doc.Create.NewDimension(doc.ActiveView, offCrv.ToRevitType(), ref), outRefs, 
#finish transaction    
TransactionManager.Instance.TransactionTaskDone()
5 Likes

Can this run with 2017.2 and dynamo 1.3?

Hey,

Umm… probably, try just copy and pasting the code? I doubt any recent the API changes would affect it…

Cheers,

Mark

@Mark.Ackerley It works great!
One side note: for interior dimensions I don’t want doors. Would it be possible to have a (toggle)option to have dimensions only for walls?

Hi Laura,

I’ll add it to the list :slight_smile: With this, nothing is quite as simple as it seems :slight_smile:

I suspect that you could make a graph using nodes to get the wall end point and intersect references much more easily than using python…

Cheers,

Mark