mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 18:21:59 +00:00
Add decorator for grids outside drawing view
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
|
import bonsai.core.root
|
||||||
from bpy.types import Operator
|
from bpy.types import Operator
|
||||||
from bpy.props import FloatProperty, IntProperty
|
from bpy.props import FloatProperty, IntProperty
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
@@ -28,7 +29,9 @@ def add_object(self, context):
|
|||||||
obj = bpy.data.objects.new("Grid", None)
|
obj = bpy.data.objects.new("Grid", None)
|
||||||
obj.name = "Grid"
|
obj.name = "Grid"
|
||||||
|
|
||||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcGrid")
|
bonsai.core.root.assign_class(
|
||||||
|
tool.Ifc, tool.Collector, tool.Root, obj=obj, ifc_class="IfcGrid", should_add_representation=False
|
||||||
|
)
|
||||||
grid = tool.Ifc.get_entity(obj)
|
grid = tool.Ifc.get_entity(obj)
|
||||||
|
|
||||||
for i in range(0, self.total_u):
|
for i in range(0, self.total_u):
|
||||||
@@ -69,6 +72,8 @@ def add_object(self, context):
|
|||||||
ifcopenshell.api.run("grid.create_axis_curve", tool.Ifc.get(), axis_curve=obj, grid_axis=result)
|
ifcopenshell.api.run("grid.create_axis_curve", tool.Ifc.get(), axis_curve=obj, grid_axis=result)
|
||||||
tool.Collector.assign(obj)
|
tool.Collector.assign(obj)
|
||||||
|
|
||||||
|
tool.Root.reload_grid_decorator()
|
||||||
|
|
||||||
|
|
||||||
class BIM_OT_add_object(Operator, tool.Ifc.Operator):
|
class BIM_OT_add_object(Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "mesh.add_grid"
|
bl_idname = "mesh.add_grid"
|
||||||
|
|||||||
@@ -827,6 +827,7 @@ class LoadProjectElements(bpy.types.Operator):
|
|||||||
tool.Project.load_default_thumbnails()
|
tool.Project.load_default_thumbnails()
|
||||||
tool.Project.set_default_context()
|
tool.Project.set_default_context()
|
||||||
tool.Project.set_default_modeling_dimensions()
|
tool.Project.set_default_modeling_dimensions()
|
||||||
|
tool.Root.reload_grid_decorator()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def get_decomposition_elements(self):
|
def get_decomposition_elements(self):
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ classes = (
|
|||||||
prop.BIMObjectSpatialProperties,
|
prop.BIMObjectSpatialProperties,
|
||||||
prop.BIMContainer,
|
prop.BIMContainer,
|
||||||
prop.BIMSpatialDecompositionProperties,
|
prop.BIMSpatialDecompositionProperties,
|
||||||
|
prop.BIMGridProperties,
|
||||||
ui.BIM_PT_spatial,
|
ui.BIM_PT_spatial,
|
||||||
ui.BIM_UL_containers_manager,
|
ui.BIM_UL_containers_manager,
|
||||||
ui.BIM_UL_elements,
|
ui.BIM_UL_elements,
|
||||||
@@ -60,6 +61,7 @@ def register():
|
|||||||
bpy.types.Scene.BIMSpatialDecompositionProperties = bpy.props.PointerProperty(
|
bpy.types.Scene.BIMSpatialDecompositionProperties = bpy.props.PointerProperty(
|
||||||
type=prop.BIMSpatialDecompositionProperties
|
type=prop.BIMSpatialDecompositionProperties
|
||||||
)
|
)
|
||||||
|
bpy.types.Scene.BIMGridProperties = bpy.props.PointerProperty(type=prop.BIMGridProperties)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
@@ -67,3 +69,4 @@ def unregister():
|
|||||||
bpy.utils.unregister_tool(workspace.SpatialTool)
|
bpy.utils.unregister_tool(workspace.SpatialTool)
|
||||||
del bpy.types.Object.BIMObjectSpatialProperties
|
del bpy.types.Object.BIMObjectSpatialProperties
|
||||||
del bpy.types.Scene.BIMSpatialDecompositionProperties
|
del bpy.types.Scene.BIMSpatialDecompositionProperties
|
||||||
|
del bpy.types.Scene.BIMGridProperties
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
# Bonsai - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2024 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of Bonsai.
|
||||||
|
#
|
||||||
|
# Bonsai is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Bonsai is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import blf
|
||||||
|
import gpu
|
||||||
|
import bmesh
|
||||||
|
import bonsai.tool as tool
|
||||||
|
from bpy.types import SpaceView3D
|
||||||
|
from mathutils import Vector
|
||||||
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
from bpy_extras.view3d_utils import location_3d_to_region_2d
|
||||||
|
|
||||||
|
|
||||||
|
class GridDecorator:
|
||||||
|
is_installed = False
|
||||||
|
handlers = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def install(cls, context):
|
||||||
|
if cls.is_installed:
|
||||||
|
cls.uninstall()
|
||||||
|
handler = cls()
|
||||||
|
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.is_installed = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def uninstall(cls):
|
||||||
|
for handler in cls.handlers:
|
||||||
|
try:
|
||||||
|
SpaceView3D.draw_handler_remove(handler, "WINDOW")
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
cls.is_installed = False
|
||||||
|
|
||||||
|
def draw_batch(self, shader_type, content_pos, color, indices=None):
|
||||||
|
shader = self.line_shader if shader_type == "LINES" else self.shader
|
||||||
|
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
||||||
|
shader.uniform_float("color", color)
|
||||||
|
batch.draw(shader)
|
||||||
|
|
||||||
|
def draw_text(self, context):
|
||||||
|
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||||
|
selected_elements_color = self.addon_prefs.decorator_color_selected
|
||||||
|
unselected_elements_color = self.addon_prefs.decorator_color_unselected
|
||||||
|
special_elements_color = self.addon_prefs.decorator_color_special
|
||||||
|
|
||||||
|
font_id = 0
|
||||||
|
blf.size(font_id, 12)
|
||||||
|
blf.enable(font_id, blf.SHADOW)
|
||||||
|
|
||||||
|
for axis in context.scene.BIMGridProperties.grid_axes:
|
||||||
|
if not (obj := axis.obj):
|
||||||
|
continue
|
||||||
|
tag = obj.name.split("/")[-1]
|
||||||
|
matrix_world = obj.matrix_world
|
||||||
|
v1 = matrix_world @ obj.data.vertices[0].co
|
||||||
|
v2 = matrix_world @ obj.data.vertices[1].co
|
||||||
|
|
||||||
|
color = selected_elements_color if obj.select_get() else unselected_elements_color
|
||||||
|
blf.color(font_id, *color)
|
||||||
|
|
||||||
|
coords_2d = location_3d_to_region_2d(context.region, context.region_data, v1)
|
||||||
|
if coords_2d:
|
||||||
|
w, h = blf.dimensions(font_id, tag)
|
||||||
|
coords_2d -= Vector((w * 0.5, h * 0.5))
|
||||||
|
blf.position(font_id, coords_2d[0], coords_2d[1], 0)
|
||||||
|
blf.draw(font_id, tag)
|
||||||
|
|
||||||
|
coords_2d = location_3d_to_region_2d(context.region, context.region_data, v2)
|
||||||
|
if coords_2d:
|
||||||
|
w, h = blf.dimensions(font_id, tag)
|
||||||
|
coords_2d -= Vector((w * 0.5, h * 0.5))
|
||||||
|
blf.position(font_id, coords_2d[0], coords_2d[1], 0)
|
||||||
|
blf.draw(font_id, tag)
|
||||||
|
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||||
|
selected_elements_color = self.addon_prefs.decorator_color_selected
|
||||||
|
unselected_elements_color = self.addon_prefs.decorator_color_unselected
|
||||||
|
special_elements_color = self.addon_prefs.decorator_color_special
|
||||||
|
|
||||||
|
gpu.state.point_size_set(6)
|
||||||
|
gpu.state.blend_set("ALPHA")
|
||||||
|
|
||||||
|
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||||
|
self.line_shader.bind() # required to be able to change uniforms of the shader
|
||||||
|
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||||
|
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||||
|
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||||
|
|
||||||
|
# general shader
|
||||||
|
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||||
|
|
||||||
|
selected_verts = []
|
||||||
|
selected_edges = []
|
||||||
|
selected_tags = []
|
||||||
|
unselected_verts = []
|
||||||
|
unselected_edges = []
|
||||||
|
for axis in context.scene.BIMGridProperties.grid_axes:
|
||||||
|
if obj := axis.obj:
|
||||||
|
if obj.select_get():
|
||||||
|
edges = selected_edges
|
||||||
|
verts = selected_verts
|
||||||
|
else:
|
||||||
|
edges = unselected_edges
|
||||||
|
verts = unselected_verts
|
||||||
|
i = len(verts)
|
||||||
|
edges.append([i, i + 1])
|
||||||
|
matrix_world = obj.matrix_world
|
||||||
|
v1 = matrix_world @ obj.data.vertices[0].co
|
||||||
|
v2 = matrix_world @ obj.data.vertices[1].co
|
||||||
|
verts.extend([v1, v2])
|
||||||
|
|
||||||
|
if selected_verts:
|
||||||
|
self.draw_batch("LINES", selected_verts, selected_elements_color, selected_edges)
|
||||||
|
|
||||||
|
if unselected_verts:
|
||||||
|
self.draw_batch("LINES", unselected_verts, unselected_elements_color, unselected_edges)
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from bonsai.bim.prop import StrProperty, Attribute
|
from bonsai.bim.prop import ObjProperty
|
||||||
from bonsai.bim.module.spatial.data import SpatialDecompositionData
|
from bonsai.bim.module.spatial.data import SpatialDecompositionData
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
@@ -166,3 +166,7 @@ class BIMSpatialDecompositionProperties(PropertyGroup):
|
|||||||
def active_element(self):
|
def active_element(self):
|
||||||
if self.elements and self.active_element_index < len(self.elements):
|
if self.elements and self.active_element_index < len(self.elements):
|
||||||
return self.elements[self.active_element_index]
|
return self.elements[self.active_element_index]
|
||||||
|
|
||||||
|
|
||||||
|
class BIMGridProperties(PropertyGroup):
|
||||||
|
grid_axes: CollectionProperty(name="Grid Axes", type=ObjProperty)
|
||||||
|
|||||||
@@ -107,4 +107,5 @@ def create_project(
|
|||||||
project.load_default_thumbnails()
|
project.load_default_thumbnails()
|
||||||
project.set_default_context()
|
project.set_default_context()
|
||||||
project.set_default_modeling_dimensions()
|
project.set_default_modeling_dimensions()
|
||||||
|
project.run_root_reload_grid_decorator()
|
||||||
georeference.set_model_origin()
|
georeference.set_model_origin()
|
||||||
|
|||||||
@@ -209,3 +209,7 @@ class Project(bonsai.core.tool.Project):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_appendable_asset_types(cls) -> tuple[str, ...]:
|
def get_appendable_asset_types(cls) -> tuple[str, ...]:
|
||||||
return tuple(e for e in APPENDABLE_ASSET_TYPES if e != "IfcProduct")
|
return tuple(e for e in APPENDABLE_ASSET_TYPES if e != "IfcProduct")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run_root_reload_grid_decorator(cls) -> None:
|
||||||
|
tool.Root.reload_grid_decorator()
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import bonsai.core.aggregate
|
|||||||
import bonsai.core.geometry
|
import bonsai.core.geometry
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
from typing import Union, Optional, Any
|
from typing import Union, Optional, Any
|
||||||
|
from bonsai.bim.module.spatial.decorator import GridDecorator
|
||||||
|
|
||||||
|
|
||||||
class Root(bonsai.core.tool.Root):
|
class Root(bonsai.core.tool.Root):
|
||||||
@@ -199,6 +200,20 @@ class Root(bonsai.core.tool.Root):
|
|||||||
return element.is_a("IfcSpatialStructureElement")
|
return element.is_a("IfcSpatialStructureElement")
|
||||||
return element.is_a("IfcSpatialElement")
|
return element.is_a("IfcSpatialElement")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_grid_axis(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||||
|
return element.is_a("IfcGridAxis")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def reload_grid_decorator(cls) -> None:
|
||||||
|
axes = bpy.context.scene.BIMGridProperties.grid_axes
|
||||||
|
axes.clear()
|
||||||
|
for axis in tool.Ifc.get().by_type("IfcGridAxis"):
|
||||||
|
if obj := tool.Ifc.get_object(axis):
|
||||||
|
new = axes.add()
|
||||||
|
new.obj = obj
|
||||||
|
GridDecorator.install(bpy.context)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def link_object_data(cls, source_obj: bpy.types.Object, destination_obj: bpy.types.Object) -> None:
|
def link_object_data(cls, source_obj: bpy.types.Object, destination_obj: bpy.types.Object) -> None:
|
||||||
destination_obj.data = source_obj.data
|
destination_obj.data = source_obj.data
|
||||||
|
|||||||
Reference in New Issue
Block a user