Points spread over face different results with list or dictionary

I all,

i’m testing some python code in Dynamo and i’m receiving different result depending on wheter i use my dictonaries or not. whichs i to link the face to there walls and also the uvs to the walls to that the faces.

any ideas on what happens differently here?


list result in revit:

dictonarie result in revit:


In green the points generated by the dinctonarie script.
It seems like there are rows and coloms of points missing…

code with list:

# Load the Python Standard and DesignScript Libraries
import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

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

items = UnwrapElement(IN[0])
if not hasattr(items, '__iter__'):
	items = [items]

Faces = []

for item in items:
        extSide = HostObjectUtils.GetSideFaces(item,ShellLayerType.Exterior)
        for e in extSide:
                extFace = item.GetGeometryObjectFromReference(e)
                Faces.append(extFace)
        intSide = HostObjectUtils.GetSideFaces(item,ShellLayerType.Interior)
        for i in intSide:
                intFace = item.GetGeometryObjectFromReference(i)
                Faces.append(intFace)

Points = []
directions = []

for face in Faces:
    bboxUV = face.GetBoundingBox()
    u_interval = (bboxUV.Max.U - bboxUV.Min.U) / 9
    v_interval = (bboxUV.Max.V - bboxUV.Min.V) / 9

    for i in range(10):
        u = bboxUV.Min.U + i * u_interval
        for j in range(10):
            v = bboxUV.Min.V + j * v_interval
            uv = UV(u, v)
            
            if face.IsInside(uv):
                Points.append(Point.Create(face.Evaluate(uv)).ToProtoType())
                directions.append(face.ComputeNormal(uv).ToVector())
                
            else:
                # If the UV point is outside the face, adjust it
                for d in range(1, 10):
                    # Try moving the point towards the center of the face
                    adjusted_uv = UV(u - d * u_interval / 10, v - d * v_interval / 10)
                    if face.IsInside(adjusted_uv):
                        Points.append(Point.Create(face.Evaluate(adjusted_uv)).ToProtoType())
                        directions.append(face.ComputeNormal(adjusted_uv).ToVector())
                        break
            
            
# Output
OUT = Points, directions

code wit dicontaries:

# Load the Python Standard and DesignScript Libraries
import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

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

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

clr.AddReference("System")
import System
from System.Collections.Generic import List

# collect walls in model
walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements()

faces = []
valid_walls = {}

# Iterate through all walls to find intersections
opt = Options()
opt.ComputeReferences = True
opt.IncludeNonVisibleObjects = True

for wall in walls:
    wall_faces = []
    
    # get interiorface
    extside = HostObjectUtils.GetSideFaces(wall,ShellLayerType.Exterior)
    for e in extside:
        extface = wall.GetGeometryObjectFromReference(e)
        if extface == None:
            continue
        else:
            wall_faces.append(extface)
        
    # get exteriorface
    intside = HostObjectUtils.GetSideFaces(wall,ShellLayerType.Interior)
    for i in intside:
        intface = wall.GetGeometryObjectFromReference(i)
        if intface == None:
            continue
        else:
            wall_faces.append(intface)
           
    # Add wall_faces to general faces list
    if len(wall_faces) > 0:
        valid_walls[wall] = wall_faces

# create points grid for each face
# create dictionary to store checkpoints in
wall_uvs = {}
points = []
# determin precision of the check
precision = 10
for wall , faces in valid_walls.items():
    # create entry in disctionary for the wall
    wall_uvs[wall] = []
    for face in faces:
        # create list to store all points of each face in
        face_uvs = []
        # get min & max UV for each face and determin the interval based on the presicion
        bboxUV = face.GetBoundingBox()
        u_interval = (bboxUV.Max.U - bboxUV.Min.U) / precision-1
        v_interval = (bboxUV.Max.V - bboxUV.Min.V) / precision-1
        # create pointgrid for the face
        for i in range(precision):
            u = bboxUV.Min.U + i * u_interval
            for j in range(precision):
                v = bboxUV.Min.V + j * v_interval
                uv = UV(u, v)
                # check if points are on the face
                if face.IsInside(uv):
                    face_uvs.append(Point.Create(face.Evaluate(uv)).ToProtoType())
                else:
                    # if the UV-point is outside the face, adjust it
                    for d in range (1, precision):
                        # Try moving the point towards the center of the face
                        adjusted_uv = UV(u - d * u_interval / precision, v - d * v_interval / precision)
                        if face.IsInside(adjusted_uv):
                            face_uvs.append(Point.Create(face.Evaluate(uv)).ToProtoType())
                            break
        # add points of face to dict entry of the wall
        points.append(face_uvs)
        wall_uvs[wall].append(face_uvs)
 


