Get Rooms materials by Wall orientation

Hi everyone. Does anybody know how can I get the rooms materials from the walls according to the cardinal position (North, South, East and West)?
I need the position for a Rooms finish schedule, sorting them by North wall, south wall, etc.
I’m getting the rooms materials using this code in Python Node:

import sys
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager

rooms = UnwrapElement(IN[0])

doc = DocumentManager.Instance.CurrentDBDocument
calculator = SpatialElementGeometryCalculator(doc)

elementMaterial = list()
for room in rooms:
	results = calculator.CalculateSpatialElementGeometry(room)
	roomSolid = results.GetGeometry()
	
	roomMaterial = list()
	for face in roomSolid.Faces:
		for subface in results.GetBoundaryFaceInfo(face):
			if subface.SubfaceType == SubfaceType.Side:
				boundingElement = subface.GetBoundingElementFace()
				elementId = boundingElement.MaterialElementId
				materialName = doc.GetElement(elementId).Name
				roomMaterial.append(materialName)
	elementMaterial.append(roomMaterial)

OUT = elementMaterial

And the workspace (this is only for walls):

Hello @DavidMena
you can try to use FaceNormal Property, faces from roomSolid = results.GetGeometry()
Note from API doc
This property is the “face normal” vector, and thus should return a vector consistently pointing out of the solid that this face is a boundary for (if it is a part of a solid).

then compute (for each faceNormalVector) the dot product with the TrueNorth vector (and the TrueEast) to find the Wall orientation

I’ll help you if you get stuck

1 Like

Thank you @c.poupin, let me try and I’ll tell you how it worked !

Ok @c.poupin this is what I’ve achieved so far (Until I got stuck :frowning: ) :
First I filtered 16 wall faces from a list of 4 rooms with this code:

import sys
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager

rooms = UnwrapElement(IN[0])

doc = DocumentManager.Instance.CurrentDBDocument
calculator = SpatialElementGeometryCalculator(doc)

walls = list()
for room in rooms:
	results = calculator.CalculateSpatialElementGeometry(room)
	geometries = results.GetGeometry()
	
	faces = list()
	for face in geometries.Faces:
		for subFace in results.GetBoundaryFaceInfo(face):
			if subFace.SubfaceType == SubfaceType.Side:
				boundingElement = subFace.GetBoundingElementFace()
				walls.append(boundingElement)			

OUT = walls

Then obtained the FaceNormal for each PlanarFace with this code in another node:

import sys
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager

faces = IN[0]

normals = []
for face in faces:
	vectorNormal = face.FaceNormal
	normals.append(vectorNormal)

OUT = normals

This is my workspace:

But now I can’t find the way to compute these vectors to get the dot product with the True North and East as you told me.
Could you please help me with any hint about it?
Thank you!

Hello @DavidMena
the dot product of 2 normalized vectors varies between -1 and 1, as the vectors normal face point towards the outside of the solid it is thus possible to determine their orientations with North an East.
Some explanation with a graph

try this script

import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')

from Autodesk.Revit.DB import *
import Autodesk
from RevitServices.Persistence import DocumentManager

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


def getOrientation(vecTrueNorth, vecTrueEast, solidFace):
	faceNorm = solidFace.FaceNormal
	dotP_N = faceNorm.DotProduct(vecTrueNorth)
	dotP_E = faceNorm.DotProduct(vecTrueEast)
	#0.7 = Pi / 4 or (45°)
	if  dotP_N > 0.7 and abs(dotP_E) <= 0.7:
		return "North"
	elif dotP_N < -0.7 and abs(dotP_E) <= 0.7:
		return "South"
	elif dotP_E > 0.7 and abs(dotP_N) <= 0.7:
		return "East"		
	elif dotP_E < -0.7 and abs(dotP_N) <= 0.7:
		return "West"
	else:
		return None
		
rooms = UnwrapElement(IN[0])

doc = DocumentManager.Instance.CurrentDBDocument
currentLocation = doc.ActiveProjectLocation
origin = XYZ(0, 0, 0)
projectPosition = currentLocation.GetProjectPosition(origin)
angleToNorth = projectPosition.Angle
vecNorth = XYZ.BasisY 
vecEast = XYZ.BasisX 
rotTransf = Transform.CreateRotation(XYZ.BasisZ,  - angleToNorth)
vecTrueNorth = rotTransf.OfVector(vecNorth)
vecTrueEast = rotTransf.OfVector(vecEast)

calculator = SpatialElementGeometryCalculator(doc)

elementMaterial = list()

for room in rooms:
	if calculator.CanCalculateGeometry(room):
		results = calculator.CalculateSpatialElementGeometry(room)

		roomSolid = results.GetGeometry()
		roomMaterial = list()
		for face in roomSolid.Faces:
			for subface in results.GetBoundaryFaceInfo(face):
				if subface.SubfaceType == SubfaceType.Side:		
					boundingElement = subface.GetBoundingElementFace()
					elementId = boundingElement.MaterialElementId
					material = doc.GetElement(elementId)
					if material is not None :
						surfaceDs = face.ToProtoType()
						nameOrient = getOrientation(vecTrueNorth, vecTrueEast, face)
						materialName = material.Name
						roomMaterial.append([face, surfaceDs[0], materialName, nameOrient])
									
						
		elementMaterial.append([room, roomMaterial])
	
