mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-11 14:22:33 +00:00
Revert "fix #7559: DirectionSense works again."
This reverts commit d79524b087.
This commit is contained in:
@@ -231,17 +231,16 @@ class DumbSlabPlaner:
|
|||||||
for inverse in tool.Ifc.get().get_inverse(layer_set):
|
for inverse in tool.Ifc.get().get_inverse(layer_set):
|
||||||
if not inverse.is_a("IfcMaterialLayerSetUsage") or inverse.LayerSetDirection != "AXIS3":
|
if not inverse.is_a("IfcMaterialLayerSetUsage") or inverse.LayerSetDirection != "AXIS3":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if tool.Ifc.get().schema == "IFC2X3":
|
if tool.Ifc.get().schema == "IFC2X3":
|
||||||
for rel in tool.Ifc.get().get_inverse(inverse):
|
for rel in tool.Ifc.get().get_inverse(inverse):
|
||||||
if not rel.is_a("IfcRelAssociatesMaterial"):
|
if not rel.is_a("IfcRelAssociatesMaterial"):
|
||||||
continue
|
continue
|
||||||
for element in rel.RelatedObjects:
|
for element in rel.RelatedObjects:
|
||||||
self.change_thickness(element, total_thickness, preserve_offset=True)
|
self.change_thickness(element, total_thickness)
|
||||||
else:
|
else:
|
||||||
for rel in inverse.AssociatedTo:
|
for rel in inverse.AssociatedTo:
|
||||||
for element in rel.RelatedObjects:
|
for element in rel.RelatedObjects:
|
||||||
self.change_thickness(element, total_thickness, preserve_offset=True)
|
self.change_thickness(element, total_thickness)
|
||||||
|
|
||||||
def regenerate_from_occurence(self, element, material_set_usage):
|
def regenerate_from_occurence(self, element, material_set_usage):
|
||||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
@@ -251,12 +250,9 @@ class DumbSlabPlaner:
|
|||||||
return
|
return
|
||||||
self.change_thickness(element, total_thickness)
|
self.change_thickness(element, total_thickness)
|
||||||
|
|
||||||
def change_thickness(
|
def change_thickness(self, element: ifcopenshell.entity_instance, thickness: float) -> None:
|
||||||
self, element: ifcopenshell.entity_instance, thickness: float, preserve_offset: bool = False
|
|
||||||
) -> None:
|
|
||||||
if tool.Model.get_usage_type(element) != "LAYER3":
|
if tool.Model.get_usage_type(element) != "LAYER3":
|
||||||
return
|
return
|
||||||
|
|
||||||
layer_params = tool.Model.get_material_layer_parameters(element)
|
layer_params = tool.Model.get_material_layer_parameters(element)
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||||
@@ -282,40 +278,79 @@ class DumbSlabPlaner:
|
|||||||
cos_angle = direction_ratios.normalized().dot(Vector((0, 0, 1)))
|
cos_angle = direction_ratios.normalized().dot(Vector((0, 0, 1)))
|
||||||
extrusion_angle = acos(min(max(cos_angle, -1), 1))
|
extrusion_angle = acos(min(max(cos_angle, -1), 1))
|
||||||
|
|
||||||
# Only apply 1/cos factor when there's actual extrusion slope
|
# FIX: Only apply 1/cos factor when there's actual extrusion slope
|
||||||
if extrusion_angle > 1e-6:
|
if extrusion_angle > 1e-6:
|
||||||
perpendicular_depth = thickness * abs(1 / cos(extrusion_angle))
|
perpendicular_depth = thickness * abs(1 / cos(extrusion_angle))
|
||||||
perpendicular_offset = layer_offset * abs(1 / cos(extrusion_angle))
|
perpendicular_offset = layer_offset * abs(1 / cos(extrusion_angle)) / self.unit_scale
|
||||||
else:
|
else:
|
||||||
perpendicular_depth = thickness
|
perpendicular_depth = thickness
|
||||||
perpendicular_offset = layer_offset
|
perpendicular_offset = layer_offset / self.unit_scale
|
||||||
|
|
||||||
|
# Check if direction sense needs to be applied
|
||||||
|
# This should only happen if explicitly requested, not automatically
|
||||||
|
if layer_params.get("apply_direction_sense", False):
|
||||||
|
# Store current direction before potential change
|
||||||
|
old_direction = direction_ratios.copy()
|
||||||
|
|
||||||
|
# Apply direction sense logic
|
||||||
|
existing_x_angle = extrusion_angle
|
||||||
|
if (abs(existing_x_angle) < (pi / 2) and direction_ratios.z > 0) or (
|
||||||
|
abs(existing_x_angle) > (pi / 2) and direction_ratios.z < 0
|
||||||
|
):
|
||||||
|
if layer_params["direction_sense"] == "NEGATIVE":
|
||||||
|
direction_ratios *= -1
|
||||||
|
elif (abs(existing_x_angle) > (pi / 2) and direction_ratios.z > 0) or (
|
||||||
|
abs(existing_x_angle) < (pi / 2) and direction_ratios.z < 0
|
||||||
|
):
|
||||||
|
offset_direction = direction_ratios.copy() * -1
|
||||||
|
if layer_params["direction_sense"] == "POSITIVE":
|
||||||
|
direction_ratios *= -1
|
||||||
|
|
||||||
|
# If direction changed, update extrusion with rotation compensation
|
||||||
|
if (direction_ratios.normalized() - old_direction.normalized()).length > 1e-6:
|
||||||
|
update_extrusion_direction(element, tuple(direction_ratios), obj)
|
||||||
|
# After updating direction, get the updated extrusion
|
||||||
|
extrusion = tool.Model.get_extrusion(representation)
|
||||||
|
|
||||||
|
# Update depth
|
||||||
extrusion.Depth = perpendicular_depth
|
extrusion.Depth = perpendicular_depth
|
||||||
|
|
||||||
# Update position
|
# Update position
|
||||||
ifc_position = extrusion.Position
|
ifc_position = extrusion.Position
|
||||||
|
|
||||||
if direction_ratios.length > 0:
|
if direction_ratios.length > 0:
|
||||||
offset_vector = direction_ratios.normalized() * perpendicular_offset
|
offset_vector = direction_ratios.normalized() * perpendicular_offset
|
||||||
position = offset_vector
|
position = offset_vector
|
||||||
|
|
||||||
material = ifcopenshell.util.element.get_material(element)
|
material = ifcopenshell.util.element.get_material(element)
|
||||||
if material and material.is_a("IfcMaterialLayerSetUsage"):
|
if material and material.is_a("IfcMaterialLayerSetUsage"):
|
||||||
# Only set offset if not preserving it (preserves independent offsets per instance)
|
material.OffsetFromReferenceLine = position.z
|
||||||
if not preserve_offset:
|
|
||||||
material.OffsetFromReferenceLine = position.z
|
|
||||||
|
|
||||||
if ifc_position:
|
if ifc_position:
|
||||||
ifc_position.Location.Coordinates = position
|
ifc_position.Location.Coordinates = position
|
||||||
else:
|
else:
|
||||||
tool.Model.add_extrusion_position(extrusion, position)
|
tool.Model.add_extrusion_position(extrusion, position)
|
||||||
|
|
||||||
bonsai.core.geometry.switch_representation(
|
else:
|
||||||
tool.Ifc,
|
props = tool.Model.get_model_props()
|
||||||
tool.Geometry,
|
x_angle = 0 if tool.Cad.is_x(props.x_angle, 0, tolerance=0.001) else props.x_angle
|
||||||
obj=obj,
|
new_rep = ifcopenshell.api.geometry.add_slab_representation(
|
||||||
representation=representation,
|
tool.Ifc.get(),
|
||||||
)
|
context=body_context,
|
||||||
|
depth=thickness * self.unit_scale,
|
||||||
|
x_angle=x_angle,
|
||||||
|
)
|
||||||
|
for inverse in tool.Ifc.get().get_inverse(representation):
|
||||||
|
ifcopenshell.util.element.replace_attribute(inverse, representation, new_rep)
|
||||||
|
bonsai.core.geometry.switch_representation(
|
||||||
|
tool.Ifc,
|
||||||
|
tool.Geometry,
|
||||||
|
obj=obj,
|
||||||
|
representation=new_rep,
|
||||||
|
)
|
||||||
|
bonsai.core.geometry.remove_representation(
|
||||||
|
tool.Ifc, tool.Geometry, obj=obj, representation=representation
|
||||||
|
)
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
props = tool.Model.get_model_props()
|
props = tool.Model.get_model_props()
|
||||||
x_angle = 0 if tool.Cad.is_x(props.x_angle, 0, tolerance=0.001) else props.x_angle
|
x_angle = 0 if tool.Cad.is_x(props.x_angle, 0, tolerance=0.001) else props.x_angle
|
||||||
@@ -328,12 +363,13 @@ class DumbSlabPlaner:
|
|||||||
ifcopenshell.api.geometry.assign_representation(
|
ifcopenshell.api.geometry.assign_representation(
|
||||||
tool.Ifc.get(), product=element, representation=representation
|
tool.Ifc.get(), product=element, representation=representation
|
||||||
)
|
)
|
||||||
bonsai.core.geometry.switch_representation(
|
|
||||||
tool.Ifc,
|
bonsai.core.geometry.switch_representation(
|
||||||
tool.Geometry,
|
tool.Ifc,
|
||||||
obj=obj,
|
tool.Geometry,
|
||||||
representation=representation,
|
obj=obj,
|
||||||
)
|
representation=representation,
|
||||||
|
)
|
||||||
|
|
||||||
def update_extrusion_direction(
|
def update_extrusion_direction(
|
||||||
element: ifcopenshell.entity_instance, new_direction_ratios: tuple, obj: bpy.types.Object = None
|
element: ifcopenshell.entity_instance, new_direction_ratios: tuple, obj: bpy.types.Object = None
|
||||||
|
|||||||
@@ -1024,7 +1024,7 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
elif material.is_a("IfcMaterialLayerSetUsage"):
|
elif material.is_a("IfcMaterialLayerSetUsage"):
|
||||||
usage = material
|
usage = material
|
||||||
layer_set = material.ForLayerSet
|
layer_set = material.ForLayerSet
|
||||||
offset = usage.OffsetFromReferenceLine
|
offset = usage.OffsetFromReferenceLine * cls.unit_scale
|
||||||
sense_factor = 1 if usage.DirectionSense == "POSITIVE" else -1
|
sense_factor = 1 if usage.DirectionSense == "POSITIVE" else -1
|
||||||
elif material.is_a("IfcMaterialLayerSet"):
|
elif material.is_a("IfcMaterialLayerSet"):
|
||||||
usage = None
|
usage = None
|
||||||
@@ -1037,17 +1037,11 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
if len(layer_set.MaterialLayers) == 1:
|
if len(layer_set.MaterialLayers) == 1:
|
||||||
return mesh
|
return mesh
|
||||||
|
|
||||||
# Get mesh bounds
|
|
||||||
if len(mesh.vertices) > 0:
|
|
||||||
z_coords = [v.co.z for v in mesh.vertices]
|
|
||||||
mesh_z_min = min(z_coords)
|
|
||||||
mesh_z_max = max(z_coords)
|
|
||||||
|
|
||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
bm.from_mesh(mesh)
|
bm.from_mesh(mesh)
|
||||||
|
|
||||||
prev_co = None
|
prev_co = None
|
||||||
advance_direction = None
|
advance_direction = None # Will store direction to advance planes
|
||||||
|
|
||||||
if not usage:
|
if not usage:
|
||||||
sense_factor = 1
|
sense_factor = 1
|
||||||
@@ -1055,7 +1049,9 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
co = Vector((0.0, 0.0, offset))
|
co = Vector((0.0, 0.0, offset))
|
||||||
advance_direction = no
|
advance_direction = no
|
||||||
elif usage.LayerSetDirection == "AXIS2":
|
elif usage.LayerSetDirection == "AXIS2":
|
||||||
# Get local extrusion direction
|
co = Vector((0.0, offset, 0.0))
|
||||||
|
|
||||||
|
# Get LOCAL extrusion direction
|
||||||
local_extrusion = Vector([0.0, 0.0, 1.0])
|
local_extrusion = Vector([0.0, 0.0, 1.0])
|
||||||
if body := ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW"):
|
if body := ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW"):
|
||||||
for item in ifcopenshell.util.representation.resolve_representation(body).Items:
|
for item in ifcopenshell.util.representation.resolve_representation(body).Items:
|
||||||
@@ -1067,58 +1063,17 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
|
|
||||||
# Thickness direction: perpendicular to extrusion and length
|
# Thickness direction: perpendicular to extrusion and length
|
||||||
thickness_dir = local_extrusion.cross(Vector([1.0, 0.0, 0.0])).normalized()
|
thickness_dir = local_extrusion.cross(Vector([1.0, 0.0, 0.0])).normalized()
|
||||||
|
|
||||||
|
# Ensure it points in POSITIVE Y (through wall thickness, not backwards)
|
||||||
if thickness_dir.y < 0:
|
if thickness_dir.y < 0:
|
||||||
thickness_dir = -thickness_dir
|
thickness_dir = -thickness_dir
|
||||||
|
|
||||||
no = thickness_dir
|
no = thickness_dir
|
||||||
|
|
||||||
# Find start point by projecting vertices onto thickness direction
|
|
||||||
if len(mesh.vertices) > 0:
|
|
||||||
projections = [Vector(v.co).dot(no) for v in mesh.vertices]
|
|
||||||
min_proj = min(projections)
|
|
||||||
max_proj = max(projections)
|
|
||||||
|
|
||||||
centroid = sum((Vector(v.co) for v in mesh.vertices), Vector()) / len(mesh.vertices)
|
|
||||||
centroid_proj = centroid.dot(no)
|
|
||||||
|
|
||||||
if sense_factor == 1:
|
|
||||||
start_proj = min_proj
|
|
||||||
else:
|
|
||||||
start_proj = max_proj
|
|
||||||
|
|
||||||
offset_dist = start_proj - centroid_proj
|
|
||||||
co = centroid + no * offset_dist
|
|
||||||
|
|
||||||
actual_mesh_height = max_proj - min_proj
|
|
||||||
else:
|
|
||||||
co = Vector((0.0, 0.0, 0.0))
|
|
||||||
|
|
||||||
advance_direction = thickness_dir
|
advance_direction = thickness_dir
|
||||||
elif usage.LayerSetDirection == "AXIS3":
|
elif usage.LayerSetDirection == "AXIS3":
|
||||||
# AXIS3 layers go through slab thickness (local Z)
|
co = Vector((0.0, 0.0, offset))
|
||||||
|
no = cls.get_extrusion_vector(element).normalized()
|
||||||
no = Vector([0.0, 0.0, 1.0])
|
no = Vector([0.0, 0.0, 1.0])
|
||||||
|
|
||||||
# Find start point by projecting vertices onto Z direction
|
|
||||||
if len(mesh.vertices) > 0:
|
|
||||||
projections = [Vector(v.co).dot(no) for v in mesh.vertices]
|
|
||||||
min_proj = min(projections)
|
|
||||||
max_proj = max(projections)
|
|
||||||
|
|
||||||
centroid = sum((Vector(v.co) for v in mesh.vertices), Vector()) / len(mesh.vertices)
|
|
||||||
centroid_proj = centroid.dot(no)
|
|
||||||
|
|
||||||
if sense_factor == 1:
|
|
||||||
start_proj = min_proj
|
|
||||||
else:
|
|
||||||
start_proj = max_proj
|
|
||||||
|
|
||||||
offset = start_proj - centroid_proj
|
|
||||||
co = centroid + no * offset
|
|
||||||
|
|
||||||
actual_mesh_height = max_proj - min_proj
|
|
||||||
else:
|
|
||||||
co = Vector((0.0, 0.0, 0.0))
|
|
||||||
|
|
||||||
advance_direction = no
|
advance_direction = no
|
||||||
elif usage.LayerSetDirection == "AXIS1":
|
elif usage.LayerSetDirection == "AXIS1":
|
||||||
co = Vector((0.0, 0.0, offset))
|
co = Vector((0.0, 0.0, offset))
|
||||||
@@ -1126,27 +1081,10 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
no = Vector([1.0, 0.0, 0.0])
|
no = Vector([1.0, 0.0, 0.0])
|
||||||
advance_direction = no
|
advance_direction = no
|
||||||
|
|
||||||
# Apply DirectionSense
|
no *= sense_factor
|
||||||
if usage and usage.LayerSetDirection == "AXIS2":
|
advance_direction *= sense_factor
|
||||||
if sense_factor == -1:
|
|
||||||
advance_direction = -advance_direction
|
|
||||||
test_normal = -no
|
|
||||||
else:
|
|
||||||
test_normal = no
|
|
||||||
elif usage and usage.LayerSetDirection == "AXIS1":
|
|
||||||
no = no * sense_factor
|
|
||||||
advance_direction = advance_direction * sense_factor
|
|
||||||
test_normal = no
|
|
||||||
elif usage and usage.LayerSetDirection == "AXIS3":
|
|
||||||
if sense_factor == -1:
|
|
||||||
advance_direction = -advance_direction
|
|
||||||
test_normal = -no
|
|
||||||
else:
|
|
||||||
test_normal = no
|
|
||||||
else:
|
|
||||||
test_normal = no
|
|
||||||
|
|
||||||
# Cache material styles
|
# Cache this
|
||||||
body = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
body = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||||
styles = {}
|
styles = {}
|
||||||
has_layer_styles = False
|
has_layer_styles = False
|
||||||
@@ -1154,23 +1092,12 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
if style := tool.Ifc.get_entity(material):
|
if style := tool.Ifc.get_entity(material):
|
||||||
styles[style] = i
|
styles[style] = i
|
||||||
|
|
||||||
layer_list = list(enumerate(layer_set.MaterialLayers))
|
|
||||||
|
|
||||||
# Calculate scale factor
|
|
||||||
total_layer_thickness = sum(layer.LayerThickness for _, layer in layer_list)
|
|
||||||
|
|
||||||
if "actual_mesh_height" not in locals():
|
|
||||||
actual_mesh_height = mesh_z_max - mesh_z_min if len(mesh.vertices) > 0 else total_layer_thickness
|
|
||||||
|
|
||||||
thickness_scale = actual_mesh_height / total_layer_thickness if total_layer_thickness > 0 else 1.0
|
|
||||||
|
|
||||||
last_i = len(layer_set.MaterialLayers) - 1
|
last_i = len(layer_set.MaterialLayers) - 1
|
||||||
|
for i, layer in enumerate(layer_set.MaterialLayers):
|
||||||
for idx, (original_i, layer) in enumerate(layer_list):
|
if i != last_i:
|
||||||
if idx != last_i:
|
|
||||||
prev_co = co.copy()
|
prev_co = co.copy()
|
||||||
advance_vector = advance_direction * layer.LayerThickness * thickness_scale
|
# Use advance_direction (not no) to move planes!
|
||||||
co += advance_vector
|
co += advance_direction * layer.LayerThickness * cls.unit_scale
|
||||||
|
|
||||||
bisect_geom = bmesh.ops.bisect_plane(
|
bisect_geom = bmesh.ops.bisect_plane(
|
||||||
bm, geom=bm.verts[:] + bm.edges[:] + bm.faces[:], dist=0.0001, plane_co=co, plane_no=no
|
bm, geom=bm.verts[:] + bm.edges[:] + bm.faces[:], dist=0.0001, plane_co=co, plane_no=no
|
||||||
@@ -1183,18 +1110,18 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
material_index = len(mesh.materials)
|
material_index = len(mesh.materials)
|
||||||
mesh.materials.append(tool.Ifc.get_object(style))
|
mesh.materials.append(tool.Ifc.get_object(style))
|
||||||
|
|
||||||
if idx == last_i:
|
if i == last_i:
|
||||||
for face in bisect_geom["geom"]:
|
for face in bisect_geom["geom"]:
|
||||||
if isinstance(face, bmesh.types.BMFace):
|
if isinstance(face, bmesh.types.BMFace):
|
||||||
center = face.calc_center_median()
|
center = face.calc_center_median()
|
||||||
if (center - co).dot(test_normal) >= 0:
|
if (center - co).dot(no) >= 0:
|
||||||
face.material_index = material_index
|
face.material_index = material_index
|
||||||
has_layer_styles = True
|
has_layer_styles = True
|
||||||
else:
|
else:
|
||||||
for face in bisect_geom["geom"]:
|
for face in bisect_geom["geom"]:
|
||||||
if isinstance(face, bmesh.types.BMFace):
|
if isinstance(face, bmesh.types.BMFace):
|
||||||
center = face.calc_center_median()
|
center = face.calc_center_median()
|
||||||
if (center - co).dot(test_normal) < 0 and (center - prev_co).dot(test_normal) >= 0:
|
if (center - co).dot(no) < 0 and (center - prev_co).dot(no) >= 0:
|
||||||
face.material_index = material_index
|
face.material_index = material_index
|
||||||
has_layer_styles = True
|
has_layer_styles = True
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user