"""
The Geometry submodule and its ``Geometry`` class contain useful helpers for handling
**Revit** element geometries. Note that it is possible to use the ``_()`` shorthand to get
the geometry of an element as follows::
geometry = _(element).getGeometry()
The full syntax without using the shorthand can also be used::
geometry = revitron.Geometry(element)
"""
import math
[docs]class Geometry:
"""
A collection of useful methods for handling Revit element geometries.
"""
[docs] def __init__(self, element):
"""
Inits a new Geometry instance.
Args:
element (object): A Revit element
"""
import revitron
self._geometry = element.get_Geometry(revitron.DB.Options())
[docs] def getFaces(self):
"""
Get a list of all faces of a given element.
Returns:
list: A list of face objects
"""
faces = []
for solid in self.getSolids():
try:
for face in solid.Faces:
faces.append(face)
except:
pass
return faces
[docs] def getSolids(self):
"""
Get a list of all solids of a given element.
Returns:
list: A list of solid objects
"""
solids = []
try:
for geo in self._geometry:
try:
for item in geo.GetInstanceGeometry():
try:
if item.Volume:
solids.append(item)
except:
pass
except:
try:
if geo.Volume:
solids.append(geo)
except:
pass
except:
pass
return solids
[docs] def getCurves(self, curveType='Line'):
"""
Get a list of all curves by type of a given element.
Note:
Child classes of Autodesk.DB.Curve can be used.
Args:
curveType (str, optional): The type of curve to return.
Defaults to 'Line'.
Returns:
list: A list of curve objects
"""
curves = []
for geo in self._geometry:
try:
for item in geo.GetInstanceGeometry():
if item.GetType().Name == curveType:
curves.append(item)
except:
pass
return curves
[docs]class GeometryUtils:
"""
The GeometryUtils class contains a collection of static utility methods for dealing
with geometry related tasks.
"""
[docs] @staticmethod
def getAbsoluteAngleXY(base, endpoint):
"""
Get the absolute angle between 0 and 360 degrees of a vector counter clockwise relative to the X-axis.
Args:
base (XYZ): The base point that represents 0,0 of the coordinate system
endpoint (XYZ): The endpoint of the line starting from the basepoint that represents the vector in question
Returns:
float: The absolute angle between 0 and 360 degrees
"""
from revitron import DB
vector = endpoint - base
angle = math.degrees(vector.AngleTo(DB.XYZ(1, 0, vector.Z)))
if vector.Y < 0:
angle = 360 - angle
return angle
[docs] @staticmethod
def getAngleXY(base, reference, endpoint):
"""
Get the angle from a vector to a reference. Note that the result returns positiv as well as negative angles
depending on the relative location of the reference vector.
Args:
base (XYZ): The base point that represents 0,0 of the coordinate system
reference (XYZ): The endpoint of the reference line starting from the basepoint
endpoint (XYZ): The endpoint of the line starting from the basepoint that represents the vector in question
Returns:
float: The angle
"""
alpha = GeometryUtils.getAbsoluteAngleXY(base, endpoint)
beta = GeometryUtils.getAbsoluteAngleXY(base, reference)
angle = beta - alpha
if angle > 180:
angle = angle - 360
if angle < -180:
angle = angle + 360
return angle
[docs] @staticmethod
def getBoundaryPoints(boundary):
"""
Get a list of points from a given boundary that is usually returned by
methods such as ``GetBoundarySegments``.
Args:
boundary (list): A list of boundary segment lists
Returns:
list: The list of points that define a boundary polygon
"""
boundaryPoints = []
for segmentsList in boundary:
for segment in segmentsList:
curve = segment.GetCurve()
start = curve.GetEndPoint(0)
boundaryPoints.append(start)
return boundaryPoints
[docs] @staticmethod
def polygonContainsPointXY(polygonPoints, point):
"""
Check whether a given polygon that is planar to a horizontal surface
contains a given point. Note that both, the polygon as well as the point must share common Z values.
The algorithm accumulates all angles (positive and negative) between neighboring lines that span from a given point to
all vertices of the polygon. In case the accumulated absolute value equals 360, the point is located inside the polygon.
Args:
polygonPoints (list): A list of points
point (XYZ): The point that is tested
Returns:
boolean: True, if the polygon contains the point
"""
for p in polygonPoints:
if round(p.Z, 3) != round(point.Z, 3):
return False
accumulated = 0
prev = -1
for n in range(0, len(polygonPoints)):
delta = GeometryUtils.getAngleXY(point, polygonPoints[prev], polygonPoints[n])
accumulated += delta
prev = n
accumulated = round(abs(accumulated))
return accumulated == 360