mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-28 11:18:18 +00:00
Plan view fixes (#7022)
This commit is contained in:
@@ -241,6 +241,7 @@ class DecoratorData:
|
|||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
cls.cut_cache = {}
|
cls.cut_cache = {}
|
||||||
cls.layerset_cache = {}
|
cls.layerset_cache = {}
|
||||||
|
cls.fill_cache = {}
|
||||||
|
|
||||||
text = {}
|
text = {}
|
||||||
dimension = {}
|
dimension = {}
|
||||||
|
|||||||
@@ -465,6 +465,12 @@ class DocProperties(PropertyGroup):
|
|||||||
return None
|
return None
|
||||||
return tool.Ifc.get().by_id(drawing_id)
|
return tool.Ifc.get().by_id(drawing_id)
|
||||||
|
|
||||||
|
def get_active_target_view(self) -> Union[str, None]:
|
||||||
|
active_drawing = self.get_active_drawing()
|
||||||
|
if not active_drawing:
|
||||||
|
return None
|
||||||
|
return tool.Drawing.get_drawing_target_view(active_drawing)
|
||||||
|
|
||||||
|
|
||||||
def update_width_height(self: "BIMCameraProperties", context: bpy.types.Context) -> None:
|
def update_width_height(self: "BIMCameraProperties", context: bpy.types.Context) -> None:
|
||||||
self.update_camera_resolution()
|
self.update_camera_resolution()
|
||||||
|
|||||||
@@ -557,7 +557,36 @@ def get_generic_product_preview_data(context, relating_type):
|
|||||||
|
|
||||||
mouse_point.z = snap_obj.matrix_world.translation.z
|
mouse_point.z = snap_obj.matrix_world.translation.z
|
||||||
|
|
||||||
|
if snap_element and (container := ifcopenshell.util.element.get_container(snap_element)):
|
||||||
|
container_obj = tool.Ifc.get_object(container)
|
||||||
|
mouse_point.z = container_obj.location.z
|
||||||
|
|
||||||
obj_type = tool.Ifc.get_object(relating_type)
|
obj_type = tool.Ifc.get_object(relating_type)
|
||||||
|
|
||||||
|
subcontexts = tool.Drawing.get_active_drawing_subcontexts()
|
||||||
|
if not subcontexts:
|
||||||
|
subcontexts = [("Model", "Body", "MODEL_VIEW")]
|
||||||
|
|
||||||
|
active_context = tool.Geometry.get_active_representation_context(obj_type)
|
||||||
|
active_context_params = tool.Geometry.get_subcontext_parameters(active_context)
|
||||||
|
for subcontext in subcontexts:
|
||||||
|
if subcontext == active_context_params:
|
||||||
|
break
|
||||||
|
|
||||||
|
representation = ifcopenshell.util.representation.get_representation(relating_type, *subcontext)
|
||||||
|
if representation:
|
||||||
|
bonsai.core.geometry.switch_representation(
|
||||||
|
tool.Ifc,
|
||||||
|
tool.Geometry,
|
||||||
|
obj_type,
|
||||||
|
representation,
|
||||||
|
should_reload=True,
|
||||||
|
is_global=False,
|
||||||
|
should_sync_changes_first=False,
|
||||||
|
)
|
||||||
|
context.view_layer.update()
|
||||||
|
break
|
||||||
|
|
||||||
if obj_type.data:
|
if obj_type.data:
|
||||||
data = ItemDecorator.get_obj_data(obj_type)
|
data = ItemDecorator.get_obj_data(obj_type)
|
||||||
data["verts"] = [tuple(obj_type.matrix_world.inverted() @ Vector(v)) for v in data["verts"]]
|
data["verts"] = [tuple(obj_type.matrix_world.inverted() @ Vector(v)) for v in data["verts"]]
|
||||||
|
|||||||
@@ -327,9 +327,21 @@ class AddOccurrence(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
if self.from_invoke and str(self.relating_type_id) in AuthoringData.data["relating_type_id"]:
|
if self.from_invoke and str(self.relating_type_id) in AuthoringData.data["relating_type_id"]:
|
||||||
props.relating_type_id = str(self.relating_type_id)
|
props.relating_type_id = str(self.relating_type_id)
|
||||||
|
|
||||||
|
building_obj = None
|
||||||
|
if len(context.selected_objects) == 1 and context.active_object:
|
||||||
|
building_obj = context.active_object
|
||||||
|
building_element = tool.Ifc.get_entity(building_obj)
|
||||||
|
|
||||||
self.container = None
|
self.container = None
|
||||||
self.container_obj = None
|
self.container_obj = None
|
||||||
if container := tool.Root.get_default_container():
|
if (
|
||||||
|
building_obj
|
||||||
|
and building_element
|
||||||
|
and (container := ifcopenshell.util.element.get_container(building_element))
|
||||||
|
):
|
||||||
|
self.container = container
|
||||||
|
self.container_obj = tool.Ifc.get_object(container)
|
||||||
|
elif container := tool.Root.get_default_container():
|
||||||
self.container = container
|
self.container = container
|
||||||
self.container_obj = tool.Ifc.get_object(container)
|
self.container_obj = tool.Ifc.get_object(container)
|
||||||
|
|
||||||
@@ -416,11 +428,6 @@ class AddOccurrence(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
building_obj = None
|
|
||||||
if len(context.selected_objects) == 1 and context.active_object:
|
|
||||||
building_obj = context.active_object
|
|
||||||
building_element = tool.Ifc.get_entity(building_obj)
|
|
||||||
|
|
||||||
mesh = bpy.data.meshes.new(name="Instance")
|
mesh = bpy.data.meshes.new(name="Instance")
|
||||||
obj = bpy.data.objects.new(tool.Model.generate_occurrence_name(relating_type, instance_class), mesh)
|
obj = bpy.data.objects.new(tool.Model.generate_occurrence_name(relating_type, instance_class), mesh)
|
||||||
|
|
||||||
@@ -487,12 +494,15 @@ class AddOccurrence(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
building_obj
|
building_obj
|
||||||
and building_element
|
and building_element
|
||||||
and building_element.is_a() in ["IfcWall", "IfcWallStandardCase", "IfcCovering", "IfcElementAssembly"]
|
and building_element.is_a() in ["IfcWall", "IfcWallStandardCase", "IfcCovering", "IfcElementAssembly"]
|
||||||
|
and instance_class in ["IfcWindow", "IfcDoor"]
|
||||||
):
|
):
|
||||||
if instance_class in ["IfcWindow", "IfcDoor"]:
|
# TODO For now we are hardcoding windows and doors as a prototype
|
||||||
# TODO For now we are hardcoding windows and doors as a prototype
|
tool.Model.add_filled_opening(building_obj, obj)
|
||||||
tool.Model.add_filled_opening(building_obj, obj)
|
|
||||||
else:
|
else:
|
||||||
if self.container_obj:
|
if self.container_obj:
|
||||||
|
bonsai.core.spatial.assign_container(
|
||||||
|
tool.Ifc, tool.Collector, tool.Spatial, container=self.container, element_obj=obj
|
||||||
|
)
|
||||||
if props.rl_mode == "BOTTOM":
|
if props.rl_mode == "BOTTOM":
|
||||||
obj.location.z = self.container_obj.location.z - tool.Blender.get_object_bounding_box(obj)["min_z"]
|
obj.location.z = self.container_obj.location.z - tool.Blender.get_object_bounding_box(obj)["min_z"]
|
||||||
elif props.rl_mode == "CONTAINER":
|
elif props.rl_mode == "CONTAINER":
|
||||||
|
|||||||
@@ -2123,22 +2123,9 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
layer_collection2.hide_viewport = False
|
layer_collection2.hide_viewport = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def activate_drawing(cls, camera: bpy.types.Object) -> None:
|
def get_drawing_context_filters(cls, target_view: str) -> list[tuple[str, str, str]]:
|
||||||
selected_objects_before = bpy.context.selected_objects
|
|
||||||
non_ifc_objects_hide = {o: o.hide_get() for o in bpy.context.view_layer.objects if not tool.Ifc.get_entity(o)}
|
|
||||||
|
|
||||||
# Sync viewport objects visibility with selectors from EPset_Drawing/Include and /Exclude
|
|
||||||
drawing = tool.Ifc.get_entity(camera)
|
|
||||||
assert drawing and isinstance(camera.data, bpy.types.Camera)
|
|
||||||
|
|
||||||
filtered_elements = cls.get_drawing_elements(drawing) | cls.get_drawing_spaces(drawing)
|
|
||||||
filtered_elements.add(drawing)
|
|
||||||
|
|
||||||
subcontexts: list[ifcopenshell.entity_instance] = []
|
|
||||||
target_view = cls.get_drawing_target_view(drawing)
|
|
||||||
|
|
||||||
if target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW"):
|
if target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW"):
|
||||||
context_filters = [
|
return [
|
||||||
("Plan", "Body", target_view),
|
("Plan", "Body", target_view),
|
||||||
("Plan", "Body", "MODEL_VIEW"),
|
("Plan", "Body", "MODEL_VIEW"),
|
||||||
("Plan", "Facetation", target_view),
|
("Plan", "Facetation", target_view),
|
||||||
@@ -2153,7 +2140,7 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
("Model", "Annotation", "MODEL_VIEW"),
|
("Model", "Annotation", "MODEL_VIEW"),
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
context_filters = [
|
return [
|
||||||
("Model", "Body", target_view),
|
("Model", "Body", target_view),
|
||||||
("Model", "Body", "MODEL_VIEW"),
|
("Model", "Body", "MODEL_VIEW"),
|
||||||
("Model", "Facetation", target_view),
|
("Model", "Facetation", target_view),
|
||||||
@@ -2162,11 +2149,42 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
("Model", "Annotation", "MODEL_VIEW"),
|
("Model", "Annotation", "MODEL_VIEW"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_drawing_subcontexts(cls, target_view: str) -> list[tuple[str, str, str]]:
|
||||||
|
context_filters = cls.get_drawing_context_filters(target_view)
|
||||||
|
subcontexts = []
|
||||||
|
|
||||||
for context_filter in context_filters:
|
for context_filter in context_filters:
|
||||||
subcontext = ifcopenshell.util.representation.get_context(tool.Ifc.get(), *context_filter)
|
subcontext = ifcopenshell.util.representation.get_context(tool.Ifc.get(), *context_filter)
|
||||||
if subcontext:
|
if subcontext:
|
||||||
subcontexts.append(context_filter)
|
subcontexts.append(context_filter)
|
||||||
|
|
||||||
|
return subcontexts
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_active_drawing_subcontexts(cls) -> list[tuple[str, str, str]] | None:
|
||||||
|
props = cls.get_document_props()
|
||||||
|
target_view = props.get_active_target_view()
|
||||||
|
if not target_view:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return cls.get_drawing_subcontexts(target_view)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def activate_drawing(cls, camera: bpy.types.Object) -> None:
|
||||||
|
selected_objects_before = bpy.context.selected_objects
|
||||||
|
non_ifc_objects_hide = {o: o.hide_get() for o in bpy.context.view_layer.objects if not tool.Ifc.get_entity(o)}
|
||||||
|
|
||||||
|
# Sync viewport objects visibility with selectors from EPset_Drawing/Include and /Exclude
|
||||||
|
drawing = tool.Ifc.get_entity(camera)
|
||||||
|
assert drawing and isinstance(camera.data, bpy.types.Camera)
|
||||||
|
|
||||||
|
filtered_elements = cls.get_drawing_elements(drawing) | cls.get_drawing_spaces(drawing)
|
||||||
|
filtered_elements.add(drawing)
|
||||||
|
|
||||||
|
target_view = cls.get_drawing_target_view(drawing)
|
||||||
|
subcontexts = cls.get_drawing_subcontexts(target_view)
|
||||||
|
|
||||||
# Hide everything first, then selectively show. This is significantly faster.
|
# Hide everything first, then selectively show. This is significantly faster.
|
||||||
with bpy.context.temp_override(**tool.Blender.get_viewport_context()):
|
with bpy.context.temp_override(**tool.Blender.get_viewport_context()):
|
||||||
bpy.ops.object.hide_view_set(unselected=False)
|
bpy.ops.object.hide_view_set(unselected=False)
|
||||||
|
|||||||
@@ -18,10 +18,13 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import bpy
|
import bpy
|
||||||
|
import bmesh
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
import bonsai.core.tool
|
import bonsai.core.tool
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
|
from bonsai.bim.module.drawing.data import DecoratorData
|
||||||
|
from bonsai.bim.module.drawing.decoration import CutDecorator
|
||||||
from bonsai.bim.module.model.decorator import PolylineDecorator
|
from bonsai.bim.module.model.decorator import PolylineDecorator
|
||||||
import math
|
import math
|
||||||
import mathutils
|
import mathutils
|
||||||
@@ -428,6 +431,53 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
point["group"] = "Object"
|
point["group"] = "Object"
|
||||||
detected_snaps.append(point)
|
detected_snaps.append(point)
|
||||||
|
|
||||||
|
# snap to cut geometry (e.g. in plan view)
|
||||||
|
if CutDecorator.installed:
|
||||||
|
cut_snaps = []
|
||||||
|
|
||||||
|
model_props = tool.Model.get_model_props()
|
||||||
|
|
||||||
|
for obj in [o for o in context.visible_objects if o.type == "MESH"]:
|
||||||
|
if not (element := tool.Ifc.get_entity(obj)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if model_props.show_cut_decorator and element.id() in DecoratorData.cut_cache:
|
||||||
|
verts, edges = DecoratorData.cut_cache[element.id()]
|
||||||
|
if not verts or not edges:
|
||||||
|
continue
|
||||||
|
|
||||||
|
bm = bmesh.new()
|
||||||
|
bverts = [bm.verts.new(pos) for pos in verts]
|
||||||
|
for edge in edges:
|
||||||
|
bm.edges.new([bverts[vi] for vi in edge])
|
||||||
|
|
||||||
|
snap_points = tool.Raycast.ray_cast_by_proximity(context, event, None, None, bm)
|
||||||
|
if snap_points:
|
||||||
|
for p in snap_points:
|
||||||
|
p["group"] = "Object"
|
||||||
|
p["object"] = obj
|
||||||
|
cut_snaps.append(p)
|
||||||
|
|
||||||
|
if model_props.show_cut_decorator_fill and element.id() in DecoratorData.fill_cache:
|
||||||
|
bm = bmesh.new()
|
||||||
|
for color, verts_and_tris in DecoratorData.fill_cache[element.id()].items():
|
||||||
|
for verts, tris in verts_and_tris:
|
||||||
|
bverts = [bm.verts.new(pos) for pos in verts]
|
||||||
|
for tri in tris:
|
||||||
|
verts = [bverts[vi] for vi in tri]
|
||||||
|
if not bm.faces.get(verts):
|
||||||
|
bm.faces.new(verts)
|
||||||
|
|
||||||
|
snap_points = tool.Raycast.ray_cast_by_proximity(context, event, None, None, bm)
|
||||||
|
if snap_points:
|
||||||
|
for p in snap_points:
|
||||||
|
p["group"] = "Object"
|
||||||
|
p["object"] = obj
|
||||||
|
cut_snaps.append(p)
|
||||||
|
|
||||||
|
if len(cut_snaps) > 0:
|
||||||
|
detected_snaps = cut_snaps
|
||||||
|
|
||||||
# Axis and Plane
|
# Axis and Plane
|
||||||
if tool.Ifc.get():
|
if tool.Ifc.get():
|
||||||
elevation = tool.Root.get_default_container_elevation()
|
elevation = tool.Root.get_default_container_elevation()
|
||||||
|
|||||||
Reference in New Issue
Block a user