OUT =  elementMaterial
2 Likes

To be specific, the dot product varies between -1 and 1. If the angle between two vectors is less than 90° then this will result in a value between 0 and 1 (1 if they are exactly aligned), if they are perpendicular (exactly 90°), then this will be 0, and if the angle between vectors is greater than 90°, then the dot product will be negative, -1 if they are exactly oppositely aligned.

2 Likes

@c.poupin Thank you so much for your dedication and your time! It really worked! . It took me time to rewrite the code to understand it. Now I think I have what I need to work with this information.
Great and very very useful.
Thanks again!

Hello, @c.poupin Thanks for your python code, it’s quite useful! Now I’m trying to add some code to identify the wall on Northeast, Northwest, Southeast, Southwest orientations. But it didn’t work. Could you help me to chack the code, and any suggestion would be greatly appreciated.

import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')

from Autodesk.Revit.DB import *
import Autodesk
from RevitServices.Persistence import DocumentManager

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


def getOrientation(vecTrueNorth, vecTrueEast, solidFace):
	faceNorm = solidFace.FaceNormal
	dotP_N = faceNorm.DotProduct(vecTrueNorth)
	dotP_E = faceNorm.DotProduct(vecTrueEast)
	#0.7 = Pi / 4 or (45°)
	if  dotP_N > 0.7 and abs(dotP_E) < 0.7:
		return "North"
    elif  dotP_N = 0.7 and dotP_E = 0.7:
		return "Northeast"
	elif dotP_N < -0.7 and abs(dotP_E) < 0.7:
		return "South"
	elif  dotP_N = 0.7 and dotP_E = -0.7:
		return "Northwest"
	elif dotP_E > 0.7 and abs(dotP_N) < 0.7:
		return "East"		
	elif  dotP_N = -0.7 and dotP_E = 0.7:
		return "Southeast"
	elif dotP_E < -0.7 and abs(dotP_N) < 0.7:
		return "West"
	elif  dotP_N = -0.7 and dotP_E = -0.7:
		return "Southwest"
	else:
		return None
		
rooms = UnwrapElement(IN[0])

doc = DocumentManager.Instance.CurrentDBDocument
currentLocation = doc.ActiveProjectLocation
origin = XYZ(0, 0, 0)
projectPosition = currentLocation.GetProjectPosition(origin)
angleToNorth = projectPosition.Angle
vecNorth = XYZ.BasisY 
vecEast = XYZ.BasisX 
rotTransf = Transform.CreateRotation(XYZ.BasisZ,  - angleToNorth)
vecTrueNorth = rotTransf.OfVector(vecNorth)
vecTrueEast = rotTransf.OfVector(vecEast)

calculator = SpatialElementGeometryCalculator(doc)

elementMaterial = list()

for room in rooms:
	if calculator.CanCalculateGeometry(room):
		results = calculator.CalculateSpatialElementGeometry(room)

		roomSolid = results.GetGeometry()
		roomMaterial = list()
		for face in roomSolid.Faces:
			for subface in results.GetBoundaryFaceInfo(face):
				if subface.SubfaceType == SubfaceType.Side:		
					boundingElement = subface.GetBoundingElementFace()
					elementId = boundingElement.MaterialElementId
					material = doc.GetElement(elementId)
					if material is not None :
						surfaceDs = face.ToProtoType()
						nameOrient = getOrientation(vecTrueNorth, vecTrueEast, face)
						materialName = material.Name
						roomMaterial.append([face, surfaceDs[0], materialName, nameOrient])
									
						
		elementMaterial.append([room, roomMaterial])
	
OUT =  elementMaterial

Hi @yushiwKWX8C and welcome, use == instead of = in the function, double-check the indentation in the function as well, rewrite it correctly if you have to (use Tab every time you are on a new line, only one space between conditionals, variables, values…), it worked from my side.

2 Likes

Hi, @Elie.Trad Thanks for your reply and suggestion! I corrected some errors in the code and the node works now. But when I randomly draw a room with a 45° angle wall, it still doesn’t seem to recognize the Northeast or Northwest Wall in Revit. Do you have any idea about this?

import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')

from Autodesk.Revit.DB import *
import Autodesk
from RevitServices.Persistence import DocumentManager

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


