first rough partial draft of alignment handling in python

This commit is contained in:
Thomas Krijnen
2021-05-23 09:37:25 +02:00
parent fd9f209a19
commit 3e8c10b775
@@ -0,0 +1,191 @@
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")