mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-10-02 13:04: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.MergeWall,
|
||||||
wall.RecalculateWall,
|
wall.RecalculateWall,
|
||||||
wall.SplitWall,
|
wall.SplitWall,
|
||||||
|
opening.AddBoolean,
|
||||||
opening.AddElementOpening,
|
opening.AddElementOpening,
|
||||||
|
opening.AddPotentialHalfSpaceSolid,
|
||||||
|
opening.AddPotentialOpening,
|
||||||
profile.ChangeCardinalPoint,
|
profile.ChangeCardinalPoint,
|
||||||
profile.ChangeProfileDepth,
|
profile.ChangeProfileDepth,
|
||||||
profile.ExtendProfile,
|
profile.ExtendProfile,
|
||||||
@@ -55,9 +58,9 @@ classes = (
|
|||||||
ui.BIM_PT_authoring,
|
ui.BIM_PT_authoring,
|
||||||
ui.DisplayConstrTypesUI,
|
ui.DisplayConstrTypesUI,
|
||||||
ui.HelpConstrTypes,
|
ui.HelpConstrTypes,
|
||||||
|
ui.BIM_MT_model,
|
||||||
grid.BIM_OT_add_object,
|
grid.BIM_OT_add_object,
|
||||||
stair.BIM_OT_add_object,
|
stair.BIM_OT_add_object,
|
||||||
opening.BIM_OT_add_object,
|
|
||||||
pie.OpenPieClass,
|
pie.OpenPieClass,
|
||||||
pie.PieUpdateContainer,
|
pie.PieUpdateContainer,
|
||||||
pie.PieAddOpening,
|
pie.PieAddOpening,
|
||||||
@@ -74,8 +77,7 @@ def register():
|
|||||||
bpy.types.Scene.BIMModelProperties = bpy.props.PointerProperty(type=prop.BIMModelProperties)
|
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(grid.add_object_button)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.append(stair.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(ui.add_menu)
|
||||||
bpy.types.VIEW3D_MT_add.append(product.add_empty_type_button)
|
|
||||||
bpy.app.handlers.load_post.append(handler.load_post)
|
bpy.app.handlers.load_post.append(handler.load_post)
|
||||||
wm = bpy.context.window_manager
|
wm = bpy.context.window_manager
|
||||||
if wm.keyconfigs.addon:
|
if wm.keyconfigs.addon:
|
||||||
@@ -92,8 +94,7 @@ def unregister():
|
|||||||
bpy.app.handlers.load_post.remove(handler.load_post)
|
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(grid.add_object_button)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.remove(stair.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(ui.add_menu)
|
||||||
bpy.types.VIEW3D_MT_add.remove(product.add_empty_type_button)
|
|
||||||
wm = bpy.context.window_manager
|
wm = bpy.context.window_manager
|
||||||
kc = wm.keyconfigs.addon
|
kc = wm.keyconfigs.addon
|
||||||
if kc:
|
if kc:
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import gpu
|
||||||
|
import bgl
|
||||||
import bmesh
|
import bmesh
|
||||||
import blenderbim.bim.handler
|
import blenderbim.bim.handler
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
@@ -27,8 +29,11 @@ from blenderbim.bim.ifc import IfcStore
|
|||||||
from math import pi
|
from math import pi
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from bpy.types import Operator
|
from bpy.types import Operator
|
||||||
|
from bpy.types import SpaceView3D
|
||||||
from bpy.props import FloatProperty
|
from bpy.props import FloatProperty
|
||||||
from bpy_extras.object_utils import AddObjectHelper, object_data_add
|
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):
|
def element_listener(element, obj):
|
||||||
@@ -202,6 +207,7 @@ class AddElementOpening(bpy.types.Operator):
|
|||||||
|
|
||||||
|
|
||||||
def add_object(self, context):
|
def add_object(self, context):
|
||||||
|
props = context.scene.BIMModelProperties
|
||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
bmesh.ops.create_cube(bm, size=self.size)
|
bmesh.ops.create_cube(bm, size=self.size)
|
||||||
bm.verts.ensure_lookup_table()
|
bm.verts.ensure_lookup_table()
|
||||||
@@ -212,20 +218,237 @@ def add_object(self, context):
|
|||||||
bm.free()
|
bm.free()
|
||||||
obj = object_data_add(context, mesh, operator=self)
|
obj = object_data_add(context, mesh, operator=self)
|
||||||
obj.name = "Opening"
|
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):
|
class AddPotentialOpening(Operator, AddObjectHelper):
|
||||||
bl_idname = "mesh.add_opening"
|
bl_idname = "bim.add_potential_opening"
|
||||||
bl_label = "Dumb Opening"
|
bl_label = "Add Potential Opening"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
size: FloatProperty(name="Size", default=0.5)
|
||||||
size: FloatProperty(name="Size", default=2)
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
add_object(self, context)
|
add_object(self, context)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
def add_object_button(self, context):
|
class AddPotentialHalfSpaceSolid(Operator, AddObjectHelper):
|
||||||
self.layout.operator(BIM_OT_add_object.bl_idname, icon="PLUGIN")
|
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/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
from blenderbim.bim.prop import ObjProperty
|
||||||
from blenderbim.bim.module.model.data import AuthoringData
|
from blenderbim.bim.module.model.data import AuthoringData
|
||||||
from blenderbim.bim.module.model.root import ConstrTypeEntityNotFound
|
from blenderbim.bim.module.model.root import ConstrTypeEntityNotFound
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
@@ -178,3 +179,4 @@ class BIMModelProperties(PropertyGroup):
|
|||||||
default="5",
|
default="5",
|
||||||
)
|
)
|
||||||
length: bpy.props.FloatProperty(default=42.0)
|
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/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
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.module.model.data import AuthoringData
|
||||||
from blenderbim.bim.helper import prop_with_search, close_operator_panel
|
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.",
|
"The Construction Type Browser allows to preview and add new instances to the model.",
|
||||||
"For further support, please click on the Documentation link below.",
|
"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