def getOrientation(vecTrueNorth, vecTrueEast, solidFace):
	faceNorm = solidFace.FaceNormal
	dotP_N = faceNorm.DotProduct(vecTrueNorth)
	dotP_E = faceNorm.DotProduct(vecTrueEast)
	#0.7 = Pi / 4 or (45°)
	if  dotP_N > 0.7 and abs(dotP_E) < 0.7:
		return "North"
	elif dotP_N < -0.7 and abs(dotP_E) < 0.7:
		return "South"
	elif dotP_E > 0.7 and abs(dotP_N) < 0.7:
		return "East"		
	elif dotP_E < -0.7 and abs(dotP_N) < 0.7:
		return "West"				
	elif dotP_N == 0.7 and dotP_E == 0.7:
		return "Northeast"
	elif dotP_N == 0.7 and dotP_E == -0.7:
		return "Northwest"
	elif dotP_N == -0.7 and dotP_E == 0.7:
		return "Southeast"
	elif dotP_N == -0.7 and dotP_E == -0.7:
		return "Southwest"
	else:
		return None
		
rooms = UnwrapElement(IN[0])

doc = DocumentManager.Instance.CurrentDBDocument
currentLocation = doc.ActiveProjectLocation
origin = XYZ(0, 0, 0)
projectPosition = currentLocation.GetProjectPosition(origin)
angleToNorth = projectPosition.Angle
vecNorth = XYZ.BasisY 
vecEast = XYZ.BasisX 
rotTransf = Transform.CreateRotation(XYZ.BasisZ,  - angleToNorth)
vecTrueNorth = rotTransf.OfVector(vecNorth)
vecTrueEast = rotTransf.OfVector(vecEast)

calculator = SpatialElementGeometryCalculator(doc)

elementMaterial = list()

for room in rooms:
	if calculator.CanCalculateGeometry(room):
		results = calculator.CalculateSpatialElementGeometry(room)

		roomSolid = results.GetGeometry()
		roomMaterial = list()
		for face in roomSolid.Faces:
			for subface in results.GetBoundaryFaceInfo(face):
				if subface.SubfaceType == SubfaceType.Side:		
					boundingElement = subface.GetBoundingElementFace()
					elementId = boundingElement.MaterialElementId
					material = doc.GetElement(elementId)
					if material is not None :
						surfaceDs = face.ToProtoType()
						nameOrient = getOrientation(vecTrueNorth, vecTrueEast, face)
						materialName = material.Name
						roomMaterial.append([face, surfaceDs[0], materialName, nameOrient])
									
						
		elementMaterial.append([room, roomMaterial])
	
OUT =  elementMaterial

Hello
you need to round the values (DotProduct)

import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')

from Autodesk.Revit.DB import *
import Autodesk
from RevitServices.Persistence import DocumentManager

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


def getOrientation(vecTrueNorth, vecTrueEast, solidFace):
	faceNorm = solidFace.FaceNormal
	dotP_N = round(faceNorm.DotProduct(vecTrueNorth), 3)
	dotP_E = round(faceNorm.DotProduct(vecTrueEast), 3)
	#0.707 = Pi / 4 or (45°)
	if  dotP_N > 0.707 and abs(dotP_E) <= 0.707:
		return "North"
	elif dotP_N < -0.707 and abs(dotP_E) <= 0.707:
		return "South"
	elif dotP_E > 0.707 and abs(dotP_N) <= 0.707:
		return "East"		
	elif dotP_E < -0.707 and abs(dotP_N) <= 0.707:
		return "West"
	elif dotP_N == 0.707 and dotP_E == 0.707:
		return "Northeast"
	elif dotP_N == 0.707 and dotP_E == -0.707:
		return "Northwest"
	elif dotP_N == -0.707 and dotP_E == 0.707:
		return "Southeast"
	elif dotP_N == -0.707 and dotP_E == -0.707:
		return "Southwest"
	else:
		return None
		
rooms = UnwrapElement(IN[0])

doc = DocumentManager.Instance.CurrentDBDocument
currentLocation = doc.ActiveProjectLocation
origin = XYZ(0, 0, 0)
projectPosition = currentLocation.GetProjectPosition(origin)
angleToNorth = projectPosition.Angle
vecNorth = XYZ.BasisY 
vecEast = XYZ.BasisX 
rotTransf = Transform.CreateRotation(XYZ.BasisZ,  - angleToNorth)
vecTrueNorth = rotTransf.OfVector(vecNorth)
vecTrueEast = rotTransf.OfVector(vecEast)

calculator = SpatialElementGeometryCalculator(doc)

elementMaterial = list()

for room in rooms:
	if calculator.CanCalculateGeometry(room):
		results = calculator.CalculateSpatialElementGeometry(room)

		roomSolid = results.GetGeometry()
		roomMaterial = list()
		for face in roomSolid.Faces:
			for subface in results.GetBoundaryFaceInfo(face):
				if subface.SubfaceType == SubfaceType.Side:		
					boundingElement = subface.GetBoundingElementFace()
					elementId = boundingElement.MaterialElementId
					material = doc.GetElement(elementId)
					if material is not None :
						surfaceDs = face.ToProtoType()
						nameOrient = getOrientation(vecTrueNorth, vecTrueEast, face)
						materialName = material.Name
						roomMaterial.append([face, surfaceDs[0], materialName, nameOrient])
									
						
		elementMaterial.append([room, roomMaterial])
	
OUT =  elementMaterial
3 Likes

That’s awesome! It works now. Thank you @c.poupin and @Elie.Trad. Really appreciated your help!

2 Likes