mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 22:50:21 +00:00
You can now generate type-based spaces and regenerate spaces when boundaries change.
This commit is contained in:
@@ -26,6 +26,7 @@ from . import (
|
|||||||
product,
|
product,
|
||||||
wall,
|
wall,
|
||||||
slab,
|
slab,
|
||||||
|
space,
|
||||||
stair,
|
stair,
|
||||||
window,
|
window,
|
||||||
opening,
|
opening,
|
||||||
@@ -47,11 +48,8 @@ classes = (
|
|||||||
product.AddEmptyType,
|
product.AddEmptyType,
|
||||||
product.AlignProduct,
|
product.AlignProduct,
|
||||||
product.ChangeTypePage,
|
product.ChangeTypePage,
|
||||||
product.DisplayConstrTypes,
|
|
||||||
product.GenerateHippedRoof,
|
product.GenerateHippedRoof,
|
||||||
product.GenerateSpace,
|
|
||||||
product.LoadTypeThumbnails,
|
product.LoadTypeThumbnails,
|
||||||
product.ReinvokeOperator,
|
|
||||||
workspace.Hotkey,
|
workspace.Hotkey,
|
||||||
wall.AlignWall,
|
wall.AlignWall,
|
||||||
wall.ChangeExtrusionDepth,
|
wall.ChangeExtrusionDepth,
|
||||||
@@ -87,6 +85,7 @@ classes = (
|
|||||||
slab.EnableEditingSketchExtrusionProfile,
|
slab.EnableEditingSketchExtrusionProfile,
|
||||||
slab.ResetVertex,
|
slab.ResetVertex,
|
||||||
slab.SetArcIndex,
|
slab.SetArcIndex,
|
||||||
|
space.GenerateSpace,
|
||||||
prop.ConstrTypeInfo,
|
prop.ConstrTypeInfo,
|
||||||
prop.ConstrClassInfo,
|
prop.ConstrClassInfo,
|
||||||
prop.ConstrBrowserState,
|
prop.ConstrBrowserState,
|
||||||
@@ -102,7 +101,6 @@ classes = (
|
|||||||
ui.BIM_PT_sverchok,
|
ui.BIM_PT_sverchok,
|
||||||
ui.BIM_PT_window,
|
ui.BIM_PT_window,
|
||||||
ui.BIM_PT_door,
|
ui.BIM_PT_door,
|
||||||
ui.DisplayConstrTypesUI,
|
|
||||||
ui.LaunchTypeManager,
|
ui.LaunchTypeManager,
|
||||||
ui.HelpConstrTypes,
|
ui.HelpConstrTypes,
|
||||||
ui.BIM_MT_model,
|
ui.BIM_MT_model,
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import blenderbim.core.geometry
|
|||||||
from . import wall, slab, profile, mep
|
from . import wall, slab, profile, mep
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.model.data import AuthoringData
|
from blenderbim.bim.module.model.data import AuthoringData
|
||||||
from blenderbim.bim.module.model.prop import store_cursor_position
|
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
from bpy_extras.object_utils import AddObjectHelper
|
from bpy_extras.object_utils import AddObjectHelper
|
||||||
from . import prop
|
from . import prop
|
||||||
@@ -223,66 +222,6 @@ class ChangeTypePage(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class DisplayConstrTypes(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.display_constr_types"
|
|
||||||
bl_label = "Browse Construction Types"
|
|
||||||
bl_options = {"REGISTER"}
|
|
||||||
bl_description = "Display all available Construction Types to add new instances"
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
if not AuthoringData.is_loaded:
|
|
||||||
AuthoringData.load()
|
|
||||||
props = context.scene.BIMModelProperties
|
|
||||||
ifc_class = props.ifc_class
|
|
||||||
constr_class_info = AuthoringData.constr_class_info(ifc_class)
|
|
||||||
if constr_class_info is None or not constr_class_info.fully_loaded:
|
|
||||||
AuthoringData.assetize_constr_class(ifc_class)
|
|
||||||
bpy.ops.bim.display_constr_types_ui("INVOKE_DEFAULT")
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class ReinvokeOperator(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.reinvoke_operator"
|
|
||||||
bl_label = "Reinvoke Popup Operator"
|
|
||||||
bl_options = {"REGISTER"}
|
|
||||||
bl_description = "Reinvoke a popup operator"
|
|
||||||
operator: bpy.props.StringProperty()
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
browser_state = context.scene.BIMModelProperties.constr_browser_state
|
|
||||||
store_cursor_position(context, event, window=False)
|
|
||||||
cursor_x, cursor_y = browser_state.cursor_x, browser_state.cursor_y
|
|
||||||
window_x, window_y = browser_state.window_x, browser_state.window_y
|
|
||||||
window = context.window
|
|
||||||
operator = self.operator
|
|
||||||
self.move_cursor_away(context, window)
|
|
||||||
|
|
||||||
def move_cursor_to_window():
|
|
||||||
window.cursor_warp(window_x, window_y)
|
|
||||||
|
|
||||||
def run_operator(operator, *args, **kwargs):
|
|
||||||
reduce(lambda x, arg: getattr(x, arg), operator.split("."), bpy.ops)(*args, **kwargs)
|
|
||||||
|
|
||||||
def reinvoke():
|
|
||||||
run_operator(operator, "INVOKE_DEFAULT", reinvoked=True)
|
|
||||||
window.cursor_warp(cursor_x, cursor_y)
|
|
||||||
|
|
||||||
bpy.app.timers.register(move_cursor_to_window, first_interval=browser_state.update_delay)
|
|
||||||
bpy.app.timers.register(reinvoke, first_interval=3 * browser_state.update_delay)
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
def move_cursor_away(self, context, window): # closes current popup
|
|
||||||
browser_state = context.scene.BIMModelProperties.constr_browser_state
|
|
||||||
window.cursor_warp(browser_state.far_away_x, browser_state.far_away_y)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def run_operator(operator, *args, **kwargs):
|
|
||||||
reduce(lambda x, arg: getattr(x, arg), operator.split("."), bpy.ops)(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class AlignProduct(bpy.types.Operator):
|
class AlignProduct(bpy.types.Operator):
|
||||||
bl_idname = "bim.align_product"
|
bl_idname = "bim.align_product"
|
||||||
bl_label = "Align Product"
|
bl_label = "Align Product"
|
||||||
@@ -579,110 +518,6 @@ def ensure_material_unassigned(usecase_path, ifc_file, settings):
|
|||||||
total_removed += 1
|
total_removed += 1
|
||||||
|
|
||||||
|
|
||||||
class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
|
||||||
bl_idname = "bim.generate_space"
|
|
||||||
bl_label = "Generate Space"
|
|
||||||
bl_options = {"REGISTER"}
|
|
||||||
|
|
||||||
def _execute(self, context):
|
|
||||||
# Note: this experimental operator relies on Shapely v2 (not v1.8 which
|
|
||||||
# is currently bundled). It only works based on a 2D plan only
|
|
||||||
# considering the standard layered walls (i.e. prismatic) in the
|
|
||||||
# currently active storey (though we can easily extend it to include
|
|
||||||
# other "bounding" elements). For now we generate rooms that exclude
|
|
||||||
# walls (i.e. not to wall midpoint or exterior / interior edge).
|
|
||||||
|
|
||||||
import bmesh
|
|
||||||
import shapely
|
|
||||||
from math import pi
|
|
||||||
|
|
||||||
collection = context.view_layer.active_layer_collection.collection
|
|
||||||
collection_obj = bpy.data.objects.get(collection.name)
|
|
||||||
if not collection_obj:
|
|
||||||
return
|
|
||||||
spatial_element = tool.Ifc.get_entity(collection_obj)
|
|
||||||
if not spatial_element:
|
|
||||||
return
|
|
||||||
|
|
||||||
boundary_elements = []
|
|
||||||
for subelement in ifcopenshell.util.element.get_decomposition(spatial_element):
|
|
||||||
if subelement.is_a("IfcWall") and tool.Model.get_usage_type(subelement) == "LAYER2":
|
|
||||||
boundary_elements.append(subelement)
|
|
||||||
|
|
||||||
boundary_lines = []
|
|
||||||
for element in boundary_elements:
|
|
||||||
obj = tool.Ifc.get_object(element)
|
|
||||||
if not obj:
|
|
||||||
continue
|
|
||||||
axis = self.get_wall_axis(obj)
|
|
||||||
for ypos in ["miny", "maxy"]:
|
|
||||||
start, end = axis[ypos]
|
|
||||||
offset = (end - start).normalized() * 0.3 # Offset wall lines by 300mm
|
|
||||||
boundary_lines.append(shapely.LineString([start - offset, end + offset]))
|
|
||||||
|
|
||||||
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
|
|
||||||
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
|
|
||||||
|
|
||||||
x, y = context.scene.cursor.location.xy
|
|
||||||
|
|
||||||
space_polygon = None
|
|
||||||
for polygon in closed_polygons.geoms:
|
|
||||||
if shapely.contains_xy(polygon, x, y):
|
|
||||||
space_polygon = shapely.force_3d(polygon)
|
|
||||||
|
|
||||||
if not space_polygon:
|
|
||||||
return
|
|
||||||
|
|
||||||
bm = bmesh.new()
|
|
||||||
bm.verts.index_update()
|
|
||||||
bm.edges.index_update()
|
|
||||||
|
|
||||||
new_verts = [bm.verts.new(v) for v in space_polygon.exterior.coords[0:-1]]
|
|
||||||
[bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
|
||||||
bm.edges.new((new_verts[len(new_verts) - 1], new_verts[0]))
|
|
||||||
|
|
||||||
for interior in space_polygon.interiors:
|
|
||||||
new_verts = [bm.verts.new(v) for v in interior.coords[0:-1]]
|
|
||||||
[bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
|
||||||
bm.edges.new((new_verts[len(new_verts) - 1], new_verts[0]))
|
|
||||||
|
|
||||||
bm.verts.index_update()
|
|
||||||
bm.edges.index_update()
|
|
||||||
|
|
||||||
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=1e-5)
|
|
||||||
bmesh.ops.triangle_fill(bm, edges=bm.edges)
|
|
||||||
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 5, verts=bm.verts, edges=bm.edges)
|
|
||||||
|
|
||||||
extrusion = bmesh.ops.extrude_face_region(bm, geom=bm.faces)
|
|
||||||
extruded_verts = [g for g in extrusion["geom"] if isinstance(g, bmesh.types.BMVert)]
|
|
||||||
bmesh.ops.translate(bm, vec=[0.0, 0.0, 3.0], verts=extruded_verts)
|
|
||||||
|
|
||||||
mesh = bpy.data.meshes.new(name="Space")
|
|
||||||
bm.to_mesh(mesh)
|
|
||||||
bm.free()
|
|
||||||
|
|
||||||
obj = bpy.data.objects.new("Space", mesh)
|
|
||||||
bpy.context.scene.collection.objects.link(obj)
|
|
||||||
|
|
||||||
def get_wall_axis(self, obj):
|
|
||||||
x_values = [v[0] for v in obj.bound_box]
|
|
||||||
y_values = [v[1] for v in obj.bound_box]
|
|
||||||
min_x = min(x_values)
|
|
||||||
max_x = max(x_values)
|
|
||||||
min_y = min(y_values)
|
|
||||||
max_y = max(y_values)
|
|
||||||
return {
|
|
||||||
"miny": [
|
|
||||||
(obj.matrix_world @ Vector((min_x, min_y, 0.0))).to_2d(),
|
|
||||||
(obj.matrix_world @ Vector((max_x, min_y, 0.0))).to_2d(),
|
|
||||||
],
|
|
||||||
"maxy": [
|
|
||||||
(obj.matrix_world @ Vector((min_x, max_y, 0.0))).to_2d(),
|
|
||||||
(obj.matrix_world @ Vector((max_x, max_y, 0.0))).to_2d(),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class GenerateHippedRoof(bpy.types.Operator, tool.Ifc.Operator):
|
class GenerateHippedRoof(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.generate_hipped_roof"
|
bl_idname = "bim.generate_hipped_roof"
|
||||||
bl_label = "Generate Hipped Roof"
|
bl_label = "Generate Hipped Roof"
|
||||||
|
|||||||
@@ -55,14 +55,6 @@ def get_type_predefined_type(self, context):
|
|||||||
return AuthoringData.data["type_predefined_type"]
|
return AuthoringData.data["type_predefined_type"]
|
||||||
|
|
||||||
|
|
||||||
def store_cursor_position(context, event, cursor=True, window=True):
|
|
||||||
browser_state = context.scene.BIMModelProperties.constr_browser_state
|
|
||||||
if cursor:
|
|
||||||
browser_state.cursor_x, browser_state.cursor_y = event.mouse_x, event.mouse_y
|
|
||||||
if window:
|
|
||||||
browser_state.window_x, browser_state.window_y = event.mouse_x, event.mouse_y
|
|
||||||
|
|
||||||
|
|
||||||
def update_icon_id(self, context, browser=False):
|
def update_icon_id(self, context, browser=False):
|
||||||
if context == "lost_context" or (context.region is not None and context.region.type != "TOOL_HEADER"):
|
if context == "lost_context" or (context.region is not None and context.region.type != "TOOL_HEADER"):
|
||||||
ifc_class = self.ifc_class_browser if browser else self.ifc_class
|
ifc_class = self.ifc_class_browser if browser else self.ifc_class
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on 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.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on 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 BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import bmesh
|
||||||
|
import shapely
|
||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
import blenderbim.core.type
|
||||||
|
from math import pi
|
||||||
|
from mathutils import Vector, Matrix
|
||||||
|
|
||||||
|
|
||||||
|
class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.generate_space"
|
||||||
|
bl_label = "Generate Space"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
# This only works based on a 2D plan only considering the standard
|
||||||
|
# layered walls (i.e. prismatic) in the currently active storey (though
|
||||||
|
# we can easily extend it to include other "bounding" elements). For
|
||||||
|
# now we generate rooms that exclude walls (i.e. not to wall midpoint
|
||||||
|
# or exterior / interior edge).
|
||||||
|
|
||||||
|
props = context.scene.BIMModelProperties
|
||||||
|
relating_type_id = props.relating_type_id
|
||||||
|
relating_type = None
|
||||||
|
if relating_type_id:
|
||||||
|
relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
||||||
|
if not relating_type.is_a("IfcSpaceType"):
|
||||||
|
relating_type = None
|
||||||
|
|
||||||
|
collection = context.view_layer.active_layer_collection.collection
|
||||||
|
collection_obj = bpy.data.objects.get(collection.name)
|
||||||
|
if not collection_obj:
|
||||||
|
return
|
||||||
|
spatial_element = tool.Ifc.get_entity(collection_obj)
|
||||||
|
if not spatial_element:
|
||||||
|
return
|
||||||
|
|
||||||
|
boundary_elements = []
|
||||||
|
for subelement in ifcopenshell.util.element.get_decomposition(spatial_element):
|
||||||
|
if subelement.is_a("IfcWall") and tool.Model.get_usage_type(subelement) == "LAYER2":
|
||||||
|
boundary_elements.append(subelement)
|
||||||
|
|
||||||
|
boundary_lines = []
|
||||||
|
for element in boundary_elements:
|
||||||
|
obj = tool.Ifc.get_object(element)
|
||||||
|
if not obj:
|
||||||
|
continue
|
||||||
|
axis = self.get_wall_axis(obj)
|
||||||
|
for ypos in ["miny", "maxy"]:
|
||||||
|
start, end = axis[ypos]
|
||||||
|
offset = (end - start).normalized() * 0.3 # Offset wall lines by 300mm
|
||||||
|
boundary_lines.append(shapely.LineString([start - offset, end + offset]))
|
||||||
|
|
||||||
|
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
|
||||||
|
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
|
||||||
|
|
||||||
|
x, y = context.scene.cursor.location.xy
|
||||||
|
|
||||||
|
space_polygon = None
|
||||||
|
for polygon in closed_polygons.geoms:
|
||||||
|
if shapely.contains_xy(polygon, x, y):
|
||||||
|
space_polygon = shapely.force_3d(polygon)
|
||||||
|
|
||||||
|
if not space_polygon:
|
||||||
|
return
|
||||||
|
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.verts.index_update()
|
||||||
|
bm.edges.index_update()
|
||||||
|
|
||||||
|
new_verts = [bm.verts.new(v) for v in space_polygon.exterior.coords[0:-1]]
|
||||||
|
[bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
||||||
|
bm.edges.new((new_verts[len(new_verts) - 1], new_verts[0]))
|
||||||
|
|
||||||
|
for interior in space_polygon.interiors:
|
||||||
|
new_verts = [bm.verts.new(v) for v in interior.coords[0:-1]]
|
||||||
|
[bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
||||||
|
bm.edges.new((new_verts[len(new_verts) - 1], new_verts[0]))
|
||||||
|
|
||||||
|
bm.verts.index_update()
|
||||||
|
bm.edges.index_update()
|
||||||
|
|
||||||
|
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=1e-5)
|
||||||
|
bmesh.ops.triangle_fill(bm, edges=bm.edges)
|
||||||
|
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 5, verts=bm.verts, edges=bm.edges)
|
||||||
|
|
||||||
|
extrusion = bmesh.ops.extrude_face_region(bm, geom=bm.faces)
|
||||||
|
extruded_verts = [g for g in extrusion["geom"] if isinstance(g, bmesh.types.BMVert)]
|
||||||
|
bmesh.ops.translate(bm, vec=[0.0, 0.0, 3.0], verts=extruded_verts)
|
||||||
|
|
||||||
|
mesh = bpy.data.meshes.new(name="Space")
|
||||||
|
bm.to_mesh(mesh)
|
||||||
|
bm.free()
|
||||||
|
|
||||||
|
active_obj = bpy.context.active_object
|
||||||
|
element = None
|
||||||
|
if bpy.context.selected_objects and active_obj:
|
||||||
|
element = tool.Ifc.get_entity(active_obj)
|
||||||
|
|
||||||
|
if element and element.is_a("IfcSpace"):
|
||||||
|
mesh.name = active_obj.data.name
|
||||||
|
mesh.BIMMeshProperties.ifc_definition_id = active_obj.data.BIMMeshProperties.ifc_definition_id
|
||||||
|
tool.Geometry.change_object_data(active_obj, mesh, is_global=True)
|
||||||
|
tool.Ifc.edit(active_obj)
|
||||||
|
else:
|
||||||
|
if relating_type:
|
||||||
|
name = tool.Model.generate_occurrence_name(relating_type, "IfcSpace")
|
||||||
|
else:
|
||||||
|
name = "Space"
|
||||||
|
obj = bpy.data.objects.new("Space", mesh)
|
||||||
|
collection.objects.link(obj)
|
||||||
|
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcSpace")
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
if relating_type:
|
||||||
|
print(element)
|
||||||
|
print(relating_type)
|
||||||
|
blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type)
|
||||||
|
|
||||||
|
def get_wall_axis(self, obj):
|
||||||
|
x_values = [v[0] for v in obj.bound_box]
|
||||||
|
y_values = [v[1] for v in obj.bound_box]
|
||||||
|
min_x = min(x_values)
|
||||||
|
max_x = max(x_values)
|
||||||
|
min_y = min(y_values)
|
||||||
|
max_y = max(y_values)
|
||||||
|
return {
|
||||||
|
"miny": [
|
||||||
|
(obj.matrix_world @ Vector((min_x, min_y, 0.0))).to_2d(),
|
||||||
|
(obj.matrix_world @ Vector((max_x, min_y, 0.0))).to_2d(),
|
||||||
|
],
|
||||||
|
"maxy": [
|
||||||
|
(obj.matrix_world @ Vector((min_x, max_y, 0.0))).to_2d(),
|
||||||
|
(obj.matrix_world @ Vector((max_x, max_y, 0.0))).to_2d(),
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ import bpy
|
|||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from bpy.types import Panel, Operator, Menu
|
from bpy.types import Panel, Operator, Menu
|
||||||
from blenderbim.bim.module.model.data import AuthoringData, ArrayData, StairData, SverchokData, WindowData, DoorData
|
from blenderbim.bim.module.model.data import AuthoringData, ArrayData, StairData, SverchokData, WindowData, DoorData
|
||||||
from blenderbim.bim.module.model.prop import store_cursor_position, get_ifc_class
|
from blenderbim.bim.module.model.prop import get_ifc_class
|
||||||
from blenderbim.bim.module.model.stair import update_stair_modifier
|
from blenderbim.bim.module.model.stair import update_stair_modifier
|
||||||
from blenderbim.bim.module.model.window import update_window_modifier_bmesh
|
from blenderbim.bim.module.model.window import update_window_modifier_bmesh
|
||||||
from blenderbim.bim.module.model.door import update_door_modifier_bmesh
|
from blenderbim.bim.module.model.door import update_door_modifier_bmesh
|
||||||
@@ -125,86 +125,6 @@ class BIM_PT_authoring(Panel):
|
|||||||
row.operator("bim.align_wall", icon="ANCHOR_BOTTOM", text="Int.").align_type = "INTERIOR"
|
row.operator("bim.align_wall", icon="ANCHOR_BOTTOM", text="Int.").align_type = "INTERIOR"
|
||||||
|
|
||||||
|
|
||||||
class DisplayConstrTypesUI(Operator):
|
|
||||||
bl_idname = "bim.display_constr_types_ui"
|
|
||||||
bl_label = "Browse Construction Types"
|
|
||||||
bl_options = {"REGISTER"}
|
|
||||||
bl_description = "Display all available Construction Types to add new instances"
|
|
||||||
reinvoked: bpy.props.BoolProperty(default=False)
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
store_cursor_position(context, event, window=not self.reinvoked)
|
|
||||||
return context.window_manager.invoke_popup(self, width=550)
|
|
||||||
|
|
||||||
def reinvoke(self, context):
|
|
||||||
browser_state = context.scene.BIMModelProperties.constr_browser_state
|
|
||||||
|
|
||||||
def set_updating_transaction():
|
|
||||||
browser_state.updating = True
|
|
||||||
|
|
||||||
def run_operator():
|
|
||||||
bpy.ops.bim.reinvoke_operator("INVOKE_DEFAULT", operator="bim.display_constr_types_ui")
|
|
||||||
browser_state.updating = False
|
|
||||||
|
|
||||||
if not browser_state.updating:
|
|
||||||
bpy.app.timers.register(set_updating_transaction)
|
|
||||||
bpy.app.timers.register(run_operator, first_interval=browser_state.update_delay)
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
props = context.scene.BIMModelProperties
|
|
||||||
|
|
||||||
if AuthoringData.data["ifc_classes"]:
|
|
||||||
row = self.layout.row()
|
|
||||||
row.label(text="", icon="FILE_VOLUME")
|
|
||||||
prop_with_search(row, props, "ifc_class_browser", text="")
|
|
||||||
ifc_class = props.ifc_class_browser
|
|
||||||
num_cols = 3
|
|
||||||
self.layout.row().separator(factor=0.25)
|
|
||||||
flow = self.layout.grid_flow(row_major=True, columns=num_cols, even_columns=True, even_rows=True, align=True)
|
|
||||||
relating_types = AuthoringData.relating_types_browser()
|
|
||||||
num_types = len(relating_types)
|
|
||||||
|
|
||||||
for idx, (relating_type_id, name, desc) in enumerate(relating_types):
|
|
||||||
outer_col = flow.column()
|
|
||||||
box = outer_col.box()
|
|
||||||
row = box.row()
|
|
||||||
row.label(text=name, icon="FILE_3D")
|
|
||||||
row.alignment = "CENTER"
|
|
||||||
row = box.row()
|
|
||||||
|
|
||||||
if ifc_class in props.constr_classes:
|
|
||||||
constr_class_info = props.constr_classes[ifc_class]
|
|
||||||
constr_types_info = constr_class_info.constr_types
|
|
||||||
|
|
||||||
if relating_type_id in constr_types_info:
|
|
||||||
icon_id = constr_types_info[relating_type_id].icon_id
|
|
||||||
row.template_icon(icon_value=icon_id, scale=6.0)
|
|
||||||
else:
|
|
||||||
self.reinvoke(context)
|
|
||||||
return
|
|
||||||
|
|
||||||
row = box.row()
|
|
||||||
op = row.operator("bim.add_constr_type_instance", icon="ADD")
|
|
||||||
op.from_invoke = True
|
|
||||||
op.ifc_class = ifc_class
|
|
||||||
|
|
||||||
if relating_type_id.isnumeric():
|
|
||||||
op.relating_type_id = int(relating_type_id)
|
|
||||||
|
|
||||||
last_row_cols = num_types % num_cols
|
|
||||||
|
|
||||||
if last_row_cols != 0:
|
|
||||||
for _ in range(num_cols - last_row_cols):
|
|
||||||
flow.column()
|
|
||||||
|
|
||||||
row = self.layout.row()
|
|
||||||
row.alignment = "RIGHT"
|
|
||||||
row.operator("bim.help_relating_types", text="", icon="QUESTION")
|
|
||||||
|
|
||||||
|
|
||||||
class HelpConstrTypes(Operator):
|
class HelpConstrTypes(Operator):
|
||||||
bl_idname = "bim.help_relating_types"
|
bl_idname = "bim.help_relating_types"
|
||||||
bl_label = "Construction Types Help"
|
bl_label = "Construction Types Help"
|
||||||
|
|||||||
@@ -122,11 +122,6 @@ class BimToolUI:
|
|||||||
if not AuthoringData.is_loaded:
|
if not AuthoringData.is_loaded:
|
||||||
AuthoringData.load()
|
AuthoringData.load()
|
||||||
|
|
||||||
# if ifc_classes and relating_types_ids and not cls.props.icon_id and not is_tool_header:
|
|
||||||
# # hack Dion won't like to show a preview also on the first time the sidebar is shown
|
|
||||||
# bpy.app.timers.register(lambda: prop.update_ifc_class(cls.props, "lost_context"))
|
|
||||||
# bpy.app.timers.register(lambda: prop.update_relating_type(cls.props, "lost_context"))
|
|
||||||
|
|
||||||
if context.region.type == "TOOL_HEADER":
|
if context.region.type == "TOOL_HEADER":
|
||||||
cls.draw_header_interface()
|
cls.draw_header_interface()
|
||||||
elif context.region.type in ("UI", "WINDOW"):
|
elif context.region.type in ("UI", "WINDOW"):
|
||||||
@@ -170,6 +165,11 @@ class BimToolUI:
|
|||||||
elif cls.props.ifc_class in ("IfcWindowType", "IfcWindowStyle", "IfcDoorType", "IfcDoorStyle"):
|
elif cls.props.ifc_class in ("IfcWindowType", "IfcWindowStyle", "IfcDoorType", "IfcDoorStyle"):
|
||||||
row = cls.layout.row(align=True)
|
row = cls.layout.row(align=True)
|
||||||
row.prop(data=cls.props, property="rl2", text="RL")
|
row.prop(data=cls.props, property="rl2", text="RL")
|
||||||
|
elif cls.props.ifc_class in ("IfcSpaceType"):
|
||||||
|
row = cls.layout.row(align=True)
|
||||||
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
|
row.label(text="", icon="EVENT_G")
|
||||||
|
add_layout_hotkey_operator(row, "Generate", "S_G", bpy.ops.bim.generate_space.__doc__)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def draw_edit_object_interface(cls, context):
|
def draw_edit_object_interface(cls, context):
|
||||||
@@ -290,6 +290,11 @@ class BimToolUI:
|
|||||||
row.label(text="", icon="EVENT_SHIFT")
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
row.label(text="", icon="EVENT_F")
|
row.label(text="", icon="EVENT_F")
|
||||||
row.operator("bim.hotkey", text="Flip").hotkey = "S_F"
|
row.operator("bim.hotkey", text="Flip").hotkey = "S_F"
|
||||||
|
elif AuthoringData.data["active_class"] in ("IfcSpace",):
|
||||||
|
row = cls.layout.row(align=True)
|
||||||
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
|
row.label(text="", icon="EVENT_G")
|
||||||
|
add_layout_hotkey_operator(row, "Regen", "S_G", bpy.ops.bim.generate_space.__doc__)
|
||||||
elif AuthoringData.data["active_class"] in (
|
elif AuthoringData.data["active_class"] in (
|
||||||
"IfcCableCarrierSegmentType",
|
"IfcCableCarrierSegmentType",
|
||||||
"IfcCableSegmentType",
|
"IfcCableSegmentType",
|
||||||
@@ -514,6 +519,8 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
def hotkey_S_G(self):
|
def hotkey_S_G(self):
|
||||||
if not bpy.context.selected_objects:
|
if not bpy.context.selected_objects:
|
||||||
|
if self.props.ifc_class == "IfcSpaceType":
|
||||||
|
bpy.ops.bim.generate_space()
|
||||||
return
|
return
|
||||||
if self.active_material_usage == "LAYER2":
|
if self.active_material_usage == "LAYER2":
|
||||||
bpy.ops.bim.recalculate_wall()
|
bpy.ops.bim.recalculate_wall()
|
||||||
@@ -521,6 +528,8 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bpy.ops.bim.recalculate_profile()
|
bpy.ops.bim.recalculate_profile()
|
||||||
elif self.active_class in ("IfcWindow", "IfcWindowStandardCase", "IfcDoor", "IfcDoorStandardCase"):
|
elif self.active_class in ("IfcWindow", "IfcWindowStandardCase", "IfcDoor", "IfcDoorStandardCase"):
|
||||||
bpy.ops.bim.recalculate_fill()
|
bpy.ops.bim.recalculate_fill()
|
||||||
|
elif self.active_class in ("IfcSpace"):
|
||||||
|
bpy.ops.bim.generate_space()
|
||||||
|
|
||||||
def hotkey_S_M(self):
|
def hotkey_S_M(self):
|
||||||
if not bpy.context.selected_objects:
|
if not bpy.context.selected_objects:
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ class Ifc(blenderbim.core.tool.Ifc):
|
|||||||
def link(cls, element, obj):
|
def link(cls, element, obj):
|
||||||
IfcStore.link_element(element, obj)
|
IfcStore.link_element(element, obj)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def edit(cls, obj):
|
||||||
|
IfcStore.edited_objs.add(obj)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete(cls, element):
|
def delete(cls, element):
|
||||||
IfcStore.delete_element(element)
|
IfcStore.delete_element(element)
|
||||||
|
|||||||
Reference in New Issue
Block a user