mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 10:57:49 +00:00
Openings are now specially shaded, and you can now add half space clipping booleans
# Conflicts: # src/blenderbim/blenderbim/bim/module/model/ui.py
This commit is contained in:
@@ -35,7 +35,10 @@ classes = (
|
||||
wall.MergeWall,
|
||||
wall.RecalculateWall,
|
||||
wall.SplitWall,
|
||||
opening.AddBoolean,
|
||||
opening.AddElementOpening,
|
||||
opening.AddPotentialHalfSpaceSolid,
|
||||
opening.AddPotentialOpening,
|
||||
profile.ChangeCardinalPoint,
|
||||
profile.ChangeProfileDepth,
|
||||
profile.ExtendProfile,
|
||||
@@ -55,9 +58,9 @@ classes = (
|
||||
ui.BIM_PT_authoring,
|
||||
ui.DisplayConstrTypesUI,
|
||||
ui.HelpConstrTypes,
|
||||
ui.BIM_MT_model,
|
||||
grid.BIM_OT_add_object,
|
||||
stair.BIM_OT_add_object,
|
||||
opening.BIM_OT_add_object,
|
||||
pie.OpenPieClass,
|
||||
pie.PieUpdateContainer,
|
||||
pie.PieAddOpening,
|
||||
@@ -74,8 +77,7 @@ def register():
|
||||
bpy.types.Scene.BIMModelProperties = bpy.props.PointerProperty(type=prop.BIMModelProperties)
|
||||
bpy.types.VIEW3D_MT_mesh_add.append(grid.add_object_button)
|
||||
bpy.types.VIEW3D_MT_mesh_add.append(stair.add_object_button)
|
||||
bpy.types.VIEW3D_MT_mesh_add.append(opening.add_object_button)
|
||||
bpy.types.VIEW3D_MT_add.append(product.add_empty_type_button)
|
||||
bpy.types.VIEW3D_MT_add.append(ui.add_menu)
|
||||
bpy.app.handlers.load_post.append(handler.load_post)
|
||||
wm = bpy.context.window_manager
|
||||
if wm.keyconfigs.addon:
|
||||
@@ -92,8 +94,7 @@ def unregister():
|
||||
bpy.app.handlers.load_post.remove(handler.load_post)
|
||||
bpy.types.VIEW3D_MT_mesh_add.remove(grid.add_object_button)
|
||||
bpy.types.VIEW3D_MT_mesh_add.remove(stair.add_object_button)
|
||||
bpy.types.VIEW3D_MT_mesh_add.remove(opening.add_object_button)
|
||||
bpy.types.VIEW3D_MT_add.remove(product.add_empty_type_button)
|
||||
bpy.types.VIEW3D_MT_add.remove(ui.add_menu)
|
||||
wm = bpy.context.window_manager
|
||||
kc = wm.keyconfigs.addon
|
||||
if kc:
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import gpu
|
||||
import bgl
|
||||
import bmesh
|
||||
import blenderbim.bim.handler
|
||||
import ifcopenshell
|
||||
@@ -27,8 +29,11 @@ from blenderbim.bim.ifc import IfcStore
|
||||
from math import pi
|
||||
from mathutils import Vector
|
||||
from bpy.types import Operator
|
||||
from bpy.types import SpaceView3D
|
||||
from bpy.props import FloatProperty
|
||||
from bpy_extras.object_utils import AddObjectHelper, object_data_add
|
||||
from gpu.types import GPUShader, GPUBatch, GPUIndexBuf, GPUVertBuf, GPUVertFormat
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
||||
|
||||
def element_listener(element, obj):
|
||||
@@ -202,6 +207,7 @@ class AddElementOpening(bpy.types.Operator):
|
||||
|
||||
|
||||
def add_object(self, context):
|
||||
props = context.scene.BIMModelProperties
|
||||
bm = bmesh.new()
|
||||
bmesh.ops.create_cube(bm, size=self.size)
|
||||
bm.verts.ensure_lookup_table()
|
||||
@@ -212,20 +218,237 @@ def add_object(self, context):
|
||||
bm.free()
|
||||
obj = object_data_add(context, mesh, operator=self)
|
||||
obj.name = "Opening"
|
||||
obj.display_type = "WIRE"
|
||||
|
||||
has_deleted_opening = True
|
||||
while has_deleted_opening:
|
||||
has_deleted_opening = False
|
||||
for i, opening in enumerate(props.openings):
|
||||
if not opening.obj:
|
||||
props.openings.remove(i)
|
||||
has_deleted_opening = True
|
||||
|
||||
new = props.openings.add()
|
||||
new.obj = obj
|
||||
|
||||
DecorationsHandler.install(context)
|
||||
|
||||
|
||||
class BIM_OT_add_object(Operator, AddObjectHelper):
|
||||
bl_idname = "mesh.add_opening"
|
||||
bl_label = "Dumb Opening"
|
||||
class AddPotentialOpening(Operator, AddObjectHelper):
|
||||
bl_idname = "bim.add_potential_opening"
|
||||
bl_label = "Add Potential Opening"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
size: FloatProperty(name="Size", default=2)
|
||||
size: FloatProperty(name="Size", default=0.5)
|
||||
|
||||
def execute(self, context):
|
||||
add_object(self, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def add_object_button(self, context):
|
||||
self.layout.operator(BIM_OT_add_object.bl_idname, icon="PLUGIN")
|
||||
class AddPotentialHalfSpaceSolid(Operator, AddObjectHelper):
|
||||
bl_idname = "bim.add_potential_half_space_solid"
|
||||
bl_label = "Add Potential Half Space Solid"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMModelProperties
|
||||
bm = bmesh.new()
|
||||
bmesh.ops.create_grid(bm, size=0.5)
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.edges.ensure_lookup_table()
|
||||
bm.faces.ensure_lookup_table()
|
||||
mesh = bpy.data.meshes.new(name="Dumb Opening")
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
obj = object_data_add(context, mesh, operator=self)
|
||||
obj.name = "HalfSpaceSolid"
|
||||
|
||||
has_deleted_opening = True
|
||||
while has_deleted_opening:
|
||||
has_deleted_opening = False
|
||||
for i, opening in enumerate(props.openings):
|
||||
if not opening.obj:
|
||||
props.openings.remove(i)
|
||||
has_deleted_opening = True
|
||||
|
||||
new = props.openings.add()
|
||||
new.obj = obj
|
||||
|
||||
DecorationsHandler.install(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddBoolean(Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_boolean"
|
||||
bl_label = "Add Boolean"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMModelProperties
|
||||
objs = context.selected_objects
|
||||
if len(objs) != 2:
|
||||
return {"FINISHED"}
|
||||
obj1, obj2 = objs
|
||||
element1 = tool.Ifc.get_entity(obj1)
|
||||
element2 = tool.Ifc.get_entity(obj2)
|
||||
if element1 and element2:
|
||||
return {"FINISHED"}
|
||||
if not element1 and not element2:
|
||||
return {"FINISHED"}
|
||||
if element2 and not element1:
|
||||
obj1, obj2 = obj2, obj1
|
||||
if not obj1.data or not hasattr(obj1.data, "BIMMeshProperties"):
|
||||
return {"FINISHED"}
|
||||
representation = tool.Ifc.get().by_id(obj1.data.BIMMeshProperties.ifc_definition_id)
|
||||
|
||||
ifcopenshell.api.run(
|
||||
"geometry.add_boolean",
|
||||
tool.Ifc.get(),
|
||||
representation=representation,
|
||||
operator="DIFFERENCE",
|
||||
matrix=obj1.matrix_world.inverted() @ obj2.matrix_world,
|
||||
)
|
||||
|
||||
has_deleted_opening = True
|
||||
while has_deleted_opening:
|
||||
has_deleted_opening = False
|
||||
for i, opening in enumerate(props.openings):
|
||||
if not opening.obj:
|
||||
props.openings.remove(i)
|
||||
has_deleted_opening = True
|
||||
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=obj1,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
enable_dynamic_voids=False,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DecorationsHandler:
|
||||
installed = None
|
||||
|
||||
@classmethod
|
||||
def install(cls, context):
|
||||
if cls.installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
cls.installed = SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_VIEW")
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls):
|
||||
try:
|
||||
SpaceView3D.draw_handler_remove(cls.installed, "WINDOW")
|
||||
except ValueError:
|
||||
pass
|
||||
cls.installed = None
|
||||
|
||||
def __call__(self, context):
|
||||
bgl.glLineWidth(2)
|
||||
bgl.glPointSize(6)
|
||||
bgl.glEnable(bgl.GL_BLEND)
|
||||
bgl.glEnable(bgl.GL_LINE_SMOOTH)
|
||||
|
||||
for opening in context.scene.BIMModelProperties.openings:
|
||||
obj = opening.obj
|
||||
|
||||
if not obj:
|
||||
continue
|
||||
|
||||
white = (1, 1, 1, 1)
|
||||
white_t = (1, 1, 1, 0.1)
|
||||
green = (0.545, 0.863, 0, 1)
|
||||
red = (1, 0.2, 0.322, 1)
|
||||
red_t = (1, 0.2, 0.322, 0.1)
|
||||
blue = (0.157, 0.565, 1, 1)
|
||||
blue_t = (0.157, 0.565, 1, 0.1)
|
||||
grey = (0.2, 0.2, 0.2, 1)
|
||||
|
||||
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
|
||||
verts = []
|
||||
selected_edges = []
|
||||
unselected_edges = []
|
||||
selected_vertices = []
|
||||
unselected_vertices = []
|
||||
|
||||
if obj.mode == "EDIT":
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
|
||||
for vertex in bm.verts:
|
||||
co = tuple(obj.matrix_world @ vertex.co)
|
||||
verts.append(co)
|
||||
if vertex.hide:
|
||||
continue
|
||||
|
||||
if vertex.select:
|
||||
selected_vertices.append(co)
|
||||
else:
|
||||
unselected_vertices.append(co)
|
||||
|
||||
for edge in bm.edges:
|
||||
edge_indices = [v.index for v in edge.verts]
|
||||
if edge.hide:
|
||||
continue
|
||||
if edge.select:
|
||||
selected_edges.append(edge_indices)
|
||||
else:
|
||||
unselected_edges.append(edge_indices)
|
||||
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=unselected_edges)
|
||||
self.shader.bind()
|
||||
self.shader.uniform_float("color", white)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=selected_edges)
|
||||
self.shader.uniform_float("color", green)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": unselected_vertices})
|
||||
self.shader.uniform_float("color", white)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": selected_vertices})
|
||||
self.shader.uniform_float("color", green)
|
||||
batch.draw(self.shader)
|
||||
else:
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
|
||||
verts = [tuple(obj.matrix_world @ v.co) for v in bm.verts]
|
||||
edges = [tuple([v.index for v in e.verts]) for e in bm.edges]
|
||||
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": verts}, indices=edges)
|
||||
self.shader.bind()
|
||||
self.shader.uniform_float("color", green if obj in context.selected_objects else blue)
|
||||
batch.draw(self.shader)
|
||||
|
||||
obj.data.calc_loop_triangles()
|
||||
tris = [tuple(t.vertices) for t in obj.data.loop_triangles]
|
||||
|
||||
batch = batch_for_shader(self.shader, "TRIS", {"pos": verts}, indices=tris)
|
||||
self.shader.bind()
|
||||
self.shader.uniform_float("color", blue_t)
|
||||
batch.draw(self.shader)
|
||||
|
||||
if "HalfSpaceSolid" in obj.name:
|
||||
# Arrow shape
|
||||
verts = [
|
||||
tuple(obj.matrix_world @ Vector((0, 0, 0))),
|
||||
tuple(obj.matrix_world @ Vector((0, 0, 0.5))),
|
||||
tuple(obj.matrix_world @ Vector((0.05, 0, 0.45))),
|
||||
tuple(obj.matrix_world @ Vector((-0.05, 0, 0.45))),
|
||||
tuple(obj.matrix_world @ Vector((0, 0.05, 0.45))),
|
||||
tuple(obj.matrix_world @ Vector((0, -0.05, 0.45))),
|
||||
]
|
||||
edges = [(0, 1), (1, 2), (1, 3), (1, 4), (1, 5)]
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": verts}, indices=edges)
|
||||
self.shader.bind()
|
||||
self.shader.uniform_float("color", green if obj in context.selected_objects else blue)
|
||||
batch.draw(self.shader)
|
||||
|
||||
if obj.mode != "EDIT":
|
||||
bm.free()
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.prop import ObjProperty
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
from blenderbim.bim.module.model.root import ConstrTypeEntityNotFound
|
||||
from bpy.types import PropertyGroup
|
||||
@@ -178,3 +179,4 @@ class BIMModelProperties(PropertyGroup):
|
||||
default="5",
|
||||
)
|
||||
length: bpy.props.FloatProperty(default=42.0)
|
||||
openings: bpy.props.CollectionProperty(type=ObjProperty)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from bpy.types import Panel, Operator
|
||||
from bpy.types import Panel, Operator, Menu
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
from blenderbim.bim.helper import prop_with_search, close_operator_panel
|
||||
|
||||
@@ -153,3 +153,18 @@ class HelpConstrTypes(Operator):
|
||||
"The Construction Type Browser allows to preview and add new instances to the model.",
|
||||
"For further support, please click on the Documentation link below.",
|
||||
]
|
||||
|
||||
|
||||
class BIM_MT_model(Menu):
|
||||
bl_idname = "BIM_MT_model"
|
||||
bl_label = "IFC Objects"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator("bim.add_empty_type", text="Empty Type", icon="EMPTY_AXIS")
|
||||
layout.operator("bim.add_potential_half_space_solid", text="Half Space Proxy", icon="ORIENTATION_NORMAL")
|
||||
layout.operator("bim.add_potential_opening", text="Opening Proxy", icon="CUBE")
|
||||
|
||||
|
||||
def add_menu(self, context):
|
||||
self.layout.menu("BIM_MT_model", icon="FILE_3D")
|
||||
|
||||
Reference in New Issue
Block a user