mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-16 21:42:19 +00:00
See #5972. Boolean operands are now loaded in item editing mode and are editable.
This commit is contained in:
@@ -36,15 +36,18 @@ class ItemDecorator:
|
|||||||
if cls.is_installed:
|
if cls.is_installed:
|
||||||
cls.uninstall()
|
cls.uninstall()
|
||||||
obj_is_selected = {}
|
obj_is_selected = {}
|
||||||
|
obj_is_boolean = {}
|
||||||
objs = {}
|
objs = {}
|
||||||
for item_obj in context.scene.BIMGeometryProperties.item_objs:
|
for item_obj in context.scene.BIMGeometryProperties.item_objs:
|
||||||
if obj := item_obj.obj:
|
if obj := item_obj.obj:
|
||||||
objs[obj.name] = cls.get_obj_data(obj)
|
objs[obj.name] = cls.get_obj_data(obj)
|
||||||
obj_is_selected[obj.name] = obj.select_get()
|
obj_is_selected[obj.name] = obj.select_get()
|
||||||
|
obj_is_boolean[obj.name] = item_obj.is_boolean
|
||||||
|
|
||||||
handler = cls()
|
handler = cls()
|
||||||
handler.objs = objs
|
handler.objs = objs
|
||||||
handler.obj_is_selected = obj_is_selected
|
handler.obj_is_selected = obj_is_selected
|
||||||
|
handler.obj_is_boolean = obj_is_boolean
|
||||||
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_text, (context,), "WINDOW", "POST_PIXEL"))
|
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_text, (context,), "WINDOW", "POST_PIXEL"))
|
||||||
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw, (context,), "WINDOW", "POST_VIEW"))
|
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw, (context,), "WINDOW", "POST_VIEW"))
|
||||||
cls.is_installed = True
|
cls.is_installed = True
|
||||||
@@ -138,7 +141,7 @@ class ItemDecorator:
|
|||||||
self.line_shader.bind() # required to be able to change uniforms of the shader
|
self.line_shader.bind() # required to be able to change uniforms of the shader
|
||||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||||
self.line_shader.uniform_float("lineWidth", 1.0)
|
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||||
|
|
||||||
# general shader
|
# general shader
|
||||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||||
@@ -154,6 +157,9 @@ class ItemDecorator:
|
|||||||
continue
|
continue
|
||||||
self.draw_batch("LINES", data["verts"], selected_elements_color, data["edges"])
|
self.draw_batch("LINES", data["verts"], selected_elements_color, data["edges"])
|
||||||
self.draw_batch("TRIS", data["verts"], transparent_color(selected_elements_color), data["tris"])
|
self.draw_batch("TRIS", data["verts"], transparent_color(selected_elements_color), data["tris"])
|
||||||
else:
|
elif self.obj_is_boolean[obj_name]:
|
||||||
self.draw_batch("LINES", data["verts"], decorator_color_background, data["edges"])
|
self.draw_batch("LINES", data["verts"], special_elements_color, data["edges"])
|
||||||
|
self.draw_batch("TRIS", data["verts"], transparent_color(special_elements_color), data["tris"])
|
||||||
|
else:
|
||||||
|
self.draw_batch("LINES", data["verts"], transparent_color(unselected_elements_color, alpha=0.2), data["edges"])
|
||||||
self.draw_batch("TRIS", data["verts"], transparent_color(special_elements_color), data["tris"])
|
self.draw_batch("TRIS", data["verts"], transparent_color(special_elements_color), data["tris"])
|
||||||
|
|||||||
@@ -2694,8 +2694,19 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
data = obj.data
|
data = obj.data
|
||||||
assert data
|
assert data
|
||||||
item_ids = data["ios_item_ids"] if "ios_item_ids" in data else data["ios_edges_item_ids"]
|
item_ids = data["ios_item_ids"] if "ios_item_ids" in data else data["ios_edges_item_ids"]
|
||||||
for item_id in set(item_ids):
|
queue = list(set(item_ids))
|
||||||
|
processed_ids = set()
|
||||||
|
boolean_ids = set()
|
||||||
|
while queue:
|
||||||
|
item_id = queue.pop()
|
||||||
|
if item_id in processed_ids:
|
||||||
|
continue # In theory, an item can be used multiple times (e.g. in a boolean stack).
|
||||||
item = tool.Ifc.get().by_id(item_id)
|
item = tool.Ifc.get().by_id(item_id)
|
||||||
|
if item.is_a("IfcBooleanResult"):
|
||||||
|
queue.append(item.FirstOperand.id())
|
||||||
|
queue.append(item.SecondOperand.id())
|
||||||
|
boolean_ids.add(item.SecondOperand.id())
|
||||||
|
continue
|
||||||
item_mesh = bpy.data.meshes.new(f"Item/{item.is_a()}/{item_id}")
|
item_mesh = bpy.data.meshes.new(f"Item/{item.is_a()}/{item_id}")
|
||||||
item_mesh.BIMMeshProperties.ifc_definition_id = item_id
|
item_mesh.BIMMeshProperties.ifc_definition_id = item_id
|
||||||
|
|
||||||
@@ -2705,6 +2716,7 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
new = props.item_objs.add()
|
new = props.item_objs.add()
|
||||||
new.obj = item_obj
|
new.obj = item_obj
|
||||||
|
new.is_boolean = item_id in boolean_ids
|
||||||
|
|
||||||
tool.Geometry.import_item(item_obj)
|
tool.Geometry.import_item(item_obj)
|
||||||
tool.Geometry.import_item_attributes(item_obj)
|
tool.Geometry.import_item_attributes(item_obj)
|
||||||
@@ -2712,11 +2724,9 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
if not tool.Geometry.is_movable(item):
|
if not tool.Geometry.is_movable(item):
|
||||||
tool.Geometry.lock_object(item_obj)
|
tool.Geometry.lock_object(item_obj)
|
||||||
|
|
||||||
if "IfcOpeningElement" in obj.name:
|
|
||||||
item_obj.display_type = "WIRE"
|
|
||||||
|
|
||||||
item_obj.select_set(True) # so you can quickly hit tab again, for edit mode
|
item_obj.select_set(True) # so you can quickly hit tab again, for edit mode
|
||||||
context.view_layer.objects.active = item_obj
|
context.view_layer.objects.active = item_obj
|
||||||
|
processed_ids.add(item_id)
|
||||||
|
|
||||||
tool.Root.reload_item_decorator()
|
tool.Root.reload_item_decorator()
|
||||||
|
|
||||||
|
|||||||
@@ -131,10 +131,7 @@ class RepresentationItemObject(PropertyGroup):
|
|||||||
name: StringProperty(name="Name")
|
name: StringProperty(name="Name")
|
||||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
obj: PointerProperty(type=bpy.types.Object)
|
obj: PointerProperty(type=bpy.types.Object)
|
||||||
verts: StringProperty(name="Verts")
|
is_boolean: BoolProperty(name="Is Boolean", default=False)
|
||||||
edges: StringProperty(name="Edges")
|
|
||||||
special_verts: StringProperty(name="Special Verts")
|
|
||||||
special_edges: StringProperty(name="Special Edges")
|
|
||||||
|
|
||||||
|
|
||||||
class ShapeAspect(PropertyGroup):
|
class ShapeAspect(PropertyGroup):
|
||||||
|
|||||||
@@ -952,7 +952,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_movable(cls, item: ifcopenshell.entity_instance) -> bool:
|
def is_movable(cls, item: ifcopenshell.entity_instance) -> bool:
|
||||||
return item.is_a("IfcSweptAreaSolid")
|
return item.is_a("IfcSweptAreaSolid") or item.is_a("IfcHalfSpaceSolid")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_profile_based(cls, data: bpy.types.Mesh) -> bool:
|
def is_profile_based(cls, data: bpy.types.Mesh) -> bool:
|
||||||
@@ -1571,12 +1571,10 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
has_changed = False
|
has_changed = False
|
||||||
|
|
||||||
for item_obj in props.item_objs:
|
for item_obj in props.item_objs:
|
||||||
if not (obj := item_obj.obj):
|
if not (obj := item_obj.obj) or not tool.Ifc.is_moved(obj):
|
||||||
continue
|
continue
|
||||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||||
if is_swept_area := item.is_a("IfcSweptAreaSolid"):
|
if is_swept_area := item.is_a("IfcSweptAreaSolid"):
|
||||||
if not tool.Ifc.is_moved(obj):
|
|
||||||
continue
|
|
||||||
has_changed = True
|
has_changed = True
|
||||||
old_position = item.Position
|
old_position = item.Position
|
||||||
|
|
||||||
@@ -1591,6 +1589,25 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
item.Position = builder.create_axis2_placement_3d_from_matrix(position)
|
item.Position = builder.create_axis2_placement_3d_from_matrix(position)
|
||||||
if old_position:
|
if old_position:
|
||||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position)
|
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position)
|
||||||
|
if item.is_a("IfcHalfSpaceSolid"):
|
||||||
|
has_changed = True
|
||||||
|
surface = item.BaseSurface
|
||||||
|
if surface.is_a("IfcPlane"):
|
||||||
|
position = surface.Position
|
||||||
|
m = Matrix(ifcopenshell.util.placement.get_axis2placement(position).tolist())
|
||||||
|
m.translation *= unit_scale
|
||||||
|
|
||||||
|
new_m = rep_obj.matrix_world.inverted() @ obj.matrix_world
|
||||||
|
new_m.normalize()
|
||||||
|
new_m.translation /= unit_scale
|
||||||
|
new_m = np.array(new_m)
|
||||||
|
surface.Position = builder.create_axis2_placement_3d_from_matrix(new_m)
|
||||||
|
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), position)
|
||||||
|
else:
|
||||||
|
self.report(
|
||||||
|
{"INFO"},
|
||||||
|
f"Editing boolean using non-IfcPlane IfcSurface ({surface.is_a()}) is not supported.",
|
||||||
|
)
|
||||||
|
|
||||||
if has_changed:
|
if has_changed:
|
||||||
cls.reload_representation(rep_obj)
|
cls.reload_representation(rep_obj)
|
||||||
@@ -1631,21 +1648,36 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||||
obj.data.clear_geometry()
|
obj.data.clear_geometry()
|
||||||
|
|
||||||
geometry = tool.Loader.create_generic_shape(item)
|
if item.is_a("IfcHalfSpaceSolid"):
|
||||||
if (cartesian_point_offset := cls.get_cartesian_point_offset(rep_obj)) is not None:
|
bm = bmesh.new()
|
||||||
verts_array = np.array(geometry.verts)
|
bmesh.ops.create_grid(bm, size=0.5)
|
||||||
offset = np.array([-cartesian_point_offset[0], -cartesian_point_offset[1], -cartesian_point_offset[2]])
|
bm.verts.ensure_lookup_table()
|
||||||
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
|
bm.edges.ensure_lookup_table()
|
||||||
verts = offset_verts.tolist()
|
bm.faces.ensure_lookup_table()
|
||||||
|
bm.to_mesh(obj.data)
|
||||||
|
bm.free()
|
||||||
|
|
||||||
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
|
position = item.BaseSurface.Position
|
||||||
|
position = Matrix(ifcopenshell.util.placement.get_axis2placement(position).tolist())
|
||||||
|
position.translation *= unit_scale
|
||||||
|
obj.matrix_world = rep_obj.matrix_world @ position
|
||||||
else:
|
else:
|
||||||
verts = geometry.verts
|
geometry = tool.Loader.create_generic_shape(item)
|
||||||
tool.Loader.convert_geometry_to_mesh(geometry, obj.data, verts=verts)
|
if (cartesian_point_offset := cls.get_cartesian_point_offset(rep_obj)) is not None:
|
||||||
|
verts_array = np.array(geometry.verts)
|
||||||
|
offset = np.array([-cartesian_point_offset[0], -cartesian_point_offset[1], -cartesian_point_offset[2]])
|
||||||
|
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
|
||||||
|
verts = offset_verts.tolist()
|
||||||
|
else:
|
||||||
|
verts = geometry.verts
|
||||||
|
tool.Loader.convert_geometry_to_mesh(geometry, obj.data, verts=verts)
|
||||||
|
|
||||||
if ios_materials := list(obj.data["ios_materials"]):
|
if ios_materials := list(obj.data["ios_materials"]):
|
||||||
material = tool.Ifc.get_object(tool.Ifc.get().by_id(ios_materials[0]))
|
material = tool.Ifc.get_object(tool.Ifc.get().by_id(ios_materials[0]))
|
||||||
obj.data.materials.append(material)
|
obj.data.materials.append(material)
|
||||||
|
|
||||||
obj.matrix_world = rep_obj.matrix_world.copy()
|
obj.matrix_world = rep_obj.matrix_world.copy()
|
||||||
|
|
||||||
if is_swept_area := item.is_a("IfcSweptAreaSolid"):
|
if is_swept_area := item.is_a("IfcSweptAreaSolid"):
|
||||||
position = item.Position
|
position = item.Position
|
||||||
|
|||||||
Reference in New Issue
Block a user