diff --git a/src/bonsai/bonsai/bim/module/model/slab.py b/src/bonsai/bonsai/bim/module/model/slab.py index 1010217cae..a5ac6ae121 100644 --- a/src/bonsai/bonsai/bim/module/model/slab.py +++ b/src/bonsai/bonsai/bim/module/model/slab.py @@ -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() diff --git a/src/bonsai/bonsai/tool/model.py b/src/bonsai/bonsai/tool/model.py index d8f986f48c..2af631b671 100644 --- a/src/bonsai/bonsai/tool/model.py +++ b/src/bonsai/bonsai/tool/model.py @@ -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 diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py index 05ed08edf4..6443e3a857 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py @@ -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)