# Output
OUT = points

I think you need parentheses (precision -1)

1 Like

@SeanP Thx for finding my oversight!! it solved the problem :slight_smile:

1 Like

Hi,

another solution with numpy linspace()


# Load the Python Standard and DesignScript Libraries
import sys
import clr
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

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

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

clr.AddReference('Python.Included')
import Python.Included as pyInc
path_py3_lib = pyInc.Installer.EmbeddedPythonHome
sys.path.append(path_py3_lib + r'\Lib\site-packages')

import numpy as np

wall = UnwrapElement(IN[0])
precision = 10
face_uvs = []


extside = HostObjectUtils.GetSideFaces(wall,ShellLayerType.Exterior)
face = wall.GetGeometryObjectFromReference(extside[0])

bboxUV = face.GetBoundingBox()

for u in np.linspace(bboxUV.Min.U, bboxUV.Max.U, precision):
    for v in np.linspace(bboxUV.Min.V, bboxUV.Max.V, precision):
        uv = UV(u, v)
        face_uvs.append(face.Evaluate(uv).ToPoint())
        
OUT = face_uvs

Note
without dependance of numpy, the linspace method can be replaced by a python generator

4 Likes

hi Cyril,

very intresting solution!

I don’t want to burden you to much with my next question, but ill have a go :slight_smile: .
I never imported solution outside of the pyrevit module into revit.
I tried it once by making a script in cpython and using the cpython engine instead of ironpython.

How exaclty does this importing works to get numpy to work, the code isn’t 100% clear to me:

clr.AddReference('Python.Included')
import Python.Included as pyInc
path_py3_lib = pyInc.Installer.EmbeddedPythonHome
sys.path.append(path_py3_lib + r'\Lib\site-packages')

u add the reference Python.Included, what does this module contain or do?

Thx for the extra info and your insight i’ll definitly experiment with this code.

Python.Included is a framework for Python.Net, here I use it to have the path of the python packages installed via pip.

here I use it to have the path of the python packages installed via pip. (for PyRevit I don’t know)

an example with pure Python generator

# Load the Python Standard and DesignScript Libraries
import sys
import clr
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

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

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


def py_linspace(start, stop, num=50, endpoint=True):
    num = int(num)
    start = start * 1.
    stop = stop * 1.

    if num == 1:
        yield stop
        return
    if endpoint:
        step = (stop - start) / float(num - 1)
    else:
        step = (stop - start) / float(num)

    for i in range(num):
        yield start + step * i

wall = UnwrapElement(IN[0])
precision = 10
face_uvs = []


extside = HostObjectUtils.GetSideFaces(wall,ShellLayerType.Exterior)
face = wall.GetGeometryObjectFromReference(extside[0])

bboxUV = face.GetBoundingBox()

for u in py_linspace(bboxUV.Min.U, bboxUV.Max.U, precision):
    for v in py_linspace(bboxUV.Min.V, bboxUV.Max.V, precision):
        uv = UV(u, v)
        face_uvs.append(face.Evaluate(uv).ToPoint())
        
OUT = face_uvs
2 Likes

So it is not installing the package itself, just detecting the installed onces?

The reason why i ask is because i have been looking for a way to pip install modules when people use a script that is dependent on external python modules.
In a way that they don’t have to go into a terminal and install themselfs

no, the packages must be installed manually or pre-installed

2 Likes

Hello, you always have to dig up the nuggets.
self-installation :wink:

Cordially
christian.stan

2 Likes