mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 06:39:13 +00:00
Merge branch 'v0.7.0' into pset_select_same_value
This commit is contained in:
@@ -106,6 +106,7 @@ classes = [
|
|||||||
prop.ObjProperty,
|
prop.ObjProperty,
|
||||||
prop.Attribute,
|
prop.Attribute,
|
||||||
prop.ModuleVisibility,
|
prop.ModuleVisibility,
|
||||||
|
prop.BIMAreaProperties,
|
||||||
prop.BIMProperties,
|
prop.BIMProperties,
|
||||||
prop.IfcParameter,
|
prop.IfcParameter,
|
||||||
prop.PsetQto,
|
prop.PsetQto,
|
||||||
@@ -119,6 +120,7 @@ classes = [
|
|||||||
ui.BIM_UL_topics,
|
ui.BIM_UL_topics,
|
||||||
ui.BIM_ADDON_preferences,
|
ui.BIM_ADDON_preferences,
|
||||||
# Scene panel groups
|
# Scene panel groups
|
||||||
|
ui.BIM_PT_root,
|
||||||
ui.BIM_PT_project_info,
|
ui.BIM_PT_project_info,
|
||||||
ui.BIM_PT_project_setup,
|
ui.BIM_PT_project_setup,
|
||||||
ui.BIM_PT_collaboration,
|
ui.BIM_PT_collaboration,
|
||||||
@@ -159,6 +161,7 @@ def register():
|
|||||||
bpy.app.handlers.load_post.append(handler.loadIfcStore)
|
bpy.app.handlers.load_post.append(handler.loadIfcStore)
|
||||||
bpy.app.handlers.save_post.append(handler.ensureIfcExported)
|
bpy.app.handlers.save_post.append(handler.ensureIfcExported)
|
||||||
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
||||||
|
bpy.types.Screen.BIMAreaProperties = bpy.props.CollectionProperty(type=prop.BIMAreaProperties)
|
||||||
bpy.types.Collection.BIMCollectionProperties = bpy.props.PointerProperty(type=prop.BIMCollectionProperties)
|
bpy.types.Collection.BIMCollectionProperties = bpy.props.PointerProperty(type=prop.BIMCollectionProperties)
|
||||||
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||||
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||||
|
|||||||
@@ -389,7 +389,7 @@
|
|||||||
"scene.display.shading.studiolight_background_blur": 0.0,
|
"scene.display.shading.studiolight_background_blur": 0.0,
|
||||||
"scene.display.shading.studiolight_intensity": 0.0,
|
"scene.display.shading.studiolight_intensity": 0.0,
|
||||||
"scene.display.shading.studiolight_rotate_z": 0.0,
|
"scene.display.shading.studiolight_rotate_z": 0.0,
|
||||||
"scene.display.shading.type": "SOLID",
|
"scene.display.shading.type": "RENDERED",
|
||||||
"scene.display.shading.use_compositor": "DISABLED",
|
"scene.display.shading.use_compositor": "DISABLED",
|
||||||
"scene.display.shading.use_dof": false,
|
"scene.display.shading.use_dof": false,
|
||||||
"scene.display.shading.use_scene_lights": false,
|
"scene.display.shading.use_scene_lights": false,
|
||||||
@@ -640,7 +640,7 @@
|
|||||||
"scene.display.shading.studiolight_background_blur": 0.0,
|
"scene.display.shading.studiolight_background_blur": 0.0,
|
||||||
"scene.display.shading.studiolight_intensity": 0.0,
|
"scene.display.shading.studiolight_intensity": 0.0,
|
||||||
"scene.display.shading.studiolight_rotate_z": 0.0,
|
"scene.display.shading.studiolight_rotate_z": 0.0,
|
||||||
"scene.display.shading.type": "SOLID",
|
"scene.display.shading.type": "RENDERED",
|
||||||
"scene.display.shading.use_compositor": "DISABLED",
|
"scene.display.shading.use_compositor": "DISABLED",
|
||||||
"scene.display.shading.use_dof": false,
|
"scene.display.shading.use_dof": false,
|
||||||
"scene.display.shading.use_scene_lights": false,
|
"scene.display.shading.use_scene_lights": false,
|
||||||
@@ -752,4 +752,4 @@
|
|||||||
"space.overlay.xray_alpha_bone": 0.0
|
"space.overlay.xray_alpha_bone": 0.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
|||||||
Name,foo
|
|
||||||
TransomThickness,500
|
|
||||||
|
Binary file not shown.
@@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# 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/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
import addon_utils
|
import addon_utils
|
||||||
@@ -30,6 +31,7 @@ from mathutils import Vector
|
|||||||
from math import cos, degrees
|
from math import cos, degrees
|
||||||
|
|
||||||
|
|
||||||
|
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||||
global_subscription_owner = object()
|
global_subscription_owner = object()
|
||||||
|
|
||||||
|
|
||||||
@@ -72,6 +74,9 @@ def name_callback(obj, data):
|
|||||||
if not obj.BIMObjectProperties.ifc_definition_id or "/" not in obj.name:
|
if not obj.BIMObjectProperties.ifc_definition_id or "/" not in obj.name:
|
||||||
return
|
return
|
||||||
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
if element.is_a("IfcGridAxis"):
|
||||||
|
element.AxisTag = obj.name.split("/")[1]
|
||||||
|
refresh_ui_data()
|
||||||
if not element.is_a("IfcRoot"):
|
if not element.is_a("IfcRoot"):
|
||||||
return
|
return
|
||||||
if obj.BIMObjectProperties.collection:
|
if obj.BIMObjectProperties.collection:
|
||||||
@@ -293,3 +298,33 @@ def setDefaultProperties(scene):
|
|||||||
ifcopenshell.api.owner.settings.get_user = lambda ifc: core_owner.get_user(tool.Owner)
|
ifcopenshell.api.owner.settings.get_user = lambda ifc: core_owner.get_user(tool.Owner)
|
||||||
ifcopenshell.api.owner.settings.get_application = get_application
|
ifcopenshell.api.owner.settings.get_application = get_application
|
||||||
AuthoringData.type_thumbnails = {}
|
AuthoringData.type_thumbnails = {}
|
||||||
|
|
||||||
|
if bpy.context.preferences.addons["blenderbim"].preferences.should_setup_workspace:
|
||||||
|
if "BIM" in bpy.data.workspaces:
|
||||||
|
bpy.context.window.workspace = bpy.data.workspaces["BIM"]
|
||||||
|
else:
|
||||||
|
bpy.ops.workspace.append_activate(idname="BIM", filepath=os.path.join(cwd, "data", "workspace.blend"))
|
||||||
|
|
||||||
|
for obj in [bpy.data.objects.get("Camera"), bpy.data.objects.get("Light")]:
|
||||||
|
if obj:
|
||||||
|
bpy.data.objects.remove(obj)
|
||||||
|
|
||||||
|
for panel in [
|
||||||
|
"SCENE_PT_scene",
|
||||||
|
"SCENE_PT_unit",
|
||||||
|
"SCENE_PT_physics",
|
||||||
|
"SCENE_PT_rigid_body_world",
|
||||||
|
"SCENE_PT_audio",
|
||||||
|
"SCENE_PT_keying_sets",
|
||||||
|
"SCENE_PT_custom_props",
|
||||||
|
]:
|
||||||
|
try:
|
||||||
|
bpy.utils.unregister_class(getattr(bpy.types, panel))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# https://blender.stackexchange.com/questions/140644/how-can-make-the-state-of-a-boolean-property-relative-to-the-3d-view-area
|
||||||
|
for screen in bpy.data.screens:
|
||||||
|
screen.BIMAreaProperties.clear()
|
||||||
|
for i in range(20): # 20 is an arbitrary value of split areas
|
||||||
|
screen.BIMAreaProperties.add()
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ classes = (
|
|||||||
operator.UndoBrick,
|
operator.UndoBrick,
|
||||||
operator.RedoBrick,
|
operator.RedoBrick,
|
||||||
operator.SerializeBrick,
|
operator.SerializeBrick,
|
||||||
|
operator.AddBrickNamespace,
|
||||||
prop.Brick,
|
prop.Brick,
|
||||||
prop.BIMBrickProperties,
|
prop.BIMBrickProperties,
|
||||||
ui.BIM_PT_brickschema,
|
ui.BIM_PT_brickschema,
|
||||||
|
|||||||
@@ -109,9 +109,10 @@ class BrickschemaData:
|
|||||||
if BrickStore.graph is None:
|
if BrickStore.graph is None:
|
||||||
return []
|
return []
|
||||||
results = []
|
results = []
|
||||||
|
filter = ["brick", "owl", "w3", "xml"]
|
||||||
for alias, uri in BrickStore.graph.namespaces():
|
for alias, uri in BrickStore.graph.namespaces():
|
||||||
# results.append((uri, f"{alias}: {uri}", ""))
|
if alias not in filter:
|
||||||
results.append((uri, f"{alias}", ""))
|
results.append((uri, f"{alias}: {uri}", ""))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import blenderbim.tool as tool
|
|||||||
import blenderbim.core.brick as core
|
import blenderbim.core.brick as core
|
||||||
import blenderbim.bim.handler
|
import blenderbim.bim.handler
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.tool.brick import BrickStore
|
||||||
|
|
||||||
class Operator:
|
class Operator:
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -126,6 +126,7 @@ class AddBrick(bpy.types.Operator, Operator):
|
|||||||
namespace=props.namespace,
|
namespace=props.namespace,
|
||||||
brick_class=props.brick_equipment_class,
|
brick_class=props.brick_equipment_class,
|
||||||
library=library,
|
library=library,
|
||||||
|
label=props.new_brick_label
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -207,6 +208,36 @@ class RedoBrick(bpy.types.Operator, Operator):
|
|||||||
class SerializeBrick(bpy.types.Operator, Operator):
|
class SerializeBrick(bpy.types.Operator, Operator):
|
||||||
bl_idname = "bim.serialize_brick"
|
bl_idname = "bim.serialize_brick"
|
||||||
bl_label = "Serialize Brick"
|
bl_label = "Serialize Brick"
|
||||||
|
filter_glob: bpy.props.StringProperty(default="*.ttl", options={"HIDDEN"})
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
should_save_as: bpy.props.BoolProperty(name="Should Save As", default=False, options={"HIDDEN"})
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
if self.should_save_as or not BrickStore.path:
|
||||||
|
WindowManager = context.window_manager
|
||||||
|
WindowManager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
else:
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
core.serialize_brick(tool.Brick)
|
if self.should_save_as or not BrickStore.path:
|
||||||
|
BrickStore.path = self.filepath
|
||||||
|
core.serialize_brick(tool.Brick)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def description(cls, context, properties):
|
||||||
|
if properties.should_save_as:
|
||||||
|
return "Save Brick project to a selected file"
|
||||||
|
return "Save the Brick project"
|
||||||
|
|
||||||
|
class AddBrickNamespace(bpy.types.Operator, Operator):
|
||||||
|
bl_idname = "bim.add_brick_namespace"
|
||||||
|
bl_label = "Add Brick Namespace"
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
props = context.scene.BIMBrickProperties
|
||||||
|
alias = props.new_brick_namespace_alias
|
||||||
|
uri = props.new_brick_namespace_uri
|
||||||
|
core.add_namespace(tool.Brick, alias=alias, uri=uri)
|
||||||
@@ -69,3 +69,7 @@ class BIMBrickProperties(PropertyGroup):
|
|||||||
libraries: EnumProperty(name="Libraries", items=get_libraries)
|
libraries: EnumProperty(name="Libraries", items=get_libraries)
|
||||||
namespace: EnumProperty(name="Namespace", items=get_namespaces)
|
namespace: EnumProperty(name="Namespace", items=get_namespaces)
|
||||||
brick_equipment_class: EnumProperty(name="Brick Equipment Class", items=get_brick_equipment_classes)
|
brick_equipment_class: EnumProperty(name="Brick Equipment Class", items=get_brick_equipment_classes)
|
||||||
|
brick_settings_toggled: BoolProperty(name="Brick Settings Toggled", default=False)
|
||||||
|
new_brick_label: StringProperty(name="New Brick Label")
|
||||||
|
new_brick_namespace_alias: StringProperty(name="New Brick Namespace Alias")
|
||||||
|
new_brick_namespace_uri: StringProperty(name="New Brick Namespace URI")
|
||||||
@@ -20,7 +20,7 @@ import blenderbim.tool as tool
|
|||||||
from bpy.types import Panel, UIList
|
from bpy.types import Panel, UIList
|
||||||
from blenderbim.bim.helper import prop_with_search
|
from blenderbim.bim.helper import prop_with_search
|
||||||
from blenderbim.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
|
from blenderbim.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
|
||||||
|
from blenderbim.tool.brick import BrickStore
|
||||||
|
|
||||||
class BIM_PT_brickschema(Panel):
|
class BIM_PT_brickschema(Panel):
|
||||||
bl_label = "Brickschema Project"
|
bl_label = "Brickschema Project"
|
||||||
@@ -30,6 +30,11 @@ class BIM_PT_brickschema(Panel):
|
|||||||
bl_region_type = "WINDOW"
|
bl_region_type = "WINDOW"
|
||||||
bl_context = "scene"
|
bl_context = "scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
return aprops.tab == "OTHER"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
if not BrickschemaData.is_loaded:
|
if not BrickschemaData.is_loaded:
|
||||||
BrickschemaData.load()
|
BrickschemaData.load()
|
||||||
@@ -41,15 +46,39 @@ class BIM_PT_brickschema(Panel):
|
|||||||
row.operator("bim.load_brick_project", text="Load Project")
|
row.operator("bim.load_brick_project", text="Load Project")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if BrickStore.path:
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text=BrickStore.path, icon='FILEBROWSER')
|
||||||
|
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
if len(self.props.brick_breadcrumbs):
|
if len(self.props.brick_breadcrumbs):
|
||||||
row.operator("bim.rewind_brick_class", text="", icon="FRAME_PREV")
|
row.operator("bim.rewind_brick_class", text="", icon="FRAME_PREV")
|
||||||
row.label(text=self.props.active_brick_class)
|
row.label(text=self.props.active_brick_class)
|
||||||
|
row.prop(data=self.props, property="brick_settings_toggled", text="", icon="PREFERENCES")
|
||||||
row.operator("bim.refresh_brick_viewer", text="", icon="FILE_REFRESH")
|
row.operator("bim.refresh_brick_viewer", text="", icon="FILE_REFRESH")
|
||||||
row.operator("bim.close_brick_project", text="", icon="CANCEL")
|
row.operator("bim.close_brick_project", text="", icon="CANCEL")
|
||||||
|
|
||||||
|
if self.props.brick_settings_toggled:
|
||||||
|
box = self.layout.box()
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.label(text="Active Namespace:")
|
||||||
|
row = box.row(align=True)
|
||||||
|
prop_with_search(row, self.props, "namespace", text="")
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.label(text="Bind New Namespace:")
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.prop(data=self.props, property="new_brick_namespace_alias", text="")
|
||||||
|
col = row.column()
|
||||||
|
col.alignment = "CENTER"
|
||||||
|
col.scale_x = 1.1
|
||||||
|
col.label(text=":")
|
||||||
|
row.prop(data=self.props, property="new_brick_namespace_uri", text="")
|
||||||
|
row.operator("bim.add_brick_namespace", text="", icon="ADD")
|
||||||
|
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
prop_with_search(row, self.props, "namespace", text="")
|
row.label(text="Create Entity:")
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(data=self.props, property="new_brick_label", text="")
|
||||||
prop_with_search(row, self.props, "brick_equipment_class", text="")
|
prop_with_search(row, self.props, "brick_equipment_class", text="")
|
||||||
row.operator("bim.add_brick", text="", icon="ADD")
|
row.operator("bim.add_brick", text="", icon="ADD")
|
||||||
|
|
||||||
@@ -63,7 +92,10 @@ class BIM_PT_brickschema(Panel):
|
|||||||
row.operator("bim.redo_brick", icon="LOOP_FORWARDS")
|
row.operator("bim.redo_brick", icon="LOOP_FORWARDS")
|
||||||
|
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.operator("bim.serialize_brick")
|
op = row.operator("bim.serialize_brick", icon="EXPORT", text="Save")
|
||||||
|
op.should_save_as = False
|
||||||
|
op = row.operator("bim.serialize_brick", icon="FILE_TICK", text="Save As")
|
||||||
|
op.should_save_as = True
|
||||||
|
|
||||||
self.layout.template_list("BIM_UL_bricks", "", self.props, "bricks", self.props, "active_brick_index")
|
self.layout.template_list("BIM_UL_bricks", "", self.props, "bricks", self.props, "active_brick_index")
|
||||||
|
|
||||||
|
|||||||
@@ -190,6 +190,8 @@ class CreateAllShapes(bpy.types.Operator):
|
|||||||
|
|
||||||
total = len(elements)
|
total = len(elements)
|
||||||
settings = ifcopenshell.geom.settings()
|
settings = ifcopenshell.geom.settings()
|
||||||
|
settings_2d = ifcopenshell.geom.settings()
|
||||||
|
settings_2d.set(settings_2d.INCLUDE_CURVES, True)
|
||||||
failures = []
|
failures = []
|
||||||
excludes = () # For the developer to debug with
|
excludes = () # For the developer to debug with
|
||||||
for i, element in enumerate(elements):
|
for i, element in enumerate(elements):
|
||||||
@@ -197,8 +199,16 @@ class CreateAllShapes(bpy.types.Operator):
|
|||||||
continue
|
continue
|
||||||
print(f"{i}/{total}:", element)
|
print(f"{i}/{total}:", element)
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
shape = None
|
||||||
try:
|
try:
|
||||||
shape = ifcopenshell.geom.create_shape(settings, element)
|
shape = ifcopenshell.geom.create_shape(settings, element)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
shape = ifcopenshell.geom.create_shape(settings_2d, element)
|
||||||
|
except:
|
||||||
|
failures.append(element)
|
||||||
|
print("***** FAILURE *****")
|
||||||
|
if shape:
|
||||||
print(
|
print(
|
||||||
"Success",
|
"Success",
|
||||||
time.time() - start,
|
time.time() - start,
|
||||||
@@ -206,9 +216,6 @@ class CreateAllShapes(bpy.types.Operator):
|
|||||||
len(shape.geometry.edges),
|
len(shape.geometry.edges),
|
||||||
len(shape.geometry.faces),
|
len(shape.geometry.faces),
|
||||||
)
|
)
|
||||||
except:
|
|
||||||
failures.append(element)
|
|
||||||
print("***** FAILURE *****")
|
|
||||||
print(f"Failures: {len(failures)}")
|
print(f"Failures: {len(failures)}")
|
||||||
for failure in failures:
|
for failure in failures:
|
||||||
print(failure)
|
print(failure)
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ classes = (
|
|||||||
prop.BIMAnnotationProperties,
|
prop.BIMAnnotationProperties,
|
||||||
ui.BIM_PT_camera,
|
ui.BIM_PT_camera,
|
||||||
ui.BIM_PT_drawing_underlay,
|
ui.BIM_PT_drawing_underlay,
|
||||||
ui.BIM_PT_annotation_utilities,
|
|
||||||
ui.BIM_PT_sheets,
|
ui.BIM_PT_sheets,
|
||||||
ui.BIM_PT_drawings,
|
ui.BIM_PT_drawings,
|
||||||
ui.BIM_PT_schedules,
|
ui.BIM_PT_schedules,
|
||||||
|
|||||||
@@ -300,9 +300,14 @@ class DecoratorData:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
dimension_style = "arrow"
|
dimension_style = "arrow"
|
||||||
|
fill_bg = False
|
||||||
classes = ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "Classes")
|
classes = ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "Classes")
|
||||||
if classes and "oblique" in classes.lower().split():
|
if classes:
|
||||||
dimension_style = "oblique"
|
classes_split = classes.lower().split()
|
||||||
|
if "oblique" in classes_split:
|
||||||
|
dimension_style = "oblique"
|
||||||
|
elif "fill-bg" in classes_split:
|
||||||
|
fill_bg = True
|
||||||
|
|
||||||
pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Dimension") or {}
|
pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Dimension") or {}
|
||||||
show_description_only = pset_data.get("ShowDescriptionOnly", False)
|
show_description_only = pset_data.get("ShowDescriptionOnly", False)
|
||||||
@@ -316,6 +321,7 @@ class DecoratorData:
|
|||||||
"suppress_zero_inches": suppress_zero_inches,
|
"suppress_zero_inches": suppress_zero_inches,
|
||||||
"text_prefix": text_prefix,
|
"text_prefix": text_prefix,
|
||||||
"text_suffix": text_suffix,
|
"text_suffix": text_suffix,
|
||||||
|
"fill_bg": fill_bg,
|
||||||
}
|
}
|
||||||
cls.data[obj.name] = dimension_data
|
cls.data[obj.name] = dimension_data
|
||||||
return dimension_data
|
return dimension_data
|
||||||
|
|||||||
@@ -1633,10 +1633,17 @@ class ActivateDrawingStyle(bpy.types.Operator, Operator):
|
|||||||
space = self.get_view_3d(context) # Do not remove. It is used in exec later
|
space = self.get_view_3d(context) # Do not remove. It is used in exec later
|
||||||
style = json.loads(self.drawing_style.raster_style)
|
style = json.loads(self.drawing_style.raster_style)
|
||||||
for path, value in style.items():
|
for path, value in style.items():
|
||||||
if isinstance(value, str):
|
try:
|
||||||
exec(f"{path} = '{value}'")
|
if isinstance(value, str):
|
||||||
else:
|
exec(f"{path} = '{value}'")
|
||||||
exec(f"{path} = {value}")
|
else:
|
||||||
|
exec(f"{path} = {value}")
|
||||||
|
except:
|
||||||
|
# Differences in Blender versions mean result in failures here
|
||||||
|
print("Failed to set shading style {path} to {value}")
|
||||||
|
shading_type = style.get("scene.display.shading.type", None)
|
||||||
|
if shading_type:
|
||||||
|
space.shading.type = shading_type
|
||||||
|
|
||||||
def set_query(self, context):
|
def set_query(self, context):
|
||||||
self.include_global_ids = []
|
self.include_global_ids = []
|
||||||
|
|||||||
@@ -828,45 +828,20 @@ class SvgWriter:
|
|||||||
|
|
||||||
line_number = 0
|
line_number = 0
|
||||||
for text_literal in text_literals:
|
for text_literal in text_literals:
|
||||||
# after pretty indentation some redundant spaces can occur in svg tags
|
|
||||||
# this is why we apply "font-size: 0;" to the text tag to remove those spaces
|
|
||||||
# and add clases to the tspan tags
|
|
||||||
# ref: https://github.com/IfcOpenShell/IfcOpenShell/issues/2833#issuecomment-1471584960
|
|
||||||
|
|
||||||
text = tool.Drawing.replace_text_literal_variables(text_literal.Literal, product)
|
text = tool.Drawing.replace_text_literal_variables(text_literal.Literal, product)
|
||||||
attribs = {
|
text_tags = self.create_text_tag(
|
||||||
"transform": text_transform,
|
text,
|
||||||
"style": "font-size: 0;",
|
text_position_svg,
|
||||||
}
|
angle,
|
||||||
|
text_literal.BoxAlignment,
|
||||||
def add_text_tag(add_fill_bg):
|
classes_str,
|
||||||
nonlocal line_number
|
multiline=True,
|
||||||
text_tag = self.svg.text(
|
fill_bg="fill-bg" in classes,
|
||||||
"",
|
line_number_start=line_number,
|
||||||
**(attribs | {"filter": "url(#fill-background)"}) if add_fill_bg else attribs,
|
)
|
||||||
**SvgWriter.get_box_alignment_parameters(text_literal.BoxAlignment),
|
for tag in text_tags:
|
||||||
)
|
self.svg.add(tag)
|
||||||
self.svg.add(text_tag)
|
line_number += len(tag.elements)
|
||||||
|
|
||||||
text_lines = text.replace("\\n", "\n").split("\n")
|
|
||||||
|
|
||||||
for text_line in text_lines:
|
|
||||||
# position has to be inserted at tspan to avoid x offset between tspans
|
|
||||||
# note that tspan doesn't support using `transform` attribute
|
|
||||||
# so we use (0,0) position because tspan is already offseted by text transform
|
|
||||||
tspan = self.svg.tspan(text_line, class_=classes_str, insert=(0, 0))
|
|
||||||
# doing it here and not in tspan constructor because constructor adds unnecessary spaces
|
|
||||||
tspan.update({"dy": f"{line_number}em"})
|
|
||||||
text_tag.add(tspan)
|
|
||||||
line_number += 1
|
|
||||||
|
|
||||||
if add_fill_bg:
|
|
||||||
# return line_number back to the original value
|
|
||||||
line_number -= len(text_lines)
|
|
||||||
|
|
||||||
if "fill-bg" in classes:
|
|
||||||
add_text_tag(True)
|
|
||||||
add_text_tag(False)
|
|
||||||
|
|
||||||
def draw_break_annotations(self, obj):
|
def draw_break_annotations(self, obj):
|
||||||
x_offset = self.raw_width / 2
|
x_offset = self.raw_width / 2
|
||||||
@@ -1231,6 +1206,7 @@ class SvgWriter:
|
|||||||
suppress_zero_inches=dimension_data["suppress_zero_inches"],
|
suppress_zero_inches=dimension_data["suppress_zero_inches"],
|
||||||
text_prefix=dimension_data["text_prefix"],
|
text_prefix=dimension_data["text_prefix"],
|
||||||
text_suffix=dimension_data["text_suffix"],
|
text_suffix=dimension_data["text_suffix"],
|
||||||
|
fill_bg=dimension_data["fill_bg"],
|
||||||
)
|
)
|
||||||
|
|
||||||
def draw_measureit_arch_dimension_annotations(self):
|
def draw_measureit_arch_dimension_annotations(self):
|
||||||
@@ -1256,6 +1232,7 @@ class SvgWriter:
|
|||||||
suppress_zero_inches=False,
|
suppress_zero_inches=False,
|
||||||
text_prefix="",
|
text_prefix="",
|
||||||
text_suffix="",
|
text_suffix="",
|
||||||
|
fill_bg=False,
|
||||||
):
|
):
|
||||||
offset = Vector([self.raw_width, self.raw_height]) / 2
|
offset = Vector([self.raw_width, self.raw_height]) / 2
|
||||||
v0 = self.project_point_onto_camera(v0_global)
|
v0 = self.project_point_onto_camera(v0_global)
|
||||||
@@ -1281,44 +1258,44 @@ class SvgWriter:
|
|||||||
line = self.svg.line(start=start, end=end, class_=" ".join(classes))
|
line = self.svg.line(start=start, end=end, class_=" ".join(classes))
|
||||||
self.svg.add(line)
|
self.svg.add(line)
|
||||||
|
|
||||||
|
text_tags = []
|
||||||
|
text_tag_kwargs = {
|
||||||
|
"angle": angle,
|
||||||
|
"class_str": "DIMENSION",
|
||||||
|
"text_format": text_format,
|
||||||
|
"multiline": True,
|
||||||
|
"fill_bg": fill_bg,
|
||||||
|
}
|
||||||
|
|
||||||
if not show_description_only:
|
if not show_description_only:
|
||||||
text = f"{text_prefix}{str(dimension)}{text_suffix}"
|
text = f"{text_prefix}{str(dimension)}{text_suffix}"
|
||||||
text_tag = self.create_text_tag(
|
text_tags += self.create_text_tag(
|
||||||
text,
|
text,
|
||||||
text_position + perpendicular,
|
text_position + perpendicular,
|
||||||
angle,
|
box_alignment="bottom-middle",
|
||||||
"bottom-middle",
|
|
||||||
"DIMENSION",
|
|
||||||
text_format=text_format,
|
|
||||||
multiline=True,
|
|
||||||
multiline_to_bottom=False,
|
multiline_to_bottom=False,
|
||||||
|
**text_tag_kwargs,
|
||||||
)
|
)
|
||||||
self.svg.add(text_tag)
|
|
||||||
if dimension_text:
|
if dimension_text:
|
||||||
text_tag = self.create_text_tag(
|
text_tags += self.create_text_tag(
|
||||||
dimension_text,
|
dimension_text,
|
||||||
text_position - perpendicular,
|
text_position - perpendicular,
|
||||||
angle,
|
box_alignment="top-middle",
|
||||||
"top-middle",
|
|
||||||
"DIMENSION",
|
|
||||||
text_format=text_format,
|
|
||||||
multiline=True,
|
|
||||||
multiline_to_bottom=True,
|
multiline_to_bottom=True,
|
||||||
|
**text_tag_kwargs,
|
||||||
)
|
)
|
||||||
self.svg.add(text_tag)
|
|
||||||
|
|
||||||
elif show_description_only and dimension_text:
|
elif show_description_only and dimension_text:
|
||||||
text_tag = self.create_text_tag(
|
text_tags.extend += self.create_text_tag(
|
||||||
dimension_text,
|
dimension_text,
|
||||||
text_position + perpendicular,
|
text_position + perpendicular,
|
||||||
angle,
|
box_alignment="bottom-middle",
|
||||||
"bottom-middle",
|
|
||||||
"DIMENSION",
|
|
||||||
text_format=text_format,
|
|
||||||
multiline=True,
|
|
||||||
multiline_to_bottom=False,
|
multiline_to_bottom=False,
|
||||||
|
**text_tag_kwargs,
|
||||||
)
|
)
|
||||||
self.svg.add(text_tag)
|
|
||||||
|
for tag in text_tags:
|
||||||
|
self.svg.add(tag)
|
||||||
|
|
||||||
def create_text_tag(
|
def create_text_tag(
|
||||||
self,
|
self,
|
||||||
@@ -1329,33 +1306,58 @@ class SvgWriter:
|
|||||||
class_str,
|
class_str,
|
||||||
text_format=lambda x: x,
|
text_format=lambda x: x,
|
||||||
multiline=False,
|
multiline=False,
|
||||||
multiline_to_bottom=False,
|
multiline_to_bottom=True,
|
||||||
|
fill_bg=False,
|
||||||
|
line_number_start=0,
|
||||||
|
_draw_fill_bg=False,
|
||||||
):
|
):
|
||||||
|
"""returns list of created text tags"""
|
||||||
|
text_tags = []
|
||||||
|
if fill_bg:
|
||||||
|
method_kwargs = locals() | {"_draw_fill_bg": True, "fill_bg": False}
|
||||||
|
del method_kwargs["self"]
|
||||||
|
del method_kwargs["text_tags"]
|
||||||
|
text_tags += self.create_text_tag(**method_kwargs)
|
||||||
|
|
||||||
|
base_text_attrs = SvgWriter.get_box_alignment_parameters(box_alignment)
|
||||||
|
base_text_attrs = base_text_attrs | ({"filter": "url(#fill-background)"} if _draw_fill_bg else {})
|
||||||
|
|
||||||
if not multiline:
|
if not multiline:
|
||||||
text_kwargs = {"transform": "rotate({} {} {})".format(angle, text_position.x, text_position.y)}
|
transform_kwargs = {"transform": "rotate({} {} {})".format(angle, text_position.x, text_position.y)}
|
||||||
return self.svg.text(
|
text_tag = self.svg.text(
|
||||||
text_format(text),
|
text_format(text),
|
||||||
insert=text_position,
|
insert=text_position,
|
||||||
class_=class_str,
|
class_=class_str,
|
||||||
**(text_kwargs | SvgWriter.get_box_alignment_parameters(box_alignment)),
|
**(transform_kwargs | base_text_attrs),
|
||||||
)
|
)
|
||||||
|
text_tags.append(text_tag)
|
||||||
|
return text_tags
|
||||||
|
|
||||||
text_position_svg_str = ", ".join(map(str, text_position))
|
text_position_svg_str = ", ".join(map(str, text_position))
|
||||||
text_transform = f"translate({text_position_svg_str}) rotate({angle})"
|
text_transform = f"translate({text_position_svg_str}) rotate({angle})"
|
||||||
|
# after pretty indentation some redundant spaces can occur in svg tags
|
||||||
|
# this is why we apply "font-size: 0;" to the text tag to remove those spaces
|
||||||
|
# and add clases to the tspan tags
|
||||||
|
# ref: https://github.com/IfcOpenShell/IfcOpenShell/issues/2833#issuecomment-1471584960
|
||||||
text_kwargs = {
|
text_kwargs = {
|
||||||
"transform": text_transform,
|
"transform": text_transform,
|
||||||
"style": "font-size: 0;",
|
"style": "font-size: 0;",
|
||||||
}
|
}
|
||||||
|
|
||||||
text_tag = self.svg.text("", **text_kwargs, **SvgWriter.get_box_alignment_parameters(box_alignment))
|
text_tag = self.svg.text("", **text_kwargs, **base_text_attrs)
|
||||||
|
text_tags.append(text_tag)
|
||||||
text_lines = text.replace("\\n", "\n").split("\n")
|
text_lines = text.replace("\\n", "\n").split("\n")
|
||||||
text_lines = text_lines if multiline_to_bottom else text_lines[::-1]
|
text_lines = text_lines if multiline_to_bottom else text_lines[::-1]
|
||||||
|
|
||||||
for line_number, text_line in enumerate(text_lines):
|
for line_number, text_line in enumerate(text_lines, line_number_start):
|
||||||
|
# position has to be inserted at tspan to avoid x offset between tspans
|
||||||
|
# note that tspan doesn't support using `transform` attribute
|
||||||
|
# so we use (0,0) position because tspan is already offseted by text transform
|
||||||
tspan = self.svg.tspan(text_format(text_line), class_=class_str, insert=(0, 0))
|
tspan = self.svg.tspan(text_format(text_line), class_=class_str, insert=(0, 0))
|
||||||
|
# doing it here and not in tspan constructor because constructor adds unnecessary spaces
|
||||||
tspan.update({"dy": f"{line_number if multiline_to_bottom else -line_number}em"})
|
tspan.update({"dy": f"{line_number if multiline_to_bottom else -line_number}em"})
|
||||||
text_tag.add(tspan)
|
text_tag.add(tspan)
|
||||||
return text_tag
|
return text_tags
|
||||||
|
|
||||||
def project_point_onto_camera(self, point):
|
def project_point_onto_camera(self, point):
|
||||||
# TODO is this needlessly complex?
|
# TODO is this needlessly complex?
|
||||||
|
|||||||
@@ -489,87 +489,6 @@ class BIM_PT_text(Panel):
|
|||||||
row.label(text=literal_data[attribute])
|
row.label(text=literal_data[attribute])
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_annotation_utilities(Panel):
|
|
||||||
bl_idname = "BIM_PT_annotation_utilities"
|
|
||||||
bl_label = "Annotation"
|
|
||||||
bl_space_type = "VIEW_3D"
|
|
||||||
bl_region_type = "UI"
|
|
||||||
bl_category = "BIM Documentation"
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
|
|
||||||
self.props = context.scene.DocProperties
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.add_annotation", text="Dimension", icon="FIXED_SIZE")
|
|
||||||
op.object_type = "DIMENSION"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
op = row.operator("bim.add_annotation", text="Angle", icon="DRIVER_ROTATIONAL_DIFFERENCE")
|
|
||||||
op.object_type = "ANGLE"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.add_annotation", text="Radius", icon="FORWARD")
|
|
||||||
op.object_type = "RADIUS"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
op = row.operator("bim.add_annotation", text="Diameter", icon="ARROW_LEFTRIGHT")
|
|
||||||
op.object_type = "DIAMETER"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.add_annotation", text="Text", icon="SMALL_CAPS")
|
|
||||||
op.object_type = "TEXT"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
op = row.operator("bim.add_annotation", text="Leader", icon="TRACKING_BACKWARDS")
|
|
||||||
op.object_type = "TEXT_LEADER"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.add_annotation", text="Stair Arrow", icon="SCREEN_BACK")
|
|
||||||
op.object_type = "STAIR_ARROW"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
op = row.operator("bim.add_annotation", text="Hidden", icon="CON_TRACKTO")
|
|
||||||
op.object_type = "HIDDEN_LINE"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.add_annotation", text="Level (Plan)", icon="SORTBYEXT")
|
|
||||||
op.object_type = "PLAN_LEVEL"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
op = row.operator("bim.add_annotation", text="Level (Section)", icon="TRIA_DOWN")
|
|
||||||
op.object_type = "SECTION_LEVEL"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.add_annotation", text="Breakline", icon="FCURVE")
|
|
||||||
op.object_type = "BREAKLINE"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
op = row.operator("bim.add_annotation", text="Line", icon="MESH_MONKEY")
|
|
||||||
op.object_type = "LINEWORK"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.add_annotation", text="Batting", icon="FORCE_FORCE")
|
|
||||||
op.object_type = "BATTING"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
op.description = "Add batting annotation.\nThickness could be changed through Thickness property of BBIM_Batting property set"
|
|
||||||
op = row.operator("bim.add_annotation", text="Fill Area", icon="NODE_TEXTURE")
|
|
||||||
op.object_type = "FILL_AREA"
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.add_annotation", text="Fall", icon="SORT_ASC")
|
|
||||||
op.object_type = "FALL"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
op = row.operator("bim.add_annotation", text="Revision", icon="VOLUME_DATA")
|
|
||||||
op.object_type = "REVISION_CLOUD"
|
|
||||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
row.prop(self.props, "should_draw_decorations", text="Viewport Annotations")
|
|
||||||
row.enabled = context.scene.camera is not None
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_UL_drawinglist(bpy.types.UIList):
|
class BIM_UL_drawinglist(bpy.types.UIList):
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||||
if item:
|
if item:
|
||||||
|
|||||||
@@ -187,6 +187,9 @@ class AnnotationToolUI:
|
|||||||
selected_icon = "CHECKBOX_HLT" if cls.props.create_representation_for_type else "CHECKBOX_DEHLT"
|
selected_icon = "CHECKBOX_HLT" if cls.props.create_representation_for_type else "CHECKBOX_DEHLT"
|
||||||
row.prop(cls.props, "create_representation_for_type", text="", icon=selected_icon)
|
row.prop(cls.props, "create_representation_for_type", text="", icon=selected_icon)
|
||||||
|
|
||||||
|
row = cls.layout.row(align=True)
|
||||||
|
row.prop(bpy.context.scene.DocProperties, "should_draw_decorations", text="Viewport Annotations")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def draw_edit_object_interface(cls, context):
|
def draw_edit_object_interface(cls, context):
|
||||||
if DecoratorData.get_ifc_text_data(bpy.context.object):
|
if DecoratorData.get_ifc_text_data(bpy.context.object):
|
||||||
|
|||||||
@@ -415,7 +415,11 @@ class OverrideDelete(bpy.types.Operator):
|
|||||||
if self.is_batch:
|
if self.is_batch:
|
||||||
ifcopenshell.util.element.batch_remove_deep2(tool.Ifc.get())
|
ifcopenshell.util.element.batch_remove_deep2(tool.Ifc.get())
|
||||||
for obj in context.selected_objects:
|
for obj in context.selected_objects:
|
||||||
if tool.Ifc.get_entity(obj):
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
if element:
|
||||||
|
if ifcopenshell.util.element.get_pset(element, "BBIM_Array"):
|
||||||
|
self.report({"INFO"}, "Elements that are part of an array cannot be deleted.")
|
||||||
|
return {"FINISHED"}
|
||||||
tool.Geometry.delete_ifc_object(obj)
|
tool.Geometry.delete_ifc_object(obj)
|
||||||
else:
|
else:
|
||||||
bpy.data.objects.remove(obj)
|
bpy.data.objects.remove(obj)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ classes = (
|
|||||||
operator.ConvertGlobalToLocal,
|
operator.ConvertGlobalToLocal,
|
||||||
operator.GetCursorLocation,
|
operator.GetCursorLocation,
|
||||||
operator.SetCursorLocation,
|
operator.SetCursorLocation,
|
||||||
|
operator.ConvertAngleToCoordinates,
|
||||||
prop.BIMGeoreferenceProperties,
|
prop.BIMGeoreferenceProperties,
|
||||||
ui.BIM_PT_gis,
|
ui.BIM_PT_gis,
|
||||||
ui.BIM_PT_gis_utilities,
|
ui.BIM_PT_gis_utilities,
|
||||||
|
|||||||
@@ -171,3 +171,19 @@ class ConvertGlobalToLocal(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
core.convert_global_to_local(tool.Georeference)
|
core.convert_global_to_local(tool.Georeference)
|
||||||
|
|
||||||
|
class ConvertAngleToCoordinates(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.convert_angle_to_coord"
|
||||||
|
bl_label = "Convert Angle To Y Axis"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Convert angle to Y axis"
|
||||||
|
type: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
file = tool.Ifc.get()
|
||||||
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
|
return file and (props.angle_degree_input_x or props.angle_degree_input_y)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.convert_angle_to_coord(tool.Georeference, type=self.type)
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ class BIMGeoreferenceProperties(PropertyGroup):
|
|||||||
projected_crs: CollectionProperty(name="Projected CRS", type=Attribute)
|
projected_crs: CollectionProperty(name="Projected CRS", type=Attribute)
|
||||||
coordinate_input: StringProperty(name="Coordinate Input", description='Formatted "x,y,z" (without quotes)')
|
coordinate_input: StringProperty(name="Coordinate Input", description='Formatted "x,y,z" (without quotes)')
|
||||||
coordinate_output: StringProperty(name="Coordinate Output", description='Formatted "x,y,z" (without quotes)')
|
coordinate_output: StringProperty(name="Coordinate Output", description='Formatted "x,y,z" (without quotes)')
|
||||||
|
angle_degree_input_x: FloatProperty(name="Angle Degree Input", description="Angle (in degrees) rel to Easting")
|
||||||
|
angle_degree_input_y: FloatProperty(name="Angle Degree Input", description="Angle (in degrees) rel to +Y")
|
||||||
|
x_axis_abscissa_output: StringProperty(name="X Axis Abscissa Ordinate Output", description="X axis abscissa and ordinate", )
|
||||||
|
x_axis_ordinate_output: StringProperty(name="X Axis Abscissa Ordinate Output", description="X axis abscissa and ordinate", )
|
||||||
|
y_axis_abscissa_output: StringProperty(name="Y Axis Abscissa Ordinate Output", description="Y axis abscissa and ordinate", )
|
||||||
|
y_axis_ordinate_output: StringProperty(name="Y Axis Abscissa Ordinate Output", description="Y axis abscissa and ordinate", )
|
||||||
has_blender_offset: BoolProperty(name="Has Blender Offset")
|
has_blender_offset: BoolProperty(name="Has Blender Offset")
|
||||||
blender_eastings: StringProperty(name="Blender Eastings", default="0")
|
blender_eastings: StringProperty(name="Blender Eastings", default="0")
|
||||||
blender_northings: StringProperty(name="Blender Northings", default="0")
|
blender_northings: StringProperty(name="Blender Northings", default="0")
|
||||||
|
|||||||
@@ -167,3 +167,21 @@ class BIM_PT_gis_utilities(Panel):
|
|||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.operator("bim.convert_local_to_global", text="Local to Global")
|
row.operator("bim.convert_local_to_global", text="Local to Global")
|
||||||
row.operator("bim.convert_global_to_local", text="Global to Local")
|
row.operator("bim.convert_global_to_local", text="Global to Local")
|
||||||
|
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="Orientation Calculator", icon="TRACKING_REFINE_FORWARDS")
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "angle_degree_input_x", text="Angle (°) rel to Easting")
|
||||||
|
row.operator("bim.convert_angle_to_coord", text="", icon="FILE_REFRESH").type = "rel_x"
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "x_axis_abscissa_output", text="XAxis Abscissa")
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "x_axis_ordinate_output", text="XAxis Ordinate")
|
||||||
|
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "angle_degree_input_y", text="Angle (°) rel to to +Y")
|
||||||
|
row.operator("bim.convert_angle_to_coord", text="", icon="FILE_REFRESH").type = "rel_y"
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "y_axis_abscissa_output", text="North Abscissa")
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "y_axis_ordinate_output", text="North Ordinate")
|
||||||
@@ -54,6 +54,8 @@ classes = (
|
|||||||
product.AddEmptyType,
|
product.AddEmptyType,
|
||||||
product.AlignProduct,
|
product.AlignProduct,
|
||||||
product.ChangeTypePage,
|
product.ChangeTypePage,
|
||||||
|
product.DisableAddType,
|
||||||
|
product.EnableAddType,
|
||||||
product.LoadTypeThumbnails,
|
product.LoadTypeThumbnails,
|
||||||
product.MirrorElements,
|
product.MirrorElements,
|
||||||
workspace.Hotkey,
|
workspace.Hotkey,
|
||||||
|
|||||||
@@ -37,6 +37,24 @@ from . import prop
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class EnableAddType(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.enable_add_type"
|
||||||
|
bl_label = "Enable Add Type"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
bpy.context.scene.BIMModelProperties.is_adding_type = True
|
||||||
|
|
||||||
|
|
||||||
|
class DisableAddType(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.disable_add_type"
|
||||||
|
bl_label = "Disable Add Type"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
bpy.context.scene.BIMModelProperties.is_adding_type = False
|
||||||
|
|
||||||
|
|
||||||
class AddEmptyType(bpy.types.Operator, AddObjectHelper):
|
class AddEmptyType(bpy.types.Operator, AddObjectHelper):
|
||||||
bl_idname = "bim.add_empty_type"
|
bl_idname = "bim.add_empty_type"
|
||||||
bl_label = "Add Empty Type"
|
bl_label = "Add Empty Type"
|
||||||
@@ -154,10 +172,11 @@ class AddConstrTypeInstance(bpy.types.Operator):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
parent = ifcopenshell.util.element.get_container(building_element)
|
parent = ifcopenshell.util.element.get_container(building_element)
|
||||||
parent_obj = tool.Ifc.get_object(parent)
|
if parent:
|
||||||
blenderbim.core.spatial.assign_container(
|
parent_obj = tool.Ifc.get_object(parent)
|
||||||
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=parent_obj, element_obj=obj
|
blenderbim.core.spatial.assign_container(
|
||||||
)
|
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=parent_obj, element_obj=obj
|
||||||
|
)
|
||||||
|
|
||||||
# set occurences properties for the types defined with modifiers
|
# set occurences properties for the types defined with modifiers
|
||||||
if instance_class in ["IfcWindow", "IfcDoor"]:
|
if instance_class in ["IfcWindow", "IfcDoor"]:
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ class BIMModelProperties(PropertyGroup):
|
|||||||
)
|
)
|
||||||
icon_id: bpy.props.IntProperty()
|
icon_id: bpy.props.IntProperty()
|
||||||
updating: bpy.props.BoolProperty(default=False)
|
updating: bpy.props.BoolProperty(default=False)
|
||||||
|
is_adding_type: bpy.props.BoolProperty(default=False)
|
||||||
occurrence_name_style: bpy.props.EnumProperty(
|
occurrence_name_style: bpy.props.EnumProperty(
|
||||||
items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")],
|
items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")],
|
||||||
name="Occurrence Name Style",
|
name="Occurrence Name Style",
|
||||||
@@ -132,7 +133,7 @@ class BIMModelProperties(PropertyGroup):
|
|||||||
z: bpy.props.FloatProperty(name="Z", default=0.5)
|
z: bpy.props.FloatProperty(name="Z", default=0.5)
|
||||||
rl1: bpy.props.FloatProperty(name="RL", default=1) # Used for things like walls, doors, flooring, skirting, etc
|
rl1: bpy.props.FloatProperty(name="RL", default=1) # Used for things like walls, doors, flooring, skirting, etc
|
||||||
rl2: bpy.props.FloatProperty(name="RL", default=1) # Used for things like windows, other hosted furniture
|
rl2: bpy.props.FloatProperty(name="RL", default=1) # Used for things like windows, other hosted furniture
|
||||||
x_angle: bpy.props.FloatProperty(name="X Angle", default=0, subtype="ANGLE")
|
x_angle: bpy.props.FloatProperty(name="X Angle", default=0, subtype="ANGLE", min=0, max=pi / 180 * 89)
|
||||||
type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page)
|
type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page)
|
||||||
type_template: bpy.props.EnumProperty(
|
type_template: bpy.props.EnumProperty(
|
||||||
items=(
|
items=(
|
||||||
|
|||||||
@@ -70,10 +70,7 @@ class LaunchTypeManager(bpy.types.Operator):
|
|||||||
|
|
||||||
row = columns.row(align=True)
|
row = columns.row(align=True)
|
||||||
row.alignment = "CENTER"
|
row.alignment = "CENTER"
|
||||||
row.prop(props, "type_predefined_type", text="")
|
# In case you want something here in the future
|
||||||
row.prop(props, "type_template", text="")
|
|
||||||
row.prop(props, "type_name", text="")
|
|
||||||
row.operator("bim.add_type", icon="ADD", text="")
|
|
||||||
|
|
||||||
row = columns.row(align=True)
|
row = columns.row(align=True)
|
||||||
row.alignment = "RIGHT"
|
row.alignment = "RIGHT"
|
||||||
@@ -86,6 +83,22 @@ class LaunchTypeManager(bpy.types.Operator):
|
|||||||
op = row.operator("bim.change_type_page", icon="TRIA_RIGHT", text="")
|
op = row.operator("bim.change_type_page", icon="TRIA_RIGHT", text="")
|
||||||
op.page = AuthoringData.data["next_page"]
|
op.page = AuthoringData.data["next_page"]
|
||||||
|
|
||||||
|
if props.is_adding_type:
|
||||||
|
row = self.layout.row()
|
||||||
|
box = row.box()
|
||||||
|
row = box.row()
|
||||||
|
row.prop(props, "type_predefined_type")
|
||||||
|
row = box.row()
|
||||||
|
row.prop(props, "type_template")
|
||||||
|
row = box.row()
|
||||||
|
row.prop(props, "type_name")
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.operator("bim.add_type", icon="CHECKMARK", text="Save New Type")
|
||||||
|
row.operator("bim.disable_add_type", icon="CANCEL", text="")
|
||||||
|
else:
|
||||||
|
row = self.layout.row()
|
||||||
|
row.operator("bim.enable_add_type", icon="ADD", text="Create New Type")
|
||||||
|
|
||||||
flow = self.layout.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=True, align=True)
|
flow = self.layout.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=True, align=True)
|
||||||
|
|
||||||
for relating_type in AuthoringData.data["paginated_relating_types"]:
|
for relating_type in AuthoringData.data["paginated_relating_types"]:
|
||||||
|
|||||||
@@ -77,14 +77,15 @@ class JoinWall(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
if self.join_type in ("L", "V"):
|
if self.join_type in ("L", "V"):
|
||||||
if len(selected_objs) == 2:
|
if len(selected_objs) != 2:
|
||||||
another_selected_object = next(o for o in selected_objs if o != context.active_object)
|
self.report({"ERROR"}, f"It requires 2 selected objects to do join of type {self.join_type}")
|
||||||
if self.join_type == "L":
|
return {"CANCELLED"}
|
||||||
joiner.join_L(another_selected_object, context.active_object)
|
another_selected_object = next(o for o in selected_objs if o != context.active_object)
|
||||||
elif self.join_type == "V":
|
if self.join_type == "L":
|
||||||
joiner.join_V(another_selected_object, context.active_object)
|
joiner.join_L(another_selected_object, context.active_object)
|
||||||
self.report({"ERROR"}, f"It requires 2 selected objects to do join of type {self.join_type}")
|
elif self.join_type == "V":
|
||||||
return {"CANCELLED"}
|
joiner.join_V(another_selected_object, context.active_object)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
if self.join_type == "T":
|
if self.join_type == "T":
|
||||||
elements = [tool.Ifc.get_entity(o) for o in context.selected_objects]
|
elements = [tool.Ifc.get_entity(o) for o in context.selected_objects]
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from . import ui, prop, operator
|
|||||||
classes = (
|
classes = (
|
||||||
operator.AppendLibraryElement,
|
operator.AppendLibraryElement,
|
||||||
operator.AssignLibraryDeclaration,
|
operator.AssignLibraryDeclaration,
|
||||||
|
operator.AppendEntireLibrary,
|
||||||
operator.ChangeLibraryElement,
|
operator.ChangeLibraryElement,
|
||||||
operator.CreateProject,
|
operator.CreateProject,
|
||||||
operator.DisableEditingHeader,
|
operator.DisableEditingHeader,
|
||||||
@@ -33,10 +34,10 @@ classes = (
|
|||||||
operator.LoadLink,
|
operator.LoadLink,
|
||||||
operator.LoadProject,
|
operator.LoadProject,
|
||||||
operator.LoadProjectElements,
|
operator.LoadProjectElements,
|
||||||
|
operator.NewProject,
|
||||||
operator.RefreshLibrary,
|
operator.RefreshLibrary,
|
||||||
operator.RewindLibrary,
|
operator.RewindLibrary,
|
||||||
operator.SaveLibraryFile,
|
operator.SaveLibraryFile,
|
||||||
operator.AppendEntireLibrary,
|
|
||||||
operator.SelectLibraryFile,
|
operator.SelectLibraryFile,
|
||||||
operator.ToggleFilterCategories,
|
operator.ToggleFilterCategories,
|
||||||
operator.ToggleLinkVisibility,
|
operator.ToggleLinkVisibility,
|
||||||
@@ -48,6 +49,7 @@ classes = (
|
|||||||
prop.FilterCategory,
|
prop.FilterCategory,
|
||||||
prop.Link,
|
prop.Link,
|
||||||
prop.BIMProjectProperties,
|
prop.BIMProjectProperties,
|
||||||
|
ui.BIM_MT_project,
|
||||||
ui.BIM_PT_project,
|
ui.BIM_PT_project,
|
||||||
ui.BIM_PT_project_library,
|
ui.BIM_PT_project_library,
|
||||||
ui.BIM_PT_links,
|
ui.BIM_PT_links,
|
||||||
@@ -57,22 +59,11 @@ classes = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def menu_func_export(self, context):
|
|
||||||
op = self.layout.operator(operator.ExportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcjson)")
|
|
||||||
op.should_save_as = True
|
|
||||||
|
|
||||||
|
|
||||||
def menu_func_import(self, context):
|
|
||||||
self.layout.operator(operator.ImportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcxml)")
|
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.types.Scene.BIMProjectProperties = bpy.props.PointerProperty(type=prop.BIMProjectProperties)
|
bpy.types.Scene.BIMProjectProperties = bpy.props.PointerProperty(type=prop.BIMProjectProperties)
|
||||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
bpy.types.TOPBAR_MT_file.prepend(ui.file_menu)
|
||||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
|
bpy.types.TOPBAR_MT_file.remove(ui.file_menu)
|
||||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
|
||||||
del bpy.types.Scene.BIMProjectProperties
|
del bpy.types.Scene.BIMProjectProperties
|
||||||
|
|||||||
@@ -38,6 +38,53 @@ from blenderbim.bim import export_ifc
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class NewProject(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.new_project"
|
||||||
|
bl_label = "New Project"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Start a new IFC project in a fresh session"
|
||||||
|
preset: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.ops.wm.read_homefile()
|
||||||
|
|
||||||
|
for obj in bpy.data.objects:
|
||||||
|
bpy.data.objects.remove(obj)
|
||||||
|
|
||||||
|
if self.preset == "metric_m":
|
||||||
|
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||||
|
bpy.context.scene.unit_settings.system = "METRIC"
|
||||||
|
bpy.context.scene.unit_settings.length_unit = "METERS"
|
||||||
|
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
|
||||||
|
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
|
||||||
|
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||||
|
elif self.preset == "metric_mm":
|
||||||
|
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||||
|
bpy.context.scene.unit_settings.system = "METRIC"
|
||||||
|
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
|
||||||
|
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
|
||||||
|
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
|
||||||
|
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||||
|
elif self.preset == "imperial_ft":
|
||||||
|
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||||
|
bpy.context.scene.unit_settings.system = "IMPERIAL"
|
||||||
|
bpy.context.scene.unit_settings.length_unit = "FEET"
|
||||||
|
bpy.context.scene.BIMProperties.area_unit = "square foot"
|
||||||
|
bpy.context.scene.BIMProperties.volume_unit = "cubic foot"
|
||||||
|
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||||
|
elif self.preset == "demo":
|
||||||
|
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||||
|
bpy.context.scene.unit_settings.system = "METRIC"
|
||||||
|
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
|
||||||
|
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
|
||||||
|
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
|
||||||
|
bpy.context.scene.BIMProjectProperties.template_file = "IFC4 Demo Library.ifc"
|
||||||
|
|
||||||
|
if self.preset != "wizard":
|
||||||
|
bpy.ops.bim.create_project()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class CreateProject(bpy.types.Operator):
|
class CreateProject(bpy.types.Operator):
|
||||||
bl_idname = "bim.create_project"
|
bl_idname = "bim.create_project"
|
||||||
bl_label = "Create Project"
|
bl_label = "Create Project"
|
||||||
@@ -528,6 +575,7 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
|
|||||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcsqlite", options={"HIDDEN"})
|
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcsqlite", options={"HIDDEN"})
|
||||||
is_advanced: bpy.props.BoolProperty(name="Enable Advanced Mode", default=False)
|
is_advanced: bpy.props.BoolProperty(name="Enable Advanced Mode", default=False)
|
||||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||||
|
should_start_fresh_session: bpy.props.BoolProperty(name="Should Start Fresh Session", default=True)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if not self.is_existing_ifc_file():
|
if not self.is_existing_ifc_file():
|
||||||
@@ -540,6 +588,12 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
|
if self.should_start_fresh_session:
|
||||||
|
bpy.ops.wm.read_homefile()
|
||||||
|
|
||||||
|
for obj in bpy.data.objects:
|
||||||
|
bpy.data.objects.remove(obj)
|
||||||
|
|
||||||
context.window_manager.fileselect_add(self)
|
context.window_manager.fileselect_add(self)
|
||||||
return {"RUNNING_MODAL"}
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
@@ -828,6 +882,10 @@ class ExportIFC(bpy.types.Operator):
|
|||||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||||
save_as_invoked: bpy.props.BoolProperty(name="Save As Dialog Was Invoked", default=False, options={"HIDDEN"})
|
save_as_invoked: bpy.props.BoolProperty(name="Save As Dialog Was Invoked", default=False, options={"HIDDEN"})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return tool.Ifc.get()
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.prop(self, "json_version")
|
layout.prop(self, "json_version")
|
||||||
|
|||||||
@@ -18,11 +18,36 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from blenderbim.bim.helper import prop_with_search
|
from blenderbim.bim.helper import prop_with_search
|
||||||
from bpy.types import Panel, UIList
|
from bpy.types import Panel, Menu, UIList
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.project.data import ProjectData
|
from blenderbim.bim.module.project.data import ProjectData
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_MT_project(Menu):
|
||||||
|
bl_idname = "BIM_MT_project"
|
||||||
|
bl_label = "New IFC Project"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.operator("bim.new_project", text="New Metric (m) Project").preset = "metric_m"
|
||||||
|
layout.operator("bim.new_project", text="New Metric (mm) Project").preset = "metric_mm"
|
||||||
|
layout.operator("bim.new_project", text="New Imperial (ft) Project").preset = "imperial_ft"
|
||||||
|
layout.operator("bim.new_project", text="New Demo Project").preset = "demo"
|
||||||
|
layout.operator("bim.new_project", text="New Project Wizard").preset = "wizard"
|
||||||
|
|
||||||
|
|
||||||
|
def file_menu(self, context):
|
||||||
|
self.layout.menu("BIM_MT_project", icon="COLLECTION_NEW")
|
||||||
|
op = self.layout.operator("bim.load_project", text="Open IFC Project", icon="FILEBROWSER")
|
||||||
|
op.should_start_fresh_session = True
|
||||||
|
self.layout.separator()
|
||||||
|
op = self.layout.operator("export_ifc.bim", icon="FILE_TICK", text="Save IFC Project")
|
||||||
|
op.should_save_as = False
|
||||||
|
op = self.layout.operator("export_ifc.bim", text="Save IFC Project As...")
|
||||||
|
op.should_save_as = True
|
||||||
|
self.layout.separator()
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_project(Panel):
|
class BIM_PT_project(Panel):
|
||||||
bl_label = "IFC Project"
|
bl_label = "IFC Project"
|
||||||
bl_idname = "BIM_PT_project"
|
bl_idname = "BIM_PT_project"
|
||||||
@@ -160,10 +185,8 @@ class BIM_PT_project(Panel):
|
|||||||
row.operator("bim.select_ifc_file", icon="FILE_FOLDER", text="")
|
row.operator("bim.select_ifc_file", icon="FILE_FOLDER", text="")
|
||||||
|
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
op = row.operator("export_ifc.bim", icon="EXPORT", text="Save Project")
|
op = row.operator("export_ifc.bim", icon="FILE_TICK", text="Save Project")
|
||||||
op.should_save_as = False
|
op.should_save_as = False
|
||||||
op = row.operator("export_ifc.bim", icon="FILE_TICK", text="Save As")
|
|
||||||
op.should_save_as = True
|
|
||||||
row.operator("bim.unload_project", text="", icon="CANCEL")
|
row.operator("bim.unload_project", text="", icon="CANCEL")
|
||||||
|
|
||||||
def draw_create_project_ui(self, context):
|
def draw_create_project_ui(self, context):
|
||||||
@@ -182,7 +205,7 @@ class BIM_PT_project(Panel):
|
|||||||
|
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.operator("bim.create_project")
|
row.operator("bim.create_project")
|
||||||
row.operator("bim.load_project")
|
row.operator("bim.load_project").should_start_fresh_session = False
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_project_library(Panel):
|
class BIM_PT_project_library(Panel):
|
||||||
|
|||||||
@@ -320,7 +320,6 @@ class EditPset(bpy.types.Operator, Operator):
|
|||||||
tool.Blender.update_viewport()
|
tool.Blender.update_viewport()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SelectSimilarPsetValue(bpy.types.Operator):
|
class SelectSimilarPsetValue(bpy.types.Operator):
|
||||||
"""
|
"""
|
||||||
Selects objects with the same property value.
|
Selects objects with the same property value.
|
||||||
@@ -364,9 +363,9 @@ class SelectSimilarPsetValue(bpy.types.Operator):
|
|||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RemovePset(bpy.types.Operator, Operator):
|
class RemovePset(bpy.types.Operator, Operator):
|
||||||
bl_idname = "bim.remove_pset"
|
bl_idname = "bim.remove_pset"
|
||||||
bl_label = "Remove Pset"
|
bl_label = "Remove Pset"
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ class IfcClassData:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def name(cls):
|
def name(cls):
|
||||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
element = tool.Ifc.get_entity(bpy.context.view_layer.objects.active)
|
||||||
if not element:
|
if not element:
|
||||||
return
|
return
|
||||||
name = element.is_a()
|
name = element.is_a()
|
||||||
@@ -170,13 +170,13 @@ class IfcClassData:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ifc_class(cls):
|
def ifc_class(cls):
|
||||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
element = tool.Ifc.get_entity(bpy.context.view_layer.objects.active)
|
||||||
if element:
|
if element:
|
||||||
return element.is_a()
|
return element.is_a()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_reassign_class(cls):
|
def can_reassign_class(cls):
|
||||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
element = tool.Ifc.get_entity(bpy.context.view_layer.objects.active)
|
||||||
if element:
|
if element:
|
||||||
if element.is_a("IfcOpeningElement") or element.is_a("IfcOpeningStandardCase"):
|
if element.is_a("IfcOpeningElement") or element.is_a("IfcOpeningStandardCase"):
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -383,7 +383,7 @@ class AddType(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
elif template == "RAILING":
|
elif template == "RAILING":
|
||||||
mesh = bpy.data.meshes.new("IfcRailing")
|
mesh = bpy.data.meshes.new("IfcRailing")
|
||||||
obj = bpy.data.objects.new("TYPEX", mesh)
|
obj = bpy.data.objects.new(name, mesh)
|
||||||
element = blenderbim.core.root.assign_class(
|
element = blenderbim.core.root.assign_class(
|
||||||
tool.Ifc,
|
tool.Ifc,
|
||||||
tool.Collector,
|
tool.Collector,
|
||||||
@@ -402,7 +402,7 @@ class AddType(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
elif template == "ROOF":
|
elif template == "ROOF":
|
||||||
mesh = bpy.data.meshes.new("IfcRoof")
|
mesh = bpy.data.meshes.new("IfcRoof")
|
||||||
obj = bpy.data.objects.new("TYPEX", mesh)
|
obj = bpy.data.objects.new(name, mesh)
|
||||||
element = blenderbim.core.root.assign_class(
|
element = blenderbim.core.root.assign_class(
|
||||||
tool.Ifc,
|
tool.Ifc,
|
||||||
tool.Collector,
|
tool.Collector,
|
||||||
|
|||||||
@@ -335,6 +335,24 @@ class ModuleVisibility(PropertyGroup):
|
|||||||
is_visible: BoolProperty(name="Value", default=True, update=update_is_visible)
|
is_visible: BoolProperty(name="Value", default=True, update=update_is_visible)
|
||||||
|
|
||||||
|
|
||||||
|
class BIMAreaProperties(PropertyGroup):
|
||||||
|
tab: EnumProperty(
|
||||||
|
default="PROJECT",
|
||||||
|
items=[
|
||||||
|
("PROJECT", "Project Overview", "", "VIEW3D", 1),
|
||||||
|
("OBJECT", "Object Information", "", "FILE_3D", 2),
|
||||||
|
("MATERIALS", "Materials and Styles", "", "MATERIAL", 3),
|
||||||
|
("DRAWINGS", "Drawings and Documents", "", "DOCUMENTS", 4),
|
||||||
|
("SERVICES", "Services and Systems", "", "NETWORK_DRIVE", 5),
|
||||||
|
("STRUCTURE", "Structural Analysis", "", "EDITMODE_HLT", 6),
|
||||||
|
("SCHEDULING", "Construction Scheduling", "", "NLA", 7),
|
||||||
|
("FM", "Facility Management", "", "PACKAGE", 8),
|
||||||
|
("OTHER", "Other Properties", "", "COLLAPSEMENU", 9),
|
||||||
|
],
|
||||||
|
name="Tab",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BIMProperties(PropertyGroup):
|
class BIMProperties(PropertyGroup):
|
||||||
ui_preset: EnumProperty(
|
ui_preset: EnumProperty(
|
||||||
name="UI Preset",
|
name="UI Preset",
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
|||||||
spreadsheet_command: StringProperty(name="Spreadsheet Command", description="E.g. [['libreoffice', path]]")
|
spreadsheet_command: StringProperty(name="Spreadsheet Command", description="E.g. [['libreoffice', path]]")
|
||||||
openlca_port: IntProperty(name="OpenLCA IPC Port", default=8080)
|
openlca_port: IntProperty(name="OpenLCA IPC Port", default=8080)
|
||||||
should_hide_empty_props: BoolProperty(name="Should Hide Empty Properties", default=True)
|
should_hide_empty_props: BoolProperty(name="Should Hide Empty Properties", default=True)
|
||||||
|
should_setup_workspace: BoolProperty(name="Should Setup Workspace Layout for BIM", default=True)
|
||||||
should_play_chaching_sound: BoolProperty(
|
should_play_chaching_sound: BoolProperty(
|
||||||
name="Should Make A Cha-Ching Sound When Project Costs Updates", default=False
|
name="Should Make A Cha-Ching Sound When Project Costs Updates", default=False
|
||||||
)
|
)
|
||||||
@@ -209,6 +210,8 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
|||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(self, "should_hide_empty_props")
|
row.prop(self, "should_hide_empty_props")
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
|
row.prop(self, "should_setup_workspace")
|
||||||
|
row = layout.row()
|
||||||
row.prop(self, "should_play_chaching_sound")
|
row.prop(self, "should_play_chaching_sound")
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(self, "lock_grids_on_import")
|
row.prop(self, "lock_grids_on_import")
|
||||||
@@ -278,12 +281,30 @@ def ifc_units(self, context):
|
|||||||
|
|
||||||
|
|
||||||
# Scene panel groups
|
# Scene panel groups
|
||||||
|
class BIM_PT_root(Panel):
|
||||||
|
bl_label = "BlenderBIM Add-on"
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "scene"
|
||||||
|
bl_order = 0
|
||||||
|
bl_options = {"HIDE_HEADER"}
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
self.layout.prop(aprops, "tab", text="")
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_project_info(Panel):
|
class BIM_PT_project_info(Panel):
|
||||||
bl_label = "IFC Project Info"
|
bl_label = "IFC Project Info"
|
||||||
bl_space_type = "PROPERTIES"
|
bl_space_type = "PROPERTIES"
|
||||||
bl_region_type = "WINDOW"
|
bl_region_type = "WINDOW"
|
||||||
bl_context = "scene"
|
bl_context = "scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
return aprops.tab == "PROJECT"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -295,6 +316,11 @@ class BIM_PT_project_setup(Panel):
|
|||||||
bl_context = "scene"
|
bl_context = "scene"
|
||||||
bl_options = {"DEFAULT_CLOSED"}
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
return aprops.tab == "PROJECT"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -306,6 +332,11 @@ class BIM_PT_collaboration(Panel):
|
|||||||
bl_context = "scene"
|
bl_context = "scene"
|
||||||
bl_options = {"DEFAULT_CLOSED"}
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
return aprops.tab == "OTHER"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -319,7 +350,8 @@ class BIM_PT_selection(Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return tool.Ifc.get()
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
return aprops.tab == "PROJECT" and tool.Ifc.get()
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
pass
|
pass
|
||||||
@@ -334,7 +366,8 @@ class BIM_PT_geometry(Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return tool.Ifc.get()
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
return aprops.tab == "PROJECT" and tool.Ifc.get()
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
pass
|
pass
|
||||||
@@ -349,7 +382,8 @@ class BIM_PT_4D5D(Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return tool.Ifc.get()
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
return aprops.tab == "SCHEDULING" and tool.Ifc.get()
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
pass
|
pass
|
||||||
@@ -364,7 +398,8 @@ class BIM_PT_structural(Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return tool.Ifc.get()
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
return aprops.tab == "STRUCTURE" and tool.Ifc.get()
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
pass
|
pass
|
||||||
@@ -379,7 +414,8 @@ class BIM_PT_services(Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return tool.Ifc.get()
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
return aprops.tab == "SERVICES" and tool.Ifc.get()
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
pass
|
pass
|
||||||
@@ -392,6 +428,11 @@ class BIM_PT_quality_control(Panel):
|
|||||||
bl_context = "scene"
|
bl_context = "scene"
|
||||||
bl_options = {"DEFAULT_CLOSED"}
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
return aprops.tab == "OTHER"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -403,6 +444,11 @@ class BIM_PT_integrations(Panel):
|
|||||||
bl_context = "scene"
|
bl_context = "scene"
|
||||||
bl_options = {"DEFAULT_CLOSED"}
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
|
||||||
|
return aprops.tab == "OTHER"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -493,7 +539,7 @@ class UIData:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls):
|
def load(cls):
|
||||||
cls.data = { "version": cls.version() }
|
cls.data = {"version": cls.version()}
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ def rewind_brick_class(brick):
|
|||||||
|
|
||||||
def close_brick_project(brick):
|
def close_brick_project(brick):
|
||||||
brick.clear_project()
|
brick.clear_project()
|
||||||
|
brick.clear_brick_browser()
|
||||||
|
|
||||||
|
|
||||||
def convert_brick_project(ifc, brick):
|
def convert_brick_project(ifc, brick):
|
||||||
@@ -67,13 +68,13 @@ def assign_brick_reference(ifc, brick, element=None, library=None, brick_uri=Non
|
|||||||
brick.add_brickifc_reference(brick_uri, element, project)
|
brick.add_brickifc_reference(brick_uri, element, project)
|
||||||
|
|
||||||
|
|
||||||
def add_brick(ifc, brick, element=None, namespace=None, brick_class=None, library=None):
|
def add_brick(ifc, brick, element=None, namespace=None, brick_class=None, library=None, label="Unnamed"):
|
||||||
if element:
|
if element:
|
||||||
brick_uri = brick.add_brick_from_element(element, namespace, brick_class)
|
brick_uri = brick.add_brick_from_element(element, namespace, brick_class)
|
||||||
if library:
|
if library:
|
||||||
brick.run_assign_brick_reference(element=element, library=library, brick_uri=brick_uri)
|
brick.run_assign_brick_reference(element=element, library=library, brick_uri=brick_uri)
|
||||||
else:
|
else:
|
||||||
brick_uri = brick.add_brick(namespace, brick_class)
|
brick_uri = brick.add_brick(namespace, brick_class, label)
|
||||||
brick.run_refresh_brick_viewer()
|
brick.run_refresh_brick_viewer()
|
||||||
|
|
||||||
|
|
||||||
@@ -110,13 +111,19 @@ def remove_brick(ifc, brick, library=None, brick_uri=None):
|
|||||||
brick.remove_brick(brick_uri)
|
brick.remove_brick(brick_uri)
|
||||||
brick.run_refresh_brick_viewer()
|
brick.run_refresh_brick_viewer()
|
||||||
|
|
||||||
|
|
||||||
def undo_brick(brick):
|
def undo_brick(brick):
|
||||||
brick.undo_brick()
|
brick.undo_brick()
|
||||||
brick.run_refresh_brick_viewer()
|
brick.run_refresh_brick_viewer()
|
||||||
|
|
||||||
|
|
||||||
def redo_brick(brick):
|
def redo_brick(brick):
|
||||||
brick.redo_brick()
|
brick.redo_brick()
|
||||||
brick.run_refresh_brick_viewer()
|
brick.run_refresh_brick_viewer()
|
||||||
|
|
||||||
def serialize_brick(brick, file_name="BlenderBIMSerializeTest.ttl"):
|
|
||||||
brick.serialize_brick(file_name)
|
def serialize_brick(brick):
|
||||||
|
brick.serialize_brick()
|
||||||
|
|
||||||
|
def add_namespace(brick, alias=None, uri=None):
|
||||||
|
brick.add_namespace(alias, uri)
|
||||||
|
|||||||
@@ -80,3 +80,7 @@ def convert_global_to_local(georeference):
|
|||||||
coordinates = georeference.enh2xyz(georeference.get_coordinates("input"), georeference.get_map_conversion())
|
coordinates = georeference.enh2xyz(georeference.get_coordinates("input"), georeference.get_map_conversion())
|
||||||
georeference.set_coordinates("output", coordinates)
|
georeference.set_coordinates("output", coordinates)
|
||||||
georeference.set_cursor_location(coordinates)
|
georeference.set_cursor_location(coordinates)
|
||||||
|
|
||||||
|
def convert_angle_to_coord(georeference, type):
|
||||||
|
vector_coordinates = georeference.angle2coords(georeference.get_angle(type), type)
|
||||||
|
georeference.set_vector_coordinates(vector_coordinates,type)
|
||||||
@@ -370,13 +370,15 @@ class Geometry:
|
|||||||
|
|
||||||
@interface
|
@interface
|
||||||
class Georeference:
|
class Georeference:
|
||||||
|
def angle2coords(cls, angle, type): pass
|
||||||
def disable_editing(cls): pass
|
def disable_editing(cls): pass
|
||||||
def enable_editing(cls): pass
|
def enable_editing(cls): pass
|
||||||
def enh2xyz(cls, map_conversion, coordinates): pass
|
def enh2xyz(cls, map_conversion, coordinates): pass
|
||||||
|
def get_angle(cls, type): pass
|
||||||
def get_coordinates(cls, io): pass
|
def get_coordinates(cls, io): pass
|
||||||
def get_cursor_location(cls): pass
|
def get_cursor_location(cls): pass
|
||||||
def get_map_conversion(cls): pass
|
|
||||||
def get_map_conversion_attributes(cls): pass
|
def get_map_conversion_attributes(cls): pass
|
||||||
|
def get_map_conversion(cls): pass
|
||||||
def get_projected_crs_attributes(cls): pass
|
def get_projected_crs_attributes(cls): pass
|
||||||
def get_true_north_attributes(cls): pass
|
def get_true_north_attributes(cls): pass
|
||||||
def import_map_conversion(cls): pass
|
def import_map_conversion(cls): pass
|
||||||
@@ -388,6 +390,7 @@ class Georeference:
|
|||||||
def set_cursor_location(cls, coordinates): pass
|
def set_cursor_location(cls, coordinates): pass
|
||||||
def set_ifc_grid_north(cls): pass
|
def set_ifc_grid_north(cls): pass
|
||||||
def set_ifc_true_north(cls): pass
|
def set_ifc_true_north(cls): pass
|
||||||
|
def set_vector_coordinates(cls, vector_coordinates, type): pass
|
||||||
def xyz2enh(cls, map_conversion, coordinates): pass
|
def xyz2enh(cls, map_conversion, coordinates): pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ logger.setLevel(logging.ERROR)
|
|||||||
|
|
||||||
class Brick(blenderbim.core.tool.Brick):
|
class Brick(blenderbim.core.tool.Brick):
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_brick(cls, namespace, brick_class):
|
def add_brick(cls, namespace, brick_class, label):
|
||||||
ns = Namespace(namespace)
|
ns = Namespace(namespace)
|
||||||
brick = ns[ifcopenshell.guid.expand(ifcopenshell.guid.new())]
|
brick = ns[ifcopenshell.guid.expand(ifcopenshell.guid.new())]
|
||||||
with BrickStore.graph.new_changeset("PROJECT") as cs:
|
with BrickStore.graph.new_changeset("PROJECT") as cs:
|
||||||
cs.add((brick, RDF.type, URIRef(brick_class)))
|
cs.add((brick, RDF.type, URIRef(brick_class)))
|
||||||
cs.add((brick, URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal("Unnamed")))
|
cs.add((brick, URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal(label)))
|
||||||
return str(brick)
|
return str(brick)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -275,8 +275,6 @@ class Brick(blenderbim.core.tool.Brick):
|
|||||||
with BrickStore.graph.new_changeset("SCHEMA") as cs:
|
with BrickStore.graph.new_changeset("SCHEMA") as cs:
|
||||||
cs.load_file(BrickStore.schema)
|
cs.load_file(BrickStore.schema)
|
||||||
BrickStore.graph.bind("digitaltwin", Namespace("https://example.org/digitaltwin#"))
|
BrickStore.graph.bind("digitaltwin", Namespace("https://example.org/digitaltwin#"))
|
||||||
BrickStore.graph.bind("brick", Namespace("https://brickschema.org/schema/Brick#"))
|
|
||||||
BrickStore.graph.bind("rdfs", Namespace("http://www.w3.org/2000/01/rdf-schema#"))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def pop_brick_breadcrumb(cls):
|
def pop_brick_breadcrumb(cls):
|
||||||
@@ -332,12 +330,14 @@ class Brick(blenderbim.core.tool.Brick):
|
|||||||
BrickStore.graph.redo()
|
BrickStore.graph.redo()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def serialize_brick(cls, file_name):
|
def serialize_brick(cls):
|
||||||
#temporary file path, could either be user selected for "save as" or use the BrickStore.path for simply "save"
|
BrickStore.get_project().serialize(destination=BrickStore.path, format="turtle")
|
||||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
dest = os.path.join(cwd, "..", "bim", "schema", file_name)
|
@classmethod
|
||||||
BrickStore.get_project().serialize(destination=dest, format="turtle")
|
def add_namespace(cls, alias, uri):
|
||||||
|
BrickStore.graph.bind(alias, Namespace(uri))
|
||||||
|
# need some way to reload namespace enum view
|
||||||
|
|
||||||
class BrickStore:
|
class BrickStore:
|
||||||
schema = None # this is now a os path
|
schema = None # this is now a os path
|
||||||
path = None # file path if the project was loaded in
|
path = None # file path if the project was loaded in
|
||||||
|
|||||||
@@ -1499,7 +1499,10 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
|
|
||||||
filtered_elements = cls.get_drawing_elements(drawing) | cls.get_drawing_spaces(drawing)
|
filtered_elements = cls.get_drawing_elements(drawing) | cls.get_drawing_spaces(drawing)
|
||||||
for visible_obj in bpy.context.visible_objects:
|
for visible_obj in bpy.context.visible_objects:
|
||||||
hide = tool.Ifc.get_entity(visible_obj) not in filtered_elements
|
element = tool.Ifc.get_entity(visible_obj)
|
||||||
|
if not element:
|
||||||
|
continue
|
||||||
|
hide = element not in filtered_elements
|
||||||
if bpy.context.view_layer.objects.get(visible_obj.name):
|
if bpy.context.view_layer.objects.get(visible_obj.name):
|
||||||
visible_obj.hide_set(hide)
|
visible_obj.hide_set(hide)
|
||||||
visible_obj.hide_render = hide
|
visible_obj.hide_render = hide
|
||||||
|
|||||||
@@ -272,3 +272,27 @@ class Georeference(blenderbim.core.tool.Georeference):
|
|||||||
float(props.map_conversion.get("XAxisOrdinate").string_value),
|
float(props.map_conversion.get("XAxisOrdinate").string_value),
|
||||||
)
|
)
|
||||||
bpy.context.scene.sun_pos_properties.north_offset = -radians(angle)
|
bpy.context.scene.sun_pos_properties.north_offset = -radians(angle)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def angle2coords(cls, angle, type):
|
||||||
|
if type == "rel_x":
|
||||||
|
return ifcopenshell.util.geolocation.angle2xaxis(angle)
|
||||||
|
elif type == "rel_y":
|
||||||
|
return ifcopenshell.util.geolocation.angle2yaxis(angle)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_angle(cls, type):
|
||||||
|
if type == "rel_x":
|
||||||
|
return bpy.context.scene.BIMGeoreferenceProperties.angle_degree_input_x
|
||||||
|
elif type == "rel_y":
|
||||||
|
return bpy.context.scene.BIMGeoreferenceProperties.angle_degree_input_y
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_vector_coordinates(cls, vector_coordinates, type):
|
||||||
|
x, y = vector_coordinates
|
||||||
|
if type == "rel_x":
|
||||||
|
bpy.context.scene.BIMGeoreferenceProperties.x_axis_abscissa_output = str(x)
|
||||||
|
bpy.context.scene.BIMGeoreferenceProperties.x_axis_ordinate_output = str(y)
|
||||||
|
elif type == "rel_y":
|
||||||
|
bpy.context.scene.BIMGeoreferenceProperties.y_axis_abscissa_output = str(x)
|
||||||
|
bpy.context.scene.BIMGeoreferenceProperties.y_axis_ordinate_output = str(y)
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ class Ifc(blenderbim.core.tool.Ifc):
|
|||||||
ifc_path = cls.get_path()
|
ifc_path = cls.get_path()
|
||||||
if os.path.isfile(ifc_path):
|
if os.path.isfile(ifc_path):
|
||||||
ifc_path = os.path.dirname(ifc_path)
|
ifc_path = os.path.dirname(ifc_path)
|
||||||
return uri if not uri or os.path.isabs(uri) else os.path.join(ifc_path, uri)
|
return (uri if not uri or os.path.isabs(uri) else os.path.join(ifc_path, uri)).replace("\\", "/")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_relative_uri(cls, uri):
|
def get_relative_uri(cls, uri):
|
||||||
@@ -112,7 +112,7 @@ class Ifc(blenderbim.core.tool.Ifc):
|
|||||||
ifc_path = cls.get_path()
|
ifc_path = cls.get_path()
|
||||||
if os.path.isfile(ifc_path):
|
if os.path.isfile(ifc_path):
|
||||||
ifc_path = os.path.dirname(ifc_path)
|
ifc_path = os.path.dirname(ifc_path)
|
||||||
return os.path.relpath(uri, ifc_path)
|
return os.path.relpath(uri, ifc_path).replace("\\", "/")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def unlink(cls, element=None, obj=None):
|
def unlink(cls, element=None, obj=None):
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ class Project(blenderbim.core.tool.Project):
|
|||||||
# TODO refactor
|
# TODO refactor
|
||||||
filepath = os.path.join(bpy.context.scene.BIMProperties.data_dir, "libraries", template)
|
filepath = os.path.join(bpy.context.scene.BIMProperties.data_dir, "libraries", template)
|
||||||
bpy.ops.bim.select_library_file(filepath=filepath)
|
bpy.ops.bim.select_library_file(filepath=filepath)
|
||||||
|
if IfcStore.library_file.schema != tool.Ifc.get().schema:
|
||||||
|
return
|
||||||
for element in IfcStore.library_file.by_type("IfcTypeProduct"):
|
for element in IfcStore.library_file.by_type("IfcTypeProduct"):
|
||||||
bpy.ops.bim.append_library_element(definition=element.id())
|
bpy.ops.bim.append_library_element(definition=element.id())
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ def create_z_profile_lips_curve(ifc_file, FirstFlangeWidth, SecondFlangeWidth, D
|
|||||||
|
|
||||||
return ifc_curve
|
return ifc_curve
|
||||||
|
|
||||||
|
|
||||||
class LibraryGenerator:
|
class LibraryGenerator:
|
||||||
def generate(self, parse_profiles_type="EU", output_filename="IFC4 EU Steel.ifc"):
|
def generate(self, parse_profiles_type="EU", output_filename="IFC4 EU Steel.ifc"):
|
||||||
ifcopenshell.api.pre_listeners = {}
|
ifcopenshell.api.pre_listeners = {}
|
||||||
@@ -171,10 +172,13 @@ class LibraryGenerator:
|
|||||||
"profile_z_lips": ("IfcArbitraryClosedProfileDef", {"t": "WallThickness", "c1": "FirstFlangeWidth", "c2": "SecondFlangeWidth", "h": "Depth", "r": "FilletRadius", "ll": "Girth"}),
|
"profile_z_lips": ("IfcArbitraryClosedProfileDef", {"t": "WallThickness", "c1": "FirstFlangeWidth", "c2": "SecondFlangeWidth", "h": "Depth", "r": "FilletRadius", "ll": "Girth"}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
US_profiles = ("ibeam_w_imp", )
|
||||||
if parse_profiles_type == "AU":
|
if parse_profiles_type == "AU":
|
||||||
bolt_class_filter = lambda x: "bluescope" in x.id
|
bolt_class_filter = lambda x: "bluescope" in x.id
|
||||||
elif parse_profiles_type == "EU":
|
elif parse_profiles_type == "EU":
|
||||||
bolt_class_filter = lambda x: "bluescope" not in x.id
|
bolt_class_filter = lambda x: "bluescope" not in x.id and x.id not in US_profiles
|
||||||
|
elif parse_profiles_type == "US":
|
||||||
|
bolt_class_filter = lambda x: x.id in US_profiles
|
||||||
else:
|
else:
|
||||||
bolt_class_filter = lambda x: True
|
bolt_class_filter = lambda x: True
|
||||||
|
|
||||||
@@ -195,12 +199,27 @@ class LibraryGenerator:
|
|||||||
# like hollow_generic_square
|
# like hollow_generic_square
|
||||||
continue
|
continue
|
||||||
|
|
||||||
bolts_cols = bolt_class.parameters.tables[0].columns
|
bolts_cols_original = bolt_class.parameters.tables[0].columns
|
||||||
bolts_cols = [ifc_params_translation.get(c, "unused") for c in bolts_cols]
|
bolts_cols = [ifc_params_translation.get(c, "unused") for c in bolts_cols_original]
|
||||||
|
|
||||||
|
inch_to_mm = lambda x: x * 0.0254 * 1000
|
||||||
|
def assure_data_units_is_mm(data, units):
|
||||||
|
data = data.copy()
|
||||||
|
for i in range(len(units)):
|
||||||
|
unit = units[i]
|
||||||
|
assert unit in ("Length (in)", "Length (mm)")
|
||||||
|
if unit != "Length (in)":
|
||||||
|
continue
|
||||||
|
for profile in data:
|
||||||
|
data[profile][i] = inch_to_mm(data[profile][i])
|
||||||
|
return data
|
||||||
|
|
||||||
bolts_data = bolt_class.parameters.tables[0].data
|
bolts_data = bolt_class.parameters.tables[0].data
|
||||||
|
data_units = [bolt_class.parameters.types[col] for col in bolts_cols_original]
|
||||||
|
bolts_data = assure_data_units_is_mm(bolts_data, data_units)
|
||||||
|
|
||||||
for prof_name in bolts_data.keys():
|
for prof_name in bolts_data.keys():
|
||||||
ifc_params = dict(zip(bolts_cols, bolts_data[prof_name]))
|
ifc_params = dict(zip(bolts_cols, bolts_data[prof_name], strict=True))
|
||||||
if "unused" in ifc_params:
|
if "unused" in ifc_params:
|
||||||
del ifc_params["unused"]
|
del ifc_params["unused"]
|
||||||
|
|
||||||
@@ -240,3 +259,4 @@ if __name__ == "__main__":
|
|||||||
path = Path(__file__).parents[1] / "blenderbim/bim/data/libraries"
|
path = Path(__file__).parents[1] / "blenderbim/bim/data/libraries"
|
||||||
LibraryGenerator().generate(parse_profiles_type="EU", output_filename=str(path / "IFC4 EU Steel.ifc"))
|
LibraryGenerator().generate(parse_profiles_type="EU", output_filename=str(path / "IFC4 EU Steel.ifc"))
|
||||||
LibraryGenerator().generate(parse_profiles_type="AU", output_filename=str(path / "IFC4 AU Steel.ifc"))
|
LibraryGenerator().generate(parse_profiles_type="AU", output_filename=str(path / "IFC4 AU Steel.ifc"))
|
||||||
|
LibraryGenerator().generate(parse_profiles_type="US", output_filename=str(path / "IFC4 US Steel.ifc"))
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape
|
|||||||
TopoDS_Shape temp;
|
TopoDS_Shape temp;
|
||||||
double d;
|
double d;
|
||||||
if (util::fit_halfspace(s1, s2, temp, d, getValue(GV_PRECISION) * 1000.)) {
|
if (util::fit_halfspace(s1, s2, temp, d, getValue(GV_PRECISION) * 1000.)) {
|
||||||
// #2665 we also set a precision-independent treshold, because in the boolean op routine
|
// #2665 we also set a precision-independent threshold, because in the boolean op routine
|
||||||
// the working fuzziness might still be increased.
|
// the working fuzziness might still be increased.
|
||||||
if (d < getValue(GV_PRECISION) * 20. || d < 0.00002) {
|
if (d < getValue(GV_PRECISION) * 20. || d < 0.00002) {
|
||||||
Logger::Message(Logger::LOG_WARNING, "Halfspace subtraction yields unchanged volume:", l);
|
Logger::Message(Logger::LOG_WARNING, "Halfspace subtraction yields unchanged volume:", l);
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (converted_segments.Extent() == 0) {
|
if (converted_segments.Extent() == 0) {
|
||||||
Logger::Message(Logger::LOG_ERROR, "No segment succesfully converted:", l);
|
Logger::Message(Logger::LOG_ERROR, "No segment successfully converted:", l);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCurveBoundedPlane* l, TopoDS_S
|
|||||||
ShapeFix_Shape sfs(mf.Face());
|
ShapeFix_Shape sfs(mf.Face());
|
||||||
sfs.Perform();
|
sfs.Perform();
|
||||||
|
|
||||||
// `trsf` consitutes the placement of the plane and therefore has unit scale factor
|
// `trsf` constitutes the placement of the plane and therefore has unit scale factor
|
||||||
face = TopoDS::Face(sfs.Shape()).Moved(trsf);
|
face = TopoDS::Face(sfs.Shape()).Moved(trsf);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& result)
|
|||||||
TopoDS_Shape surface_shape;
|
TopoDS_Shape surface_shape;
|
||||||
if (!convert_shape(fs->FaceSurface(), surface_shape)) return false;
|
if (!convert_shape(fs->FaceSurface(), surface_shape)) return false;
|
||||||
|
|
||||||
// FIXME: Assert this obtaines the only face
|
// FIXME: Assert this obtains the only face
|
||||||
TopExp_Explorer exp(surface_shape, TopAbs_FACE);
|
TopExp_Explorer exp(surface_shape, TopAbs_FACE);
|
||||||
if (!exp.More()) return false;
|
if (!exp.More()) return false;
|
||||||
|
|
||||||
|
|||||||
@@ -1239,7 +1239,7 @@ bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const IfcRepre
|
|||||||
typedef std::vector< std::vector<Handle_Geom_Surface> > result_t;
|
typedef std::vector< std::vector<Handle_Geom_Surface> > result_t;
|
||||||
endpoint_connections_t endpoint_connections;
|
endpoint_connections_t endpoint_connections;
|
||||||
|
|
||||||
// Find the semantic connections ot other wall elements when they are not connected 'AT_PATH' because
|
// Find the semantic connections to other wall elements when they are not connected 'AT_PATH' because
|
||||||
// in that latter case no folds need to be made.
|
// in that latter case no folds need to be made.
|
||||||
for (IfcSchema::IfcRelConnectsPathElements::list::it it = connections->begin(); it != connections->end(); ++it) {
|
for (IfcSchema::IfcRelConnectsPathElements::list::it it = connections->begin(); it != connections->end(); ++it) {
|
||||||
IfcSchema::IfcRelConnectsPathElements* connection = *it;
|
IfcSchema::IfcRelConnectsPathElements* connection = *it;
|
||||||
@@ -1360,7 +1360,7 @@ bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const IfcRepre
|
|||||||
// range. It's only a safeguard though, so can probably be approximated.
|
// range. It's only a safeguard though, so can probably be approximated.
|
||||||
const double axis_length = own_axis_start.Distance(own_axis_end);
|
const double axis_length = own_axis_start.Distance(own_axis_end);
|
||||||
if (length_required > axis_length) {
|
if (length_required > axis_length) {
|
||||||
Logger::Warning("The wall axis is not long enough to accomodate the fold points");
|
Logger::Warning("The wall axis is not long enough to accommodate the fold points");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1455,7 +1455,7 @@ bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const IfcRepre
|
|||||||
result_t::iterator result_vector = result.begin() + 1;
|
result_t::iterator result_vector = result.begin() + 1;
|
||||||
|
|
||||||
// nb The first layer is never folded, because it corresponds
|
// nb The first layer is never folded, because it corresponds
|
||||||
// to one of the longitudonal faces of the wall. Hence the +1
|
// to one of the longitudinal faces of the wall. Hence the +1
|
||||||
for (surfaces_t::const_iterator jt = surfaces.begin() + 1; jt != surfaces.end() - 1; ++jt, ++result_vector) {
|
for (surfaces_t::const_iterator jt = surfaces.begin() + 1; jt != surfaces.end() - 1; ++jt, ++result_vector) {
|
||||||
layer_offset += *thickness++;
|
layer_offset += *thickness++;
|
||||||
|
|
||||||
|
|||||||
@@ -700,7 +700,7 @@ namespace IfcGeom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this represenation has (or will be) processed as part its mapped representation
|
// Check if this representation has (or will be) processed as part its mapped representation
|
||||||
bool representation_processed_as_mapped_item = false;
|
bool representation_processed_as_mapped_item = false;
|
||||||
IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation);
|
IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation);
|
||||||
if (representation_mapped_to) {
|
if (representation_mapped_to) {
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrimmedCurve* l, TopoDS_Wire&
|
|||||||
// Fix from @sanderboer to compare using model tolerance, see #744
|
// Fix from @sanderboer to compare using model tolerance, see #744
|
||||||
// Made dependent on radius, see #928
|
// Made dependent on radius, see #928
|
||||||
|
|
||||||
// A good critereon for determining whether to take full curve
|
// A good criterion for determining whether to take full curve
|
||||||
// or trimmed segment would be whether there are other curve segments or this
|
// or trimmed segment would be whether there are other curve segments or this
|
||||||
// is the only one.
|
// is the only one.
|
||||||
boost::optional<size_t> num_segments;
|
boost::optional<size_t> num_segments;
|
||||||
@@ -227,7 +227,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrimmedCurve* l, TopoDS_Wire&
|
|||||||
TopoDS_Vertex v0, v1;
|
TopoDS_Vertex v0, v1;
|
||||||
TopExp::Vertices(e, v0, v1);
|
TopExp::Vertices(e, v0, v1);
|
||||||
e = TopoDS::Edge(BRepBuilderAPI_MakeEdge(v0, v1).Edge().Oriented(e.Orientation()));
|
e = TopoDS::Edge(BRepBuilderAPI_MakeEdge(v0, v1).Edge().Oriented(e.Orientation()));
|
||||||
Logger::Warning("Subsituted edge with linear approximation", l);
|
Logger::Warning("Substituted edge with linear approximation", l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ IfcGeom::Kernel::faceset_helper<CP, LP>::faceset_helper(
|
|||||||
// double bdiff = std::sqrt(box.SquareExtent());
|
// double bdiff = std::sqrt(box.SquareExtent());
|
||||||
|
|
||||||
// @todo the bounding box diagonal is not used (see above)
|
// @todo the bounding box diagonal is not used (see above)
|
||||||
// because we're explicitly interested in the miminal
|
// because we're explicitly interested in the minimal
|
||||||
// dimension of the element to limit the tolerance (for sheet-
|
// dimension of the element to limit the tolerance (for sheet-
|
||||||
// like elements for example). But the way below is very
|
// like elements for example). But the way below is very
|
||||||
// dependent on orientation due to the usage of the
|
// dependent on orientation due to the usage of the
|
||||||
|
|||||||
@@ -589,7 +589,7 @@ bool IfcGeom::util::create_solid_from_faces(const TopTools_ListOfShape& face_lis
|
|||||||
bool has_shared_edges = false;
|
bool has_shared_edges = false;
|
||||||
TopTools_MapOfShape edge_set;
|
TopTools_MapOfShape edge_set;
|
||||||
|
|
||||||
// In case there are wire interesections or failures in non-planar wire triangulations
|
// In case there are wire intersections or failures in non-planar wire triangulations
|
||||||
// the idea is to let occt do an exhaustive search of edge partners. But we have not
|
// the idea is to let occt do an exhaustive search of edge partners. But we have not
|
||||||
// found a case where this actually improves boolean ops later on.
|
// found a case where this actually improves boolean ops later on.
|
||||||
// if (!faceset_helper_ || !faceset_helper_->non_manifold()) {
|
// if (!faceset_helper_ || !faceset_helper_->non_manifold()) {
|
||||||
|
|||||||
@@ -1183,9 +1183,9 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
PERF("boolean operation: manifoldness check excemption");
|
PERF("boolean operation: manifoldness check exemption");
|
||||||
|
|
||||||
// An excemption for the requirement to be manifold: When the cut operands have overlapping edge belonging to faces that do not overlap.
|
// An exemption for the requirement to be manifold: When the cut operands have overlapping edge belonging to faces that do not overlap.
|
||||||
bool operands_nonmanifold = false;
|
bool operands_nonmanifold = false;
|
||||||
if (op == BOPAlgo_CUT) {
|
if (op == BOPAlgo_CUT) {
|
||||||
TopTools_IndexedMapOfShape edges;
|
TopTools_IndexedMapOfShape edges;
|
||||||
@@ -1248,7 +1248,7 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To
|
|||||||
bool has_open_shells = false;
|
bool has_open_shells = false;
|
||||||
|
|
||||||
if (op == BOPAlgo_CUT) {
|
if (op == BOPAlgo_CUT) {
|
||||||
PERF("boolean operation: open shell face adition check");
|
PERF("boolean operation: open shell face addition check");
|
||||||
|
|
||||||
for (TopExp_Explorer exp(a, TopAbs_SHELL); exp.More(); exp.Next()) {
|
for (TopExp_Explorer exp(a, TopAbs_SHELL); exp.More(); exp.Next()) {
|
||||||
if (!exp.Current().Closed()) {
|
if (!exp.Current().Closed()) {
|
||||||
|
|||||||
@@ -43,12 +43,12 @@ changes in the IfcOpenShell C++ core.
|
|||||||
.. _py39-linux64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-39-v0.7.0-fc50bdd-linux64.zip
|
.. _py39-linux64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-39-v0.7.0-fc50bdd-linux64.zip
|
||||||
.. _py310-linux64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-310-v0.7.0-fc50bdd-linux64.zip
|
.. _py310-linux64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-310-v0.7.0-fc50bdd-linux64.zip
|
||||||
.. _py311-linux64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-311-v0.7.0-fc50bdd-linux64.zip
|
.. _py311-linux64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-311-v0.7.0-fc50bdd-linux64.zip
|
||||||
.. _py36-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-36-v0.7.0-fc50bdd-win64.zip
|
.. _py36-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-36-v0.7.0-fc50bdd-win32.zip
|
||||||
.. _py37-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-37-v0.7.0-fc50bdd-win64.zip
|
.. _py37-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-37-v0.7.0-fc50bdd-win32.zip
|
||||||
.. _py38-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-38-v0.7.0-fc50bdd-win64.zip
|
.. _py38-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-38-v0.7.0-fc50bdd-win32.zip
|
||||||
.. _py39-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-39-v0.7.0-fc50bdd-win64.zip
|
.. _py39-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-39-v0.7.0-fc50bdd-win32.zip
|
||||||
.. _py310-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-310-v0.7.0-fc50bdd-win64.zip
|
.. _py310-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-310-v0.7.0-fc50bdd-win32.zip
|
||||||
.. _py311-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-311-v0.7.0-fc50bdd-win64.zip
|
.. _py311-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-311-v0.7.0-fc50bdd-win32.zip
|
||||||
.. _py36-win64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-36-v0.7.0-fc50bdd-win64.zip
|
.. _py36-win64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-36-v0.7.0-fc50bdd-win64.zip
|
||||||
.. _py37-win64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-37-v0.7.0-fc50bdd-win64.zip
|
.. _py37-win64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-37-v0.7.0-fc50bdd-win64.zip
|
||||||
.. _py38-win64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-38-v0.7.0-fc50bdd-win64.zip
|
.. _py38-win64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-38-v0.7.0-fc50bdd-win64.zip
|
||||||
|
|||||||
@@ -188,4 +188,28 @@ def register_schema(schema):
|
|||||||
register_schema_attributes(schema.schema)
|
register_schema_attributes(schema.schema)
|
||||||
|
|
||||||
|
|
||||||
|
def schema_by_name(schema=None, schema_version=None):
|
||||||
|
"""Returns an object allowing you to query the IFC schema itself
|
||||||
|
|
||||||
|
:param schema: Which IFC schema to use, chosen from "IFC2X3", "IFC4",
|
||||||
|
or "IFC4X3". These refer to the ISO approved versions of IFC.
|
||||||
|
:type schema: string
|
||||||
|
:param schema_version: If you want to specify an exact version of IFC
|
||||||
|
that may not be an ISO approved version, use this argument instead
|
||||||
|
of ``schema``. IFC versions on technical.buildingsmart.org are
|
||||||
|
described using 4 integers representing the major, minor, addendum,
|
||||||
|
and corrigendum number. For example, (4, 0, 2, 1) refers to IFC4
|
||||||
|
ADD2 TC1, which is the official version approved by ISO when people
|
||||||
|
refer to "IFC4". Generally you should not use this argument unless
|
||||||
|
you are testing non-ISO IFC releases.
|
||||||
|
:type schema_version: tuple[int]
|
||||||
|
"""
|
||||||
|
if schema_version:
|
||||||
|
prefixes = ("IFC", "X", "_ADD", "_TC")
|
||||||
|
schema = "".join("".join(map(str, t)) if t[1] else "" for t in zip(prefixes, schema_version))
|
||||||
|
else:
|
||||||
|
schema = {"IFC4X3": "IFC4X3_ADD1"}.get(schema, schema)
|
||||||
|
return ifcopenshell_wrapper.schema_by_name(schema)
|
||||||
|
|
||||||
|
|
||||||
from .main import *
|
from .main import *
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ def run(f, logger):
|
|||||||
cd = compile(a, f"{f.schema}.py", "exec")
|
cd = compile(a, f"{f.schema}.py", "exec")
|
||||||
scope = {}
|
scope = {}
|
||||||
exec(cd, scope)
|
exec(cd, scope)
|
||||||
S = ifcopenshell.ifcopenshell_wrapper.schema_by_name(f.schema)
|
S = ifcopenshell.ifcopenshell_wrapper.schema_by_name(f.schema_identifier)
|
||||||
|
|
||||||
rules = list(filter(lambda x: hasattr(x, "SCOPE"), scope.values()))
|
rules = list(filter(lambda x: hasattr(x, "SCOPE"), scope.values()))
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ from __future__ import division
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
import re
|
||||||
import numbers
|
import numbers
|
||||||
import functools
|
|
||||||
import zipfile
|
import zipfile
|
||||||
|
import functools
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.file
|
import ifcopenshell.util.file
|
||||||
@@ -191,8 +192,52 @@ class file(object):
|
|||||||
print(products[0] == ifc_file[122] == ifc_file["2XQ$n5SLP5MBLyL442paFx"]) # True
|
print(products[0] == ifc_file[122] == ifc_file["2XQ$n5SLP5MBLyL442paFx"]) # True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, f=None, schema=None):
|
def __init__(self, f=None, schema=None, schema_version=None):
|
||||||
"""Create a new file object"""
|
"""Create a new blank IFC model
|
||||||
|
|
||||||
|
This IFC model does not have any entities in it yet. See the
|
||||||
|
``create_entity`` function for how to create new entities. All data is
|
||||||
|
stored in memory. If you wish to write the IFC model to disk, see the
|
||||||
|
``write`` function.
|
||||||
|
|
||||||
|
:param f: The underlying IfcOpenShell file object to be wrapped. This
|
||||||
|
is an internal implementation detail and should generally be left
|
||||||
|
as None by users.
|
||||||
|
:param schema: Which IFC schema to use, chosen from "IFC2X3", "IFC4",
|
||||||
|
or "IFC4X3". These refer to the ISO approved versions of IFC.
|
||||||
|
Defaults to "IFC4" if not specified, which is currently recommended
|
||||||
|
for all new projects.
|
||||||
|
:type schema: string
|
||||||
|
:param schema_version: If you want to specify an exact version of IFC
|
||||||
|
that may not be an ISO approved version, use this argument instead
|
||||||
|
of ``schema``. IFC versions on technical.buildingsmart.org are
|
||||||
|
described using 4 integers representing the major, minor, addendum,
|
||||||
|
and corrigendum number. For example, (4, 0, 2, 1) refers to IFC4
|
||||||
|
ADD2 TC1, which is the official version approved by ISO when people
|
||||||
|
refer to "IFC4". Generally you should not use this argument unless
|
||||||
|
you are testing non-ISO IFC releases.
|
||||||
|
:type schema_version: tuple[int]
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
# Create a new IFC4 model, create a wall, then save it to an IFC-SPF file.
|
||||||
|
model = ifcopenshell.file()
|
||||||
|
model.create_entity("IfcWall")
|
||||||
|
model.write("/path/to/model.ifc")
|
||||||
|
|
||||||
|
# Create a new IFC4X3 model
|
||||||
|
model = ifcopenshell.file(schema="IFC4X3")
|
||||||
|
|
||||||
|
# A poweruser testing out a particular version of IFC4X3
|
||||||
|
model = ifcopenshell.file(schema_version=(4, 3, 0, 1))
|
||||||
|
"""
|
||||||
|
if schema_version:
|
||||||
|
prefixes = ("IFC", "X", "_ADD", "_TC")
|
||||||
|
schema = "".join("".join(map(str, t)) if t[1] else "" for t in zip(prefixes, schema_version))
|
||||||
|
else:
|
||||||
|
schema = {"IFC4X3": "IFC4X3_ADD1"}.get(schema, schema)
|
||||||
if f is not None:
|
if f is not None:
|
||||||
self.wrapped_data = f
|
self.wrapped_data = f
|
||||||
else:
|
else:
|
||||||
@@ -306,6 +351,17 @@ class file(object):
|
|||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if attr[0:6] == "create":
|
if attr[0:6] == "create":
|
||||||
return functools.partial(self.create_entity, attr[6:])
|
return functools.partial(self.create_entity, attr[6:])
|
||||||
|
elif attr == "schema":
|
||||||
|
return {"IFC4X3_ADD1": "IFC4X3"}.get(self.wrapped_data.schema, self.wrapped_data.schema)
|
||||||
|
elif attr == "schema_identifier":
|
||||||
|
return self.wrapped_data.schema
|
||||||
|
elif attr == "schema_version":
|
||||||
|
schema = self.wrapped_data.schema
|
||||||
|
version = []
|
||||||
|
for prefix in ("IFC", "X", "_ADD", "_TC"):
|
||||||
|
number = re.search(prefix + r"(\d)", schema)
|
||||||
|
version.append(int(number.group(1)) if number else 0)
|
||||||
|
return tuple(version)
|
||||||
else:
|
else:
|
||||||
return getattr(self.wrapped_data, attr)
|
return getattr(self.wrapped_data, attr)
|
||||||
|
|
||||||
|
|||||||
@@ -62,9 +62,7 @@ class sqlite(file):
|
|||||||
self.ifc_class_references = {}
|
self.ifc_class_references = {}
|
||||||
self.ifc_class_inverses = {}
|
self.ifc_class_inverses = {}
|
||||||
|
|
||||||
for declaration in self.ifc_schema.declarations():
|
for declaration in self.ifc_schema.entities():
|
||||||
if not str(declaration).startswith("<entity"):
|
|
||||||
continue
|
|
||||||
# print('Dealing with declaration', declaration.name())
|
# print('Dealing with declaration', declaration.name())
|
||||||
|
|
||||||
self.ifc_class_subtypes[declaration.name()] = ifcopenshell.util.schema.get_subtypes(declaration)
|
self.ifc_class_subtypes[declaration.name()] = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||||
|
|||||||
@@ -167,10 +167,7 @@ try:
|
|||||||
self.ifc_class_references = {}
|
self.ifc_class_references = {}
|
||||||
self.ifc_class_inverses = {}
|
self.ifc_class_inverses = {}
|
||||||
|
|
||||||
for declaration in self.ifc_schema.declarations():
|
for declaration in self.ifc_schema.entities():
|
||||||
if not str(declaration).startswith("<entity"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.ifc_class_names[declaration.name().upper()] = declaration.name()
|
self.ifc_class_names[declaration.name().upper()] = declaration.name()
|
||||||
|
|
||||||
self.ifc_class_subtypes[declaration.name()] = ifcopenshell.util.schema.get_subtypes(declaration)
|
self.ifc_class_subtypes[declaration.name()] = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||||
|
|||||||
@@ -187,3 +187,18 @@ def get_true_north(ifc_file):
|
|||||||
except:
|
except:
|
||||||
return 0
|
return 0
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# Used for converting an angle in degrees to return the X and Y vectors of the X Axis in IFC grid north geolocation:
|
||||||
|
def angle2xaxis(angle):
|
||||||
|
angle_rad = math.radians(angle)
|
||||||
|
x = math.cos(angle_rad)
|
||||||
|
y = - math.sin(angle_rad)
|
||||||
|
return x, y
|
||||||
|
|
||||||
|
# Used for converting True North angle as seen in CAD (relative to +Y)
|
||||||
|
def angle2yaxis(angle):
|
||||||
|
angle_rad = math.radians(angle)
|
||||||
|
x = - math.sin(angle_rad)
|
||||||
|
y = math.cos(angle_rad)
|
||||||
|
return x, y
|
||||||
|
|||||||
@@ -156,6 +156,20 @@ class TestFile(test.bootstrap.IFC4):
|
|||||||
def test_creating_a_new_file(self):
|
def test_creating_a_new_file(self):
|
||||||
f = ifcopenshell.file(schema="IFC4")
|
f = ifcopenshell.file(schema="IFC4")
|
||||||
assert f.schema == "IFC4"
|
assert f.schema == "IFC4"
|
||||||
|
assert f.schema_identifier == "IFC4"
|
||||||
|
assert f.schema_version == (4, 0, 0, 0)
|
||||||
|
|
||||||
|
def test_creating_an_ifc4x3_file(self):
|
||||||
|
f = ifcopenshell.file(schema="IFC4X3")
|
||||||
|
assert f.schema == "IFC4X3"
|
||||||
|
assert f.schema_identifier == "IFC4X3_ADD1"
|
||||||
|
assert f.schema_version == (4, 3, 1, 0)
|
||||||
|
|
||||||
|
def test_creating_a_specific_version(self):
|
||||||
|
f = ifcopenshell.file(schema_version=(4, 3, 1, 0))
|
||||||
|
assert f.schema == "IFC4X3"
|
||||||
|
assert f.schema_identifier == "IFC4X3_ADD1"
|
||||||
|
assert f.schema_version == (4, 3, 1, 0)
|
||||||
|
|
||||||
def test_creating_an_entity(self):
|
def test_creating_an_entity(self):
|
||||||
element = self.file.create_entity("IfcPerson")
|
element = self.file.create_entity("IfcPerson")
|
||||||
|
|||||||
@@ -46,10 +46,46 @@ class Patcher:
|
|||||||
|
|
||||||
def patch(self):
|
def patch(self):
|
||||||
source = ifcopenshell.open(self.filepath)
|
source = ifcopenshell.open(self.filepath)
|
||||||
|
self.existing_contexts = self.file.by_type("IfcGeometricRepresentationContext")
|
||||||
|
self.added_contexts = set()
|
||||||
original_project = self.file.by_type("IfcProject")[0]
|
original_project = self.file.by_type("IfcProject")[0]
|
||||||
merged_project = self.file.add(source.by_type("IfcProject")[0])
|
merged_project = self.file.add(source.by_type("IfcProject")[0])
|
||||||
|
for element in source.by_type("IfcGeometricRepresentationContext"):
|
||||||
|
new = self.file.add(element)
|
||||||
|
self.added_contexts.add(new)
|
||||||
for element in source:
|
for element in source:
|
||||||
self.file.add(element)
|
self.file.add(element)
|
||||||
for inverse in self.file.get_inverse(merged_project):
|
for inverse in self.file.get_inverse(merged_project):
|
||||||
ifcopenshell.util.element.replace_attribute(inverse, merged_project, original_project)
|
ifcopenshell.util.element.replace_attribute(inverse, merged_project, original_project)
|
||||||
self.file.remove(merged_project)
|
self.file.remove(merged_project)
|
||||||
|
self.reuse_existing_contexts()
|
||||||
|
|
||||||
|
def reuse_existing_contexts(self):
|
||||||
|
to_delete = set()
|
||||||
|
|
||||||
|
for added_context in self.added_contexts:
|
||||||
|
equivalent_existing_context = self.get_equivalent_existing_context(added_context)
|
||||||
|
if equivalent_existing_context:
|
||||||
|
for inverse in self.file.get_inverse(added_context):
|
||||||
|
ifcopenshell.util.element.replace_attribute(inverse, added_context, equivalent_existing_context)
|
||||||
|
to_delete.add(added_context)
|
||||||
|
|
||||||
|
for added_context in to_delete:
|
||||||
|
ifcopenshell.util.element.remove_deep2(self.file, added_context)
|
||||||
|
|
||||||
|
def get_equivalent_existing_context(self, added_context):
|
||||||
|
for context in self.existing_contexts:
|
||||||
|
if context.is_a() != added_context.is_a():
|
||||||
|
continue
|
||||||
|
if context.is_a("IfcGeometricRepresentationSubContext"):
|
||||||
|
if (
|
||||||
|
context.ContextType == added_context.ContextType
|
||||||
|
and context.ContextIdentifier == added_context.ContextIdentifier
|
||||||
|
and context.TargetView == added_context.TargetView
|
||||||
|
):
|
||||||
|
return context
|
||||||
|
elif (
|
||||||
|
context.ContextType == added_context.ContextType
|
||||||
|
and context.ContextIdentifier == added_context.ContextIdentifier
|
||||||
|
):
|
||||||
|
return context
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import ifcopenshell.util.placement
|
|||||||
|
|
||||||
|
|
||||||
class Patcher:
|
class Patcher:
|
||||||
def __init__(self, src, file, logger, x=None, y=None, z=None, ax=None, ay=None, az=None):
|
def __init__(self, src, file, logger, x=None, y=None, z=None, should_rotate_first=True, ax=None, ay=None, az=None):
|
||||||
"""Offset and rotate all object placements in a model
|
"""Offset and rotate all object placements in a model
|
||||||
|
|
||||||
Every physical object in an IFC model has an object placement, a
|
Every physical object in an IFC model has an object placement, a
|
||||||
@@ -46,17 +46,21 @@ class Patcher:
|
|||||||
:type y: float
|
:type y: float
|
||||||
:param z: The Z coordinate to offset by in project length units.
|
:param z: The Z coordinate to offset by in project length units.
|
||||||
:type z: float
|
:type z: float
|
||||||
|
:param should_rotate_first: Whether or not to rotate first and then
|
||||||
|
translate, or to first translate and rotate afterwards. Defaults to
|
||||||
|
rotate first then translate.
|
||||||
|
:type should_rotate_first: bool
|
||||||
:param ax: An optional angle to rotate by. If only this angle is
|
:param ax: An optional angle to rotate by. If only this angle is
|
||||||
specified, it is treated as the angle to rotate in plan view (i.e.
|
specified, it is treated as the angle to rotate in plan view (i.e.
|
||||||
around the Z axis). If all angle parameters are specified, then it
|
around the Z axis). If all angle parameters are specified, then it
|
||||||
is treated as the angle to rotate around the X axis. Angles are in
|
is treated as the angle to rotate around the X axis. Angles are in
|
||||||
decimal degrees.
|
decimal degrees and positive is anticlockwise.
|
||||||
:type ax: float,optional
|
:type ax: float,optional
|
||||||
:param ay: An optional angle to rotate by for 3D rotations along the Y
|
:param ay: An optional angle to rotate by for 3D rotations along the Y
|
||||||
axis. Angles are in decimal degrees.
|
axis. Angles are in decimal degrees and positive is anticlockwise.
|
||||||
:type ay: float,optional
|
:type ay: float,optional
|
||||||
:param az: An optional angle to rotate by for 3D rotations along the Z
|
:param az: An optional angle to rotate by for 3D rotations along the Z
|
||||||
axis. Angles are in decimal degrees.
|
axis. Angles are in decimal degrees and positive is anticlockwise.
|
||||||
:type az: float,optional
|
:type az: float,optional
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@@ -67,10 +71,10 @@ class Patcher:
|
|||||||
ifcpatch.execute({"input": model, "recipe": "OffsetObjectPlacements", "arguments": [100,100,0]})
|
ifcpatch.execute({"input": model, "recipe": "OffsetObjectPlacements", "arguments": [100,100,0]})
|
||||||
|
|
||||||
# Rotate by 90 degrees, but don't do any offset
|
# Rotate by 90 degrees, but don't do any offset
|
||||||
ifcpatch.execute({"input": model, "recipe": "OffsetObjectPlacements", "arguments": [0,0,0,90]})
|
ifcpatch.execute({"input": model, "recipe": "OffsetObjectPlacements", "arguments": [0,0,0,True,90]})
|
||||||
|
|
||||||
# Some crazy 3D rotation and offset
|
# Some crazy 3D rotation and offset
|
||||||
ifcpatch.execute({"input": model, "recipe": "OffsetObjectPlacements", "arguments": [12.5,5,2,90,90,45]})
|
ifcpatch.execute({"input": model, "recipe": "OffsetObjectPlacements", "arguments": [12.5,5,2,False,90,90,45]})
|
||||||
"""
|
"""
|
||||||
self.src = src
|
self.src = src
|
||||||
self.file = file
|
self.file = file
|
||||||
@@ -78,6 +82,7 @@ class Patcher:
|
|||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
self.y = y
|
||||||
self.z = z
|
self.z = z
|
||||||
|
self.should_rotate_first = should_rotate_first
|
||||||
self.ax = ax
|
self.ax = ax
|
||||||
self.ay = ay
|
self.ay = ay
|
||||||
self.az = az
|
self.az = az
|
||||||
@@ -99,18 +104,26 @@ class Patcher:
|
|||||||
absolute_placements.append(absolute_placement)
|
absolute_placements.append(absolute_placement)
|
||||||
absolute_placements = set(absolute_placements)
|
absolute_placements = set(absolute_placements)
|
||||||
|
|
||||||
transformation = self.identity_matrix()
|
translate = self.identity_matrix()
|
||||||
|
rotate = self.identity_matrix()
|
||||||
|
|
||||||
if self.angle_type == "2D":
|
if self.angle_type == "2D":
|
||||||
angle = float(self.ax)
|
angle = float(self.ax)
|
||||||
if angle:
|
if angle:
|
||||||
transformation = self.z_rotation_matrix(math.radians(angle), transformation)
|
rotate = self.z_rotation_matrix(math.radians(angle), rotate)
|
||||||
elif self.angle_type == "3D":
|
elif self.angle_type == "3D":
|
||||||
for arg in (("x", float(self.ax)), ("y", float(self.ay)), ("z", float(self.az))):
|
for arg in (("x", float(self.ax)), ("y", float(self.ay)), ("z", float(self.az))):
|
||||||
if arg[1]:
|
if arg[1]:
|
||||||
transformation = getattr(self, f"{arg[0]}_rotation_matrix")(math.radians(arg[1]), transformation)
|
rotate = getattr(self, f"{arg[0]}_rotation_matrix")(math.radians(arg[1]), rotate)
|
||||||
transformation[0][3] += float(self.x)
|
|
||||||
transformation[1][3] += float(self.y)
|
translate[0][3] += float(self.x)
|
||||||
transformation[2][3] += float(self.z)
|
translate[1][3] += float(self.y)
|
||||||
|
translate[2][3] += float(self.z)
|
||||||
|
|
||||||
|
if self.should_rotate_first:
|
||||||
|
transformation = translate @ rotate
|
||||||
|
else:
|
||||||
|
transformation = rotate @ translate
|
||||||
|
|
||||||
for placement in absolute_placements:
|
for placement in absolute_placements:
|
||||||
placement.RelativePlacement = self.get_relative_placement(
|
placement.RelativePlacement = self.get_relative_placement(
|
||||||
|
|||||||
@@ -391,6 +391,7 @@ namespace {
|
|||||||
void add(const TopoDS_Shape& s) {
|
void add(const TopoDS_Shape& s) {
|
||||||
if (!use_prefiltering_) {
|
if (!use_prefiltering_) {
|
||||||
items_.insert(items_.end(), s);
|
items_.insert(items_.end(), s);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TopoDS_Compound C;
|
TopoDS_Compound C;
|
||||||
|
|||||||
Reference in New Issue
Block a user