Make slab's editing profile to horizontal orientation when x_angle is present.

This commit is contained in:
Bruno Perdigão
2025-01-27 15:16:22 -03:00
parent 47b2a00614
commit a4a52d6576
2 changed files with 48 additions and 9 deletions
+36 -2
View File
@@ -646,14 +646,31 @@ class EnableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
body = ifcopenshell.util.representation.resolve_representation(body) body = ifcopenshell.util.representation.resolve_representation(body)
extrusion = tool.Model.get_extrusion(body) extrusion = tool.Model.get_extrusion(body)
existing_x_angle = tool.Model.get_existing_x_angle(extrusion)
layer_params = tool.Model.get_material_layer_parameters(element)
if extrusion.Position: if extrusion.Position:
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist()) position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
position.translation *= self.unit_scale position.translation *= self.unit_scale
# Restore the position to before it was changed by the offset and x_angle
rot_matrix = Matrix.Rotation(existing_x_angle, 4, "X")
offset = Vector((0.0, 0.0, -layer_params["offset"]))
rot_offset = offset @ rot_matrix
tranlation_matrix = Matrix.Translation(rot_offset)
position = position @ tranlation_matrix
# Restore Object rotation to zero
local_rot_mat = obj.rotation_euler.to_matrix()
rot_mat = Matrix.Rotation(-existing_x_angle, 4, "X")
new_rot_mat = local_rot_mat.to_4x4() @ rot_mat
new_rot_euler = new_rot_mat.to_euler()
obj.rotation_euler = new_rot_euler
else: else:
position = Matrix() position = Matrix()
tool.Model.import_profile(extrusion.SweptArea, obj=obj, position=position) tool.Model.import_profile(extrusion.SweptArea, obj=obj, position=position, x_angle=existing_x_angle)
bpy.ops.object.mode_set(mode="EDIT") bpy.ops.object.mode_set(mode="EDIT")
ProfileDecorator.install(context, exit_edit_mode_callback=lambda: disable_editing_extrusion_profile(context)) ProfileDecorator.install(context, exit_edit_mode_callback=lambda: disable_editing_extrusion_profile(context))
@@ -678,13 +695,30 @@ class EditExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
body = ifcopenshell.util.representation.resolve_representation(body) body = ifcopenshell.util.representation.resolve_representation(body)
extrusion = tool.Model.get_extrusion(body) extrusion = tool.Model.get_extrusion(body)
existing_x_angle = tool.Model.get_existing_x_angle(extrusion)
layer_params = tool.Model.get_material_layer_parameters(element)
if extrusion.Position: if extrusion.Position:
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist()) position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
position.translation *= self.unit_scale position.translation *= self.unit_scale
# Restore the position to after it was changed by the offset and x_angle
rot_matrix = Matrix.Rotation(existing_x_angle, 4, "X")
offset = Vector((0.0, 0.0, -layer_params["offset"]))
rot_offset = offset @ rot_matrix
tranlation_matrix = Matrix.Translation(rot_offset)
position = position @ tranlation_matrix
# Restore Object rotation to x_angle
local_rot_mat = obj.rotation_euler.to_matrix()
rot_mat = Matrix.Rotation(existing_x_angle, 4, "X")
new_rot_mat = local_rot_mat.to_4x4() @ rot_mat
new_rot_euler = new_rot_mat.to_euler()
obj.rotation_euler = new_rot_euler
else: else:
position = Matrix() position = Matrix()
profile = tool.Model.export_profile(obj, position=position) profile = tool.Model.export_profile(obj, position=position, x_angle=existing_x_angle)
if not profile: if not profile:
+12 -7
View File
@@ -36,7 +36,7 @@ import bonsai.core.geometry
import bonsai.core.tool import bonsai.core.tool
import bonsai.tool as tool import bonsai.tool as tool
import bonsai.core.geometry as geometry import bonsai.core.geometry as geometry
from math import atan, degrees, radians from math import atan, cos, degrees, radians
from mathutils import Matrix, Vector from mathutils import Matrix, Vector
from copy import deepcopy from copy import deepcopy
from functools import partial from functools import partial
@@ -139,13 +139,13 @@ class Model(bonsai.core.tool.Model):
@classmethod @classmethod
def export_profile( def export_profile(
cls, obj: bpy.types.Object, position: Optional[Matrix] = None cls, obj: bpy.types.Object, position: Optional[Matrix] = None, x_angle: Optional[float] = None
) -> ifcopenshell.entity_instance | None: ) -> ifcopenshell.entity_instance | None:
"""Returns `None` in case if profile was invalid.""" """Returns `None` in case if profile was invalid."""
if position is None: if position is None:
position = Matrix() position = Matrix()
result = cls.auto_detect_profiles(obj, obj.data, position) result = cls.auto_detect_profiles(obj, obj.data, position, x_angle)
if isinstance(result, dict) and result["profile_def"]: if isinstance(result, dict) and result["profile_def"]:
return tool.Ifc.get().add(result["profile_def"]) return tool.Ifc.get().add(result["profile_def"])
@@ -286,6 +286,7 @@ class Model(bonsai.core.tool.Model):
profile: ifcopenshell.entity_instance, profile: ifcopenshell.entity_instance,
obj: Optional[bpy.types.Object] = None, obj: Optional[bpy.types.Object] = None,
position: Optional[Matrix] = None, position: Optional[Matrix] = None,
x_angle: Optional[float] = None,
) -> Union[bpy.types.Object, None]: ) -> Union[bpy.types.Object, None]:
"""Creates new profile mesh and assigns it to `obj`, """Creates new profile mesh and assigns it to `obj`,
if `obj` is `None` then new "Profile" object will be created. if `obj` is `None` then new "Profile" object will be created.
@@ -305,10 +306,10 @@ class Model(bonsai.core.tool.Model):
profiles = profile.Profiles if profile.is_a("IfcCompositeProfileDef") else [profile] profiles = profile.Profiles if profile.is_a("IfcCompositeProfileDef") else [profile]
for profile in profiles: for profile in profiles:
if profile.is_a("IfcArbitraryClosedProfileDef"): if profile.is_a("IfcArbitraryClosedProfileDef"):
cls.convert_curve_to_mesh(obj, position, profile.OuterCurve) cls.convert_curve_to_mesh(obj, position, profile.OuterCurve, x_angle=x_angle)
if profile.is_a("IfcArbitraryProfileDefWithVoids"): if profile.is_a("IfcArbitraryProfileDefWithVoids"):
for inner_curve in profile.InnerCurves: for inner_curve in profile.InnerCurves:
cls.convert_curve_to_mesh(obj, position, inner_curve) cls.convert_curve_to_mesh(obj, position, inner_curve, x_angle=x_angle)
elif profile.is_a() == "IfcRectangleProfileDef": elif profile.is_a() == "IfcRectangleProfileDef":
cls.import_rectangle(obj, position, profile) cls.import_rectangle(obj, position, profile)
elif profile.is_a() == "IfcAnnotationFillArea": elif profile.is_a() == "IfcAnnotationFillArea":
@@ -423,7 +424,7 @@ class Model(bonsai.core.tool.Model):
@classmethod @classmethod
def convert_curve_to_mesh( def convert_curve_to_mesh(
cls, obj: bpy.types.Object, position: Matrix, curve: ifcopenshell.entity_instance cls, obj: bpy.types.Object, position: Matrix, curve: ifcopenshell.entity_instance, x_angle: Optional[float] = None,
) -> None: ) -> None:
offset = len(cls.vertices) offset = len(cls.vertices)
@@ -444,6 +445,8 @@ class Model(bonsai.core.tool.Model):
elif curve.is_a("IfcIndexedPolyCurve"): elif curve.is_a("IfcIndexedPolyCurve"):
for local_point in curve.Points.CoordList: for local_point in curve.Points.CoordList:
global_point = position @ Vector(cls.convert_unit_to_si(local_point)).to_3d() global_point = position @ Vector(cls.convert_unit_to_si(local_point)).to_3d()
if x_angle:
global_point = Vector((global_point[0], global_point[1] * cos(x_angle), global_point[2]))
cls.vertices.append(global_point) cls.vertices.append(global_point)
if curve.Segments: if curve.Segments:
for segment in curve.Segments: for segment in curve.Segments:
@@ -1617,7 +1620,7 @@ class Model(bonsai.core.tool.Model):
@classmethod @classmethod
def auto_detect_profiles( def auto_detect_profiles(
cls, obj: bpy.types.Object, mesh: bpy.types.Mesh, position: Matrix | None = None cls, obj: bpy.types.Object, mesh: bpy.types.Mesh, position: Matrix | None = None, x_angle: Optional[float] = None,
) -> Union[tuple, dict]: ) -> Union[tuple, dict]:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
@@ -1785,6 +1788,8 @@ class Model(bonsai.core.tool.Model):
curves.append(tmp.createIfcPolyline(points)) curves.append(tmp.createIfcPolyline(points))
else: # Pure straight polyline, no segments required else: # Pure straight polyline, no segments required
coord_list = [list(((position_i @ v.co) / unit_scale).to_2d()) for v in loop_verts] coord_list = [list(((position_i @ v.co) / unit_scale).to_2d()) for v in loop_verts]
if x_angle:
coord_list = [(c[0], c[1] / cos(x_angle)**2) for c in coord_list]
if is_closed: if is_closed:
coord_list.append(coord_list[0]) coord_list.append(coord_list[0])
points = tmp.createIfcCartesianPointList2D(coord_list) points = tmp.createIfcCartesianPointList2D(coord_list)