mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 06:58:56 +00:00
Refactor slab addition to support obtuse x_angle. See #5938
To support obtuse x_angle a refactoring had to be made, which helped improve the general code for slab addition. This is challenging because there are a few features that interact with each other to create slabs, like `depth`, `direction_sense`, `offset` and `x_angle`. In addition, these interactions can happen in different parts of the code. This refactor improves the coherence between those different parts. Files changed: - `api/geometry/add_slab_representation.py` - `model/slab.py` - inside the function `change_thickness()` File to be changed in a following commit: - `model/wall.py` - inside the operator `ChangeExtrusionXAngle` - To-do
This commit is contained in:
@@ -31,7 +31,7 @@ import bonsai.core.geometry
|
|||||||
import bonsai.core.root
|
import bonsai.core.root
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
from bonsai.bim.ifc import IfcStore
|
from bonsai.bim.ifc import IfcStore
|
||||||
from math import cos
|
from math import cos, pi
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
from bonsai.bim.module.model.decorator import ProfileDecorator, PolylineDecorator, ProductDecorator
|
from bonsai.bim.module.model.decorator import ProfileDecorator, PolylineDecorator, ProductDecorator
|
||||||
from bonsai.bim.module.model.polyline import PolylineOperator
|
from bonsai.bim.module.model.polyline import PolylineOperator
|
||||||
@@ -135,7 +135,7 @@ class DumbSlabGenerator:
|
|||||||
matrix_world.translation.z = self.container_obj.location.z
|
matrix_world.translation.z = self.container_obj.location.z
|
||||||
else:
|
else:
|
||||||
matrix_world.translation.z
|
matrix_world.translation.z
|
||||||
obj.matrix_world = matrix_world @ Matrix.Rotation(self.x_angle, 4, "X")
|
obj.matrix_world = matrix_world
|
||||||
bpy.context.view_layer.update()
|
bpy.context.view_layer.update()
|
||||||
|
|
||||||
element = bonsai.core.root.assign_class(
|
element = bonsai.core.root.assign_class(
|
||||||
@@ -170,6 +170,7 @@ class DumbSlabGenerator:
|
|||||||
is_global=True,
|
is_global=True,
|
||||||
should_sync_changes_first=False,
|
should_sync_changes_first=False,
|
||||||
)
|
)
|
||||||
|
obj.matrix_world = obj.matrix_world @ Matrix.Rotation(self.x_angle, 4, "X")
|
||||||
|
|
||||||
if self.footprint_context:
|
if self.footprint_context:
|
||||||
extrusion = tool.Model.get_extrusion(representation)
|
extrusion = tool.Model.get_extrusion(representation)
|
||||||
@@ -294,28 +295,39 @@ class DumbSlabPlaner:
|
|||||||
if representation:
|
if representation:
|
||||||
extrusion = tool.Model.get_extrusion(representation)
|
extrusion = tool.Model.get_extrusion(representation)
|
||||||
if extrusion:
|
if extrusion:
|
||||||
x, y, z = extrusion.ExtrudedDirection.DirectionRatios
|
|
||||||
existing_x_angle = tool.Model.get_existing_x_angle(extrusion)
|
existing_x_angle = tool.Model.get_existing_x_angle(extrusion)
|
||||||
perpendicular_depth = thickness * (1 / cos(existing_x_angle))
|
existing_x_angle = 0 if tool.Cad.is_x(existing_x_angle, 0, tolerance=0.001) else existing_x_angle
|
||||||
perpendicular_offset = layer_params["offset"] * (1 / cos(existing_x_angle))
|
existing_x_angle = 0 if tool.Cad.is_x(existing_x_angle, pi, tolerance=0.001) else existing_x_angle
|
||||||
offset_vector = Vector((0.0, 0.0, perpendicular_offset / self.unit_scale))
|
direction_ratios = Vector(extrusion.ExtrudedDirection.DirectionRatios)
|
||||||
if layer_params["direction_sense"] == "POSITIVE":
|
offset_direction = direction_ratios.copy()
|
||||||
y = abs(y) if existing_x_angle > 0 else -abs(y)
|
perpendicular_depth = thickness * abs(1 / cos(existing_x_angle))
|
||||||
z = abs(z)
|
perpendicular_offset = layer_params["offset"] * abs(1 / cos(existing_x_angle)) / self.unit_scale
|
||||||
elif layer_params["direction_sense"] == "NEGATIVE":
|
|
||||||
y = -abs(y) if existing_x_angle > 0 else abs(y)
|
# Check angle and z direction to determine whether the extrusion direction is positive or negative
|
||||||
z = -abs(z)
|
if (existing_x_angle < (pi / 2) and direction_ratios.z > 0) or (
|
||||||
extrusion.ExtrudedDirection.DirectionRatios = (x, y, z)
|
existing_x_angle > (pi / 2) and direction_ratios.z < 0
|
||||||
|
):
|
||||||
|
# The extrusion direction is positive. If the layer_parameter is set to negative,
|
||||||
|
# then the we change the extrusion direction.
|
||||||
|
# The offset direction must always be positive, so we keep it.
|
||||||
|
if layer_params["direction_sense"] == "NEGATIVE":
|
||||||
|
direction_ratios *= -1
|
||||||
|
elif (existing_x_angle > (pi / 2) and direction_ratios.z > 0) or (
|
||||||
|
existing_x_angle < (pi / 2) and direction_ratios.z < 0
|
||||||
|
):
|
||||||
|
# The extrusion direction is negative. If the layer_parameter is set to positive,
|
||||||
|
# then the we change the extrusion direction.
|
||||||
|
# The offset direction must always be positive, so we change it too.
|
||||||
|
if layer_params["direction_sense"] == "POSITIVE":
|
||||||
|
direction_ratios *= -1
|
||||||
|
offset_direction *= -1
|
||||||
|
|
||||||
|
extrusion.ExtrudedDirection.DirectionRatios = tuple(direction_ratios)
|
||||||
extrusion.Depth = perpendicular_depth
|
extrusion.Depth = perpendicular_depth
|
||||||
|
|
||||||
if perpendicular_offset != 0.0 and not extrusion.Position:
|
if perpendicular_offset != 0.0 and not extrusion.Position:
|
||||||
tool.Model.add_extrusion_position(extrusion, perpendicular_offset)
|
position = offset_direction * perpendicular_offset
|
||||||
|
tool.Model.add_extrusion_position(extrusion, position)
|
||||||
# Update the extrusion's location based on its current rotation angle and offset
|
|
||||||
if extrusion.Position:
|
|
||||||
rot_matrix = Matrix.Rotation(existing_x_angle, 4, "X")
|
|
||||||
rot_offset = offset_vector @ rot_matrix
|
|
||||||
extrusion.Position.Location.Coordinates = tuple(rot_offset)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
props = tool.Model.get_model_props()
|
props = tool.Model.get_model_props()
|
||||||
|
|||||||
@@ -2008,33 +2008,22 @@ class Model(bonsai.core.tool.Model):
|
|||||||
FilledOpeningGenerator().generate(filling_obj, voided_obj)
|
FilledOpeningGenerator().generate(filling_obj, voided_obj)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_extrusion_position(cls, extrusion: ifcopenshell.entity_instance, offset: float) -> None:
|
def add_extrusion_position(cls, extrusion: ifcopenshell.entity_instance, position: tuple) -> None:
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
|
|
||||||
position = ifc_file.createIfcAxis2Placement3D(
|
new_position = ifc_file.createIfcAxis2Placement3D(
|
||||||
ifc_file.createIfcCartesianPoint((0.0, 0.0, offset)),
|
ifc_file.createIfcCartesianPoint(position),
|
||||||
ifc_file.createIfcDirection((0.0, 0.0, 1.0)),
|
ifc_file.createIfcDirection((0.0, 0.0, 1.0)),
|
||||||
ifc_file.createIfcDirection((1.0, 0.0, 0.0)),
|
ifc_file.createIfcDirection((1.0, 0.0, 0.0)),
|
||||||
)
|
)
|
||||||
|
|
||||||
extrusion.Position = position
|
extrusion.Position = new_position
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_existing_x_angle(cls, extrusion: ifcopenshell.entity_instance) -> float:
|
def get_existing_x_angle(cls, extrusion: ifcopenshell.entity_instance) -> float:
|
||||||
x, y, z = extrusion.ExtrudedDirection.DirectionRatios
|
x, y, z = extrusion.ExtrudedDirection.DirectionRatios
|
||||||
vector = Vector((0, 1))
|
vector = Vector((0, 1))
|
||||||
x_angle = vector.angle_signed(Vector((y, z)))
|
x_angle = vector.angle_signed(Vector((y, z)))
|
||||||
|
|
||||||
# The extrusion direction is changed by the layer direction change
|
|
||||||
# So we have to adapt the values of y, z and vector accordingly
|
|
||||||
if z < 0 and y < 0:
|
|
||||||
y = abs(y)
|
|
||||||
z = abs(z)
|
|
||||||
if z < 0 and y >= 0:
|
|
||||||
vector = Vector((0, -1))
|
|
||||||
|
|
||||||
x_angle = vector.angle_signed(Vector((y, z)))
|
|
||||||
|
|
||||||
return x_angle
|
return x_angle
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -85,31 +85,37 @@ class Usecase:
|
|||||||
points = ((0.0, 0.0), (size, 0.0), (size, size), (0.0, size), (0.0, 0.0))
|
points = ((0.0, 0.0), (size, 0.0), (size, size), (0.0, size), (0.0, 0.0))
|
||||||
if self.settings["polyline"]:
|
if self.settings["polyline"]:
|
||||||
points = [
|
points = [
|
||||||
(self.convert_si_to_unit(p[0]), self.convert_si_to_unit(p[1] * (1 / cos(self.settings["x_angle"]))))
|
(self.convert_si_to_unit(p[0]), self.convert_si_to_unit(p[1] * abs(1 / cos(self.settings["x_angle"]))))
|
||||||
for p in self.settings["polyline"]
|
for p in self.settings["polyline"]
|
||||||
]
|
]
|
||||||
if self.file.schema == "IFC2X3":
|
if self.file.schema == "IFC2X3":
|
||||||
curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points])
|
curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points])
|
||||||
else:
|
else:
|
||||||
curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList2D(points))
|
curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList2D(points))
|
||||||
if self.settings["x_angle"]:
|
|
||||||
extrusion_direction = self.file.createIfcDirection(
|
|
||||||
(0.0, sin(self.settings["x_angle"]), cos(self.settings["x_angle"]))
|
|
||||||
)
|
|
||||||
if self.settings["direction_sense"] == "NEGATIVE":
|
|
||||||
extrusion_direction = self.file.createIfcDirection(
|
|
||||||
(0.0, -sin(self.settings["x_angle"]), -cos(self.settings["x_angle"]))
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
extrusion_direction = self.file.createIfcDirection((0.0, 0.0, 1.0))
|
|
||||||
if self.settings["direction_sense"] == "NEGATIVE":
|
|
||||||
extrusion_direction = self.file.createIfcDirection((0.0, 0.0, -1.0))
|
|
||||||
|
|
||||||
|
if self.settings["x_angle"]:
|
||||||
|
direction_ratios = (0.0, sin(self.settings["x_angle"]), cos(self.settings["x_angle"]))
|
||||||
|
else:
|
||||||
|
direction_ratios = (0.0, 0.0, 1.0)
|
||||||
|
|
||||||
|
offset_direction = direction_ratios # offset direction doesn't change if direction_sense is negative
|
||||||
|
extrusion_direction = self.file.createIfcDirection(direction_ratios)
|
||||||
|
if self.settings["direction_sense"] == "NEGATIVE":
|
||||||
|
direction_ratios = tuple((-n for n in direction_ratios))
|
||||||
|
extrusion_direction = self.file.createIfcDirection(direction_ratios)
|
||||||
|
|
||||||
|
perpendicular_offset = self.convert_si_to_unit(self.settings["offset"]) * abs(1 / cos(self.settings["x_angle"]))
|
||||||
|
perpendicular_depth = self.convert_si_to_unit(self.settings["depth"]) * abs(1 / cos(self.settings["x_angle"]))
|
||||||
position = None
|
position = None
|
||||||
# default position for IFC2X3 where .Position is not optional
|
# default position for IFC2X3 where .Position is not optional
|
||||||
if self.file.schema == "IFC2X3" or self.settings["offset"] != 0:
|
if self.file.schema == "IFC2X3" or self.settings["offset"] != 0:
|
||||||
|
position_vector = (
|
||||||
|
offset_direction[0] * perpendicular_offset,
|
||||||
|
offset_direction[1] * perpendicular_offset,
|
||||||
|
offset_direction[2] * perpendicular_offset,
|
||||||
|
)
|
||||||
position = self.file.createIfcAxis2Placement3D(
|
position = self.file.createIfcAxis2Placement3D(
|
||||||
self.file.createIfcCartesianPoint((0.0, 0.0, self.convert_si_to_unit(self.settings["offset"]))),
|
self.file.createIfcCartesianPoint(position_vector),
|
||||||
self.file.createIfcDirection((0.0, 0.0, 1.0)),
|
self.file.createIfcDirection((0.0, 0.0, 1.0)),
|
||||||
self.file.createIfcDirection((1.0, 0.0, 0.0)),
|
self.file.createIfcDirection((1.0, 0.0, 0.0)),
|
||||||
)
|
)
|
||||||
@@ -118,7 +124,7 @@ class Usecase:
|
|||||||
self.file.createIfcArbitraryClosedProfileDef("AREA", None, curve),
|
self.file.createIfcArbitraryClosedProfileDef("AREA", None, curve),
|
||||||
position,
|
position,
|
||||||
extrusion_direction,
|
extrusion_direction,
|
||||||
self.convert_si_to_unit(self.settings["depth"]) * 1 / cos(self.settings["x_angle"]),
|
perpendicular_depth,
|
||||||
)
|
)
|
||||||
if self.settings["clippings"]:
|
if self.settings["clippings"]:
|
||||||
return self.apply_clippings(extrusion)
|
return self.apply_clippings(extrusion)
|
||||||
|
|||||||
Reference in New Issue
Block a user