mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
192 lines
5.1 KiB
Python
192 lines
5.1 KiB
Python
|
|
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")
|