ladybugdynamo.geometryoperations module
Collection of geometrical operations for Dynamo
"""Collection of geometrical operations for Dynamo"""
# import Dynamo libraries
try:
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
except ImportError:
print "Failed to import Dynamo libraries. Make sure path is added to sys.path"
from ladybug.listoperations import *
import math
def toDSVector(vector):
try:
return Vector.ByCoordinates(vector.x, vector.y, vector.z)
except:
return Vector.ByCoordinates(*vector)
def disposeGeometries(geometries):
try:
for geo in geometries: geo.Dispose()
except Exception, e:
print str(e)
def calculateSceneSize(geometries):
"""Calculate scene size for a list of geometry
Args:
geometries: List of input geometries (Solid, Geometry, Points)
Return:
length: length of scene's diagonal
"""
for geo in geometries:
assert isinstance(geo, Geometry), \
"%s is not a geometry"%str(geo)
bbox = BoundingBox.ByGeometry(geometries)
minPt = bbox.MinPoint
maxPt = bbox.MaxPoint
distance = minPt.DistanceTo(maxPt)
disposeGeometries([bbox, minPt, maxPt])
return distance
def xfrange(start, stop, step):
values = []
while start <= stop:
values.append(start)
start += step
return values
# TODO: Change numberOfSegments to gridSize
def generatePointsFromSurface(testSurface, numOfSegments, distanceFromBaseSrf):
#generate values between 0 and 1 based on number of segments
step = 1.0 / (numOfSegments - 1)
parameters = xfrange(0.00, 1.00, step)
__pts = []
__ptNormals = []
for p in parameters:
pts = []
normals = []
for pp in parameters:
uv = UV.ByCoordinates(p, pp)
pt = testSurface.PointAtParameter(uv.U, uv.V)
normal = testSurface.NormalAtParameter(uv.U, uv.V).Normalized().Scale(distanceFromBaseSrf)
pts.append(pt.Translate(normal))
normals.append(normal)
__pts.append(pts)
__ptNormals.append(normals)
return __pts, __ptNormals
# TODO: Add data on top of vectors. It can be timedate or skypatch number
class LBAnalysisPoint:
"""Ladybug Analysis points
Attributes:
testPoint: A points that represnts test sensor
vectors: Test vectors for analysis. It can be sun vectors or vectors for sky patches
length: maximum length of test secne
pointNormal: A vector that represents test point's directon. None for sunlighthours analysisPoint
values: A list of values that correspond to the list of vectors. None for sunlighthours analysis
"""
def __init__(self, testPoint, vectors, length, pointNormal = None, values = None):
self.testPoint = testPoint
# calculate LinRays
self.lineRays = [LineRay(self.testPoint, vector, length) for vector in vectors]
# angle between test point normal and vectors. If no point normal angle is set to 0
self.angles = [pointNormal.AngleBetween(vector) if pointNormal else 0 for vector in vectors]
# values for each vectors. If no value it will be set to 1
self.values = values if values else [1] * len(vectors)
# assume that point sees none of the vectors
self.intensity = [0] * len(vectors)
def calculateIntersections(self, contextGeometries, parallel = False):
"""calculate intersection for this analysis point against conetext geometries"""
for lineCount, lineRay in enumerate(self.lineRays):
# in case angle is larger than 90 or vector value is 0 result will
# be 0 - continue to next line ray
if self.angles[lineCount] >= 90 or self.values[lineCount] == 0:
continue
for geometry in contextGeometries:
intersection = geometry.Intersect(lineRay.ray)
if len(intersection) > 0:
disposeGeometries(intersection)
self.intensity[lineCount] = 0
break
else:
disposeGeometries(intersection)
# calculate value based on normal angle and value
self.intensity[lineCount] = \
self.values[lineCount] * math.cos(math.radians(self.angles[lineCount]))
#dispose lineRays
for lr in self.lineRays:
disposeGeometries([lr.ray])
del(self.lineRays)
@property
def totalNotIntersected(self):
return sum(self.intensity)
class LineRay:
"""Create a line ray
LineRay is useful for solving intersections both for Sunlighthours
and radition analysis
Attributes:
startPoint: A Point that indicated the start point of the ray
vector: A Vector which the ray should be aimed toward
length: A float value for the length of the LineRay
"""
def __init__(self, startPoint, vector, length):
self.startPoint = startPoint
self.vector = vector
self.length = length
self.ray = self.__calculateRay()
def __calculateRay(self):
return Line.ByStartPointDirectionLength(self.startPoint, self.vector, self.length)
Functions
def calculateSceneSize(
geometries)
Calculate scene size for a list of geometry
Args: geometries: List of input geometries (Solid, Geometry, Points)
Return: length: length of scene's diagonal
def calculateSceneSize(geometries):
"""Calculate scene size for a list of geometry
Args:
geometries: List of input geometries (Solid, Geometry, Points)
Return:
length: length of scene's diagonal
"""
for geo in geometries:
assert isinstance(geo, Geometry), \
"%s is not a geometry"%str(geo)
bbox = BoundingBox.ByGeometry(geometries)
minPt = bbox.MinPoint
maxPt = bbox.MaxPoint
distance = minPt.DistanceTo(maxPt)
disposeGeometries([bbox, minPt, maxPt])
return distance
def disposeGeometries(
geometries)
def disposeGeometries(geometries):
try:
for geo in geometries: geo.Dispose()
except Exception, e:
print str(e)
def generatePointsFromSurface(
testSurface, numOfSegments, distanceFromBaseSrf)
def generatePointsFromSurface(testSurface, numOfSegments, distanceFromBaseSrf):
#generate values between 0 and 1 based on number of segments
step = 1.0 / (numOfSegments - 1)
parameters = xfrange(0.00, 1.00, step)
__pts = []
__ptNormals = []
for p in parameters:
pts = []
normals = []
for pp in parameters:
uv = UV.ByCoordinates(p, pp)
pt = testSurface.PointAtParameter(uv.U, uv.V)
normal = testSurface.NormalAtParameter(uv.U, uv.V).Normalized().Scale(distanceFromBaseSrf)
pts.append(pt.Translate(normal))
normals.append(normal)
__pts.append(pts)
__ptNormals.append(normals)
return __pts, __ptNormals
def toDSVector(
vector)
def toDSVector(vector):
try:
return Vector.ByCoordinates(vector.x, vector.y, vector.z)
except:
return Vector.ByCoordinates(*vector)
def xfrange(
start, stop, step)
def xfrange(start, stop, step):
values = []
while start <= stop:
values.append(start)
start += step
return values
Classes
class LBAnalysisPoint
Ladybug Analysis points
Attributes: testPoint: A points that represnts test sensor vectors: Test vectors for analysis. It can be sun vectors or vectors for sky patches length: maximum length of test secne pointNormal: A vector that represents test point's directon. None for sunlighthours analysisPoint values: A list of values that correspond to the list of vectors. None for sunlighthours analysis
class LBAnalysisPoint:
"""Ladybug Analysis points
Attributes:
testPoint: A points that represnts test sensor
vectors: Test vectors for analysis. It can be sun vectors or vectors for sky patches
length: maximum length of test secne
pointNormal: A vector that represents test point's directon. None for sunlighthours analysisPoint
values: A list of values that correspond to the list of vectors. None for sunlighthours analysis
"""
def __init__(self, testPoint, vectors, length, pointNormal = None, values = None):
self.testPoint = testPoint
# calculate LinRays
self.lineRays = [LineRay(self.testPoint, vector, length) for vector in vectors]
# angle between test point normal and vectors. If no point normal angle is set to 0
self.angles = [pointNormal.AngleBetween(vector) if pointNormal else 0 for vector in vectors]
# values for each vectors. If no value it will be set to 1
self.values = values if values else [1] * len(vectors)
# assume that point sees none of the vectors
self.intensity = [0] * len(vectors)
def calculateIntersections(self, contextGeometries, parallel = False):
"""calculate intersection for this analysis point against conetext geometries"""
for lineCount, lineRay in enumerate(self.lineRays):
# in case angle is larger than 90 or vector value is 0 result will
# be 0 - continue to next line ray
if self.angles[lineCount] >= 90 or self.values[lineCount] == 0:
continue
for geometry in contextGeometries:
intersection = geometry.Intersect(lineRay.ray)
if len(intersection) > 0:
disposeGeometries(intersection)
self.intensity[lineCount] = 0
break
else:
disposeGeometries(intersection)
# calculate value based on normal angle and value
self.intensity[lineCount] = \
self.values[lineCount] * math.cos(math.radians(self.angles[lineCount]))
#dispose lineRays
for lr in self.lineRays:
disposeGeometries([lr.ray])
del(self.lineRays)
@property
def totalNotIntersected(self):
return sum(self.intensity)
Ancestors (in MRO)
Instance variables
var angles
var intensity
var lineRays
var testPoint
var totalNotIntersected
var values
Methods
def __init__(
self, testPoint, vectors, length, pointNormal=None, values=None)
def __init__(self, testPoint, vectors, length, pointNormal = None, values = None):
self.testPoint = testPoint
# calculate LinRays
self.lineRays = [LineRay(self.testPoint, vector, length) for vector in vectors]
# angle between test point normal and vectors. If no point normal angle is set to 0
self.angles = [pointNormal.AngleBetween(vector) if pointNormal else 0 for vector in vectors]
# values for each vectors. If no value it will be set to 1
self.values = values if values else [1] * len(vectors)
# assume that point sees none of the vectors
self.intensity = [0] * len(vectors)
def calculateIntersections(
self, contextGeometries, parallel=False)
calculate intersection for this analysis point against conetext geometries
def calculateIntersections(self, contextGeometries, parallel = False):
"""calculate intersection for this analysis point against conetext geometries"""
for lineCount, lineRay in enumerate(self.lineRays):
# in case angle is larger than 90 or vector value is 0 result will
# be 0 - continue to next line ray
if self.angles[lineCount] >= 90 or self.values[lineCount] == 0:
continue
for geometry in contextGeometries:
intersection = geometry.Intersect(lineRay.ray)
if len(intersection) > 0:
disposeGeometries(intersection)
self.intensity[lineCount] = 0
break
else:
disposeGeometries(intersection)
# calculate value based on normal angle and value
self.intensity[lineCount] = \
self.values[lineCount] * math.cos(math.radians(self.angles[lineCount]))
#dispose lineRays
for lr in self.lineRays:
disposeGeometries([lr.ray])
del(self.lineRays)
class LineRay
Create a line ray
LineRay is useful for solving intersections both for Sunlighthours and radition analysis
Attributes: startPoint: A Point that indicated the start point of the ray vector: A Vector which the ray should be aimed toward length: A float value for the length of the LineRay
class LineRay:
"""Create a line ray
LineRay is useful for solving intersections both for Sunlighthours
and radition analysis
Attributes:
startPoint: A Point that indicated the start point of the ray
vector: A Vector which the ray should be aimed toward
length: A float value for the length of the LineRay
"""
def __init__(self, startPoint, vector, length):
self.startPoint = startPoint
self.vector = vector
self.length = length
self.ray = self.__calculateRay()
def __calculateRay(self):
return Line.ByStartPointDirectionLength(self.startPoint, self.vector, self.length)
Ancestors (in MRO)
Instance variables
var length
var ray
var startPoint
var vector
Methods
def __init__(
self, startPoint, vector, length)
def __init__(self, startPoint, vector, length):
self.startPoint = startPoint
self.vector = vector
self.length = length
self.ray = self.__calculateRay()