mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
Profile extensions now use the new engine and support 3D axis representations
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import copy
|
||||
import math
|
||||
import bmesh
|
||||
import mathutils.geometry
|
||||
@@ -106,7 +107,7 @@ class DumbProfileGenerator:
|
||||
return
|
||||
|
||||
self.body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
self.axis_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Plan", "Axis", "GRAPH_VIEW")
|
||||
self.axis_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Axis", "GRAPH_VIEW")
|
||||
self.collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
self.collection_obj = bpy.data.objects.get(self.collection.name)
|
||||
self.depth = 3
|
||||
@@ -262,15 +263,14 @@ class ExtendProfile(bpy.types.Operator):
|
||||
if not context.active_object:
|
||||
return {"FINISHED"}
|
||||
if len(selected_objs) == 1:
|
||||
DumbProfileExtender(context.active_object, target_coordinate=context.scene.cursor.location).extend()
|
||||
IfcStore.edited_objs.add(context.active_object)
|
||||
DumbProfileExtender(None).join_E(context.active_object, context.scene.cursor.location)
|
||||
return {"FINISHED"}
|
||||
if len(selected_objs) < 2:
|
||||
return {"FINISHED"}
|
||||
for obj in selected_objs:
|
||||
if obj == context.active_object:
|
||||
continue
|
||||
DumbProfileExtender(obj, context.active_object).extend()
|
||||
DumbProfileExtender(obj, context.active_object).extend_old()
|
||||
IfcStore.edited_objs.add(obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -281,12 +281,136 @@ class DumbProfileExtender:
|
||||
self.profile = profile
|
||||
self.target = target
|
||||
self.target_coordinate = target_coordinate
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.axis_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Axis", "GRAPH_VIEW")
|
||||
self.body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
def join_E(self, profile1, target):
|
||||
element1 = tool.Ifc.get_entity(profile1)
|
||||
if not element1:
|
||||
return
|
||||
axis1 = self.get_profile_axis(profile1)
|
||||
intersect, connection = mathutils.geometry.intersect_point_line(target, *axis1)
|
||||
connection = "ATEND" if connection > 0.5 else "ATSTART"
|
||||
|
||||
ifcopenshell.api.run("geometry.disconnect_path", tool.Ifc.get(), element=element1, connection_type=connection)
|
||||
|
||||
axis = copy.deepcopy(axis1)
|
||||
body = copy.deepcopy(axis1)
|
||||
axis[1 if connection == "ATEND" else 0] = intersect
|
||||
body[1 if connection == "ATEND" else 0] = intersect
|
||||
self.recreate_profile(element1, profile1, axis, body)
|
||||
|
||||
def recreate_profile(self, element, obj, axis=None, body=None):
|
||||
if axis is None or body is None:
|
||||
axis = body = self.get_profile_axis(obj)
|
||||
self.axis = copy.deepcopy(axis)
|
||||
self.body = copy.deepcopy(body)
|
||||
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
||||
self.profile = material.CompositeProfile or material.MaterialProfiles[0].Profile
|
||||
height = self.get_height(tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id))
|
||||
self.clippings = []
|
||||
|
||||
for rel in element.ConnectedTo:
|
||||
connection = rel.RelatingConnectionType
|
||||
other = tool.Ifc.get_object(rel.RelatedElement)
|
||||
if connection not in ["ATPATH", "NOTDEFINED"]:
|
||||
self.join(obj, other, connection, is_relating=True)
|
||||
for rel in element.ConnectedFrom:
|
||||
connection = rel.RelatedConnectionType
|
||||
other = tool.Ifc.get_object(rel.RelatingElement)
|
||||
if connection not in ["ATPATH", "NOTDEFINED"]:
|
||||
self.join(obj, other, connection, is_relating=False)
|
||||
|
||||
new_matrix = copy.deepcopy(obj.matrix_world)
|
||||
new_matrix.col[3] = self.body[0].to_4d()
|
||||
new_matrix.invert()
|
||||
self.clippings = [new_matrix @ c for c in self.clippings]
|
||||
|
||||
depth = (self.body[1] - self.body[0]).length
|
||||
|
||||
if self.axis_context:
|
||||
axis = [(new_matrix @ a) for a in self.axis]
|
||||
new_axis = ifcopenshell.api.run(
|
||||
"geometry.add_axis_representation", tool.Ifc.get(), context=self.axis_context, axis=axis
|
||||
)
|
||||
old_axis = ifcopenshell.util.representation.get_representation(element, "Model", "Axis", "GRAPH_VIEW")
|
||||
if old_axis:
|
||||
for inverse in tool.Ifc.get().get_inverse(old_axis):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_axis, new_axis)
|
||||
blenderbim.core.geometry.remove_representation(
|
||||
tool.Ifc, tool.Geometry, obj=obj, representation=old_axis
|
||||
)
|
||||
else:
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=new_axis
|
||||
)
|
||||
|
||||
new_body = ifcopenshell.api.run(
|
||||
"geometry.add_profile_representation",
|
||||
tool.Ifc.get(),
|
||||
context=self.body_context,
|
||||
profile=self.profile,
|
||||
depth=depth,
|
||||
clippings=self.clippings,
|
||||
)
|
||||
|
||||
old_body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if old_body:
|
||||
for inverse in tool.Ifc.get().get_inverse(old_body):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_body, new_body)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = int(new_body.id())
|
||||
obj.data.name = f"{self.body_context.id()}/{new_body.id()}"
|
||||
blenderbim.core.geometry.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_body)
|
||||
else:
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=new_body
|
||||
)
|
||||
|
||||
obj.location[0], obj.location[1], obj.location[2] = self.body[0]
|
||||
bpy.context.view_layer.update()
|
||||
if tool.Ifc.is_moved(obj):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=new_body,
|
||||
should_reload=True,
|
||||
enable_dynamic_voids=False,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
|
||||
def join(self, profile1, profile2, connection, is_relating=True):
|
||||
pass
|
||||
|
||||
def get_profile_axis(self, obj):
|
||||
z_values = [v[2] for v in obj.bound_box]
|
||||
return [
|
||||
(obj.matrix_world @ Vector((0.0, 0.0, min(z_values)))),
|
||||
(obj.matrix_world @ Vector((0.0, 0.0, max(z_values)))),
|
||||
]
|
||||
|
||||
# TODO
|
||||
def get_height(self, representation):
|
||||
height = 3.0
|
||||
item = representation.Items[0]
|
||||
while True:
|
||||
if item.is_a("IfcExtrudedAreaSolid"):
|
||||
height = item.Depth * self.unit_scale
|
||||
break
|
||||
elif item.is_a("IfcBooleanClippingResult"):
|
||||
item = item.FirstOperand
|
||||
else:
|
||||
break
|
||||
return height
|
||||
|
||||
|
||||
# An extension of a profile is determined by casting a ray from the cardinal
|
||||
# point of either extreme along the profiles local Z axis to a target
|
||||
# object. The nearest result from this raycast will be the target extension
|
||||
# point.
|
||||
def extend(self):
|
||||
def extend_old(self):
|
||||
extension_data = self.get_closest_extension_point()
|
||||
if not extension_data:
|
||||
return
|
||||
|
||||
@@ -561,13 +561,7 @@ class DumbWallGenerator:
|
||||
"geometry.add_axis_representation",
|
||||
tool.Ifc.get(),
|
||||
context=self.axis_context,
|
||||
axis=[
|
||||
(
|
||||
0.0,
|
||||
0.0,
|
||||
),
|
||||
(self.length, 0.0),
|
||||
],
|
||||
axis=[(0.0, 0.0), (self.length, 0.0)],
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=representation
|
||||
@@ -715,7 +709,6 @@ class DumbWallJoiner:
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.axis_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Plan", "AXIS", "GRAPH_VIEW")
|
||||
self.body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
self.axis_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Plan", "Axis", "GRAPH_VIEW")
|
||||
|
||||
def unjoin(self, wall1):
|
||||
element1 = tool.Ifc.get_entity(wall1)
|
||||
@@ -803,7 +796,9 @@ class DumbWallJoiner:
|
||||
axis1["reference"][1] = intersect2 if connection2 > connection1 else intersect1
|
||||
|
||||
for connection in changed_connections:
|
||||
ifcopenshell.api.run("geometry.disconnect_path", tool.Ifc.get(), element=element1, connection_type=connection)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.disconnect_path", tool.Ifc.get(), element=element1, connection_type=connection
|
||||
)
|
||||
|
||||
for rel in element2.ConnectedTo:
|
||||
if rel.RelatingConnectionType in changed_connections:
|
||||
@@ -831,7 +826,6 @@ class DumbWallJoiner:
|
||||
self.recreate_wall(element1, wall1, axis1["reference"], axis1["reference"])
|
||||
bpy.data.objects.remove(wall2)
|
||||
|
||||
|
||||
def duplicate_wall(self, wall1):
|
||||
wall2 = wall1.copy()
|
||||
wall2.data = wall2.data.copy()
|
||||
|
||||
Reference in New Issue
Block a user