Files
IfcOpenShell/src/ifcopenshell-python/ifcopenshell/alignment.py
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

192 lines
5.1 KiB
Python
Raw Normal View History

from dataclasses import dataclass
import numpy
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.express
import ifcopenshell.transition_curve
# geometric primitives
# @notes
# - not sure if the separation of geometric primitives make sense
# does it make handling the variety of distance expressions and
# interpolation harder?
@dataclass
class line:
start_point: numpy.ndarray
direction_vector: numpy.ndarray
def __call__(self, u):
return self.start_point + \
self.direction_vector * u
@dataclass
class circle:
radius: numpy.ndarray
def __call__(self, u):
return numpy.array([
self.radius * numpy.cos(u),
self.radius * numpy.sin(u)
])
def place(matrix, func):
"""
Higher order function for application of a 3x3 matrix
to a 2D point. Assumes a functor such as line or circle.
"""
def inner(*args):
v = func(*args)
# homogenize
v = numpy.insert(v, v.shape[-1], 1, axis=-1)
return (matrix @ v)[0:2]
return inner
# mapping functions from IFC entities
def map_inst(inst):
"""
Looks up one of the implementation functions below in the global namespace
"""
return globals()[f"impl_{inst.is_a()}"](inst)
def impl_IfcLine(inst):
return line(
numpy.array(inst.Pnt.Coordinates),
numpy.array(inst.Dir.Orientation.DirectionRatios) * inst.Dir.Magnitude
)
def impl_IfcCircle(inst):
return place(map_inst(inst.Position), circle(
inst.Radius
))
def impl_IfcClothoid(inst):
# @todo
# place = map_inst(inst.Position)
# ifcopenshell.transition_curve.TransitionCurve(
# StartPoint = place.T[2]
# StartDirection = numpy.arctan2(place.T[0][1], place.T[0][0]),
# SegmentLength =
# IsStartRadiusCCW =
# IsEndRadiusCCW =
# TransitionCurveType =
# StartRadius =
# EndRadius =
# )
return lambda *args: numpy.array((0.,0.))
def impl_IfcAxis2Placement2D(inst):
arr = numpy.eye(3)
if inst is None:
return arr
arr.T[2, 0:2] = inst.Location.Coordinates
if inst.RefDirection is None:
return arr
arr.T[0, 0:2] = inst.RefDirection.DirectionRatios
arr.T[0, 0:2] /= numpy.linalg.norm(arr.T[0, 0:2])
arr.T[1, 0:2] = -arr.T[0,1], arr.T[0,0]
return arr
# conversion functions for semantic design parameters (not used atm)
def convert(inst):
"""
Looks up one of the conversion functions below in the global namespace
"""
yield from globals()[f"convert_{inst.is_a()}_{inst.PredefinedType}"](inst)
def convert_IfcAlignmentHorizontalSegment_LINE(data):
xy = numpy.array(data.StartPoint.Coordinates)
yield xy
di = numpy.array([
numpy.cos(data.StartDirection),
numpy.sin(data.StartDirection)
])
yield xy + di * data.SegmentLength
# Two approaches, either DesignParameters or Representation
def interpret_linear_element_semantics(settings, crv):
# traverse decomposition
for rel in crv.IsNestedBy:
for obj in rel.RelatedObjects:
yield from interpret_linear_element_semantics(settings, obj)
# lookup design parameters and dispatch to conversion function
if crv.is_a("IfcAlignmentSegment"):
dp = crv.DesignParameters
yield from convert(dp)
def interpret_linear_element_geometry(settings, crv):
for segment in crv.Representation.Representations[0].Items[0].Segments:
print(segment)
print(segment.ParentCurve)
print()
func = place(
map_inst(segment.Placement),
map_inst(segment.ParentCurve)
)
for u in numpy.linspace(
segment.SegmentStart[0],
segment.SegmentStart[0] + segment.SegmentLength[0],
num=32
):
yield func(u)
interpret_linear_element = interpret_linear_element_geometry
def create_shape(settings, elem):
if elem.is_a("IfcLinearPositioningElement") or elem.is_a("IfcLinearElement"):
return numpy.row_stack(list(interpret_linear_element(settings, elem)))
else:
return ifcopenshell.geom.create_shape(settings, elem)
def print_structure(alignment, indent=0):
"""
Debugging function to print alignment decomposition
"""
print(" " * indent, str(alignment)[0:100])
for rel in alignment.IsNestedBy:
for child in rel.RelatedObjects:
print_structure(child, indent+2)
if __name__ == "__main__":
import sys
from matplotlib import pyplot as plt
s = ifcopenshell.express.parse("IFC4x3_RC3.exp")
ifcopenshell.register_schema(s)
f = ifcopenshell.open(sys.argv[1])
print_structure(f.by_type("IfcAlignment")[0])
al_hor = f.by_type("IfcAlignmentHorizontal")[0]
xy = create_shape({}, al_hor)
plt.plot(xy.T[0], xy.T[1])
plt.savefig("horizontal_alignment.png")