mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +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.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from math import cos
|
||||
from math import cos, pi
|
||||
from mathutils import Vector, Matrix
|
||||
from bonsai.bim.module.model.decorator import ProfileDecorator, PolylineDecorator, ProductDecorator
|
||||
from bonsai.bim.module.model.polyline import PolylineOperator
|
||||
@@ -135,7 +135,7 @@ class DumbSlabGenerator:
|
||||
matrix_world.translation.z = self.container_obj.location.z
|
||||
else:
|
||||
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()
|
||||
|
||||
element = bonsai.core.root.assign_class(
|
||||
@@ -170,6 +170,7 @@ class DumbSlabGenerator:
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
obj.matrix_world = obj.matrix_world @ Matrix.Rotation(self.x_angle, 4, "X")
|
||||
|
||||
if self.footprint_context:
|
||||
extrusion = tool.Model.get_extrusion(representation)
|
||||
@@ -294,28 +295,39 @@ class DumbSlabPlaner:
|
||||
if representation:
|
||||
extrusion = tool.Model.get_extrusion(representation)
|
||||
if extrusion:
|
||||
x, y, z = extrusion.ExtrudedDirection.DirectionRatios
|
||||
existing_x_angle = tool.Model.get_existing_x_angle(extrusion)
|
||||
perpendicular_depth = thickness * (1 / cos(existing_x_angle))
|
||||
perpendicular_offset = layer_params["offset"] * (1 / cos(existing_x_angle))
|
||||
offset_vector = Vector((0.0, 0.0, perpendicular_offset / self.unit_scale))
|
||||
if layer_params["direction_sense"] == "POSITIVE":
|
||||
y = abs(y) if existing_x_angle > 0 else -abs(y)
|
||||
z = abs(z)
|
||||
elif layer_params["direction_sense"] == "NEGATIVE":
|
||||
y = -abs(y) if existing_x_angle > 0 else abs(y)
|
||||
z = -abs(z)
|
||||
extrusion.ExtrudedDirection.DirectionRatios = (x, y, z)
|
||||
existing_x_angle = 0 if tool.Cad.is_x(existing_x_angle, 0, tolerance=0.001) else existing_x_angle
|
||||
existing_x_angle = 0 if tool.Cad.is_x(existing_x_angle, pi, tolerance=0.001) else existing_x_angle
|
||||
direction_ratios = Vector(extrusion.ExtrudedDirection.DirectionRatios)
|
||||
offset_direction = direction_ratios.copy()
|
||||
perpendicular_depth = thickness * abs(1 / cos(existing_x_angle))
|
||||
perpendicular_offset = layer_params["offset"] * abs(1 / cos(existing_x_angle)) / self.unit_scale
|
||||
|
||||
# Check angle and z direction to determine whether the extrusion direction is positive or negative
|
||||
if (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 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
|
||||
|
||||
if perpendicular_offset != 0.0 and not extrusion.Position:
|
||||
tool.Model.add_extrusion_position(extrusion, perpendicular_offset)
|
||||
|
||||
# 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)
|
||||
position = offset_direction * perpendicular_offset
|
||||
tool.Model.add_extrusion_position(extrusion, position)
|
||||
|
||||
else:
|
||||
props = tool.Model.get_model_props()
|
||||
|
||||
@@ -2008,33 +2008,22 @@ class Model(bonsai.core.tool.Model):
|
||||
FilledOpeningGenerator().generate(filling_obj, voided_obj)
|
||||
|
||||
@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()
|
||||
|
||||
position = ifc_file.createIfcAxis2Placement3D(
|
||||
ifc_file.createIfcCartesianPoint((0.0, 0.0, offset)),
|
||||
new_position = ifc_file.createIfcAxis2Placement3D(
|
||||
ifc_file.createIfcCartesianPoint(position),
|
||||
ifc_file.createIfcDirection((0.0, 0.0, 1.0)),
|
||||
ifc_file.createIfcDirection((1.0, 0.0, 0.0)),
|
||||
)
|
||||
|
||||
extrusion.Position = position
|
||||
extrusion.Position = new_position
|
||||
|
||||
@classmethod
|
||||
def get_existing_x_angle(cls, extrusion: ifcopenshell.entity_instance) -> float:
|
||||
x, y, z = extrusion.ExtrudedDirection.DirectionRatios
|
||||
vector = Vector((0, 1))
|
||||
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
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -85,31 +85,37 @@ class Usecase:
|
||||
points = ((0.0, 0.0), (size, 0.0), (size, size), (0.0, size), (0.0, 0.0))
|
||||
if self.settings["polyline"]:
|
||||
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"]
|
||||
]
|
||||
if self.file.schema == "IFC2X3":
|
||||
curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points])
|
||||
else:
|
||||
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
|
||||
# default position for IFC2X3 where .Position is not optional
|
||||
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(
|
||||
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((1.0, 0.0, 0.0)),
|
||||
)
|
||||
@@ -118,7 +124,7 @@ class Usecase:
|
||||
self.file.createIfcArbitraryClosedProfileDef("AREA", None, curve),
|
||||
position,
|
||||
extrusion_direction,
|
||||
self.convert_si_to_unit(self.settings["depth"]) * 1 / cos(self.settings["x_angle"]),
|
||||
perpendicular_depth,
|
||||
)
|
||||
if self.settings["clippings"]:
|
||||
return self.apply_clippings(extrusion)
|
||||
|
||||
Reference in New Issue
Block a user