Add decorator for grids outside drawing view

This commit is contained in:
Dion Moult
2024-09-06 16:40:46 +10:00
parent 6b14111b24
commit 11497f9b37
8 changed files with 170 additions and 2 deletions
+6 -1
View File
@@ -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)
+5 -1
View File
@@ -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)
+1
View File
@@ -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()
+4
View File
@@ -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()
+15
View File
@@ -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