mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 18:43:26 +00:00
Merge branch 'v0.7.0' of https://github.com/IfcOpenShell/IfcOpenShell into pr-daily-builds
Conflicts: cmake/CMakeLists.txt
This commit is contained in:
@@ -82,6 +82,8 @@ jobs:
|
||||
sudo /usr/bin/python -m pip install src/bcf
|
||||
sudo /usr/bin/python -m pip install pytest
|
||||
sudo /usr/bin/python -m pip install isodate
|
||||
sudo /usr/bin/python -m pip install lark
|
||||
sudo /usr/bin/python -m pip install networkx
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
|
||||
@@ -62,6 +62,10 @@ endif()
|
||||
OPTION(USERSPACE_PYTHON_PREFIX "Installs IfcPython for the current user only instead of system-wide." OFF)
|
||||
OPTION(ADD_COMMIT_SHA "Add commit sha and branch in version number, warning results in many rebuilds, requires git" OFF)
|
||||
|
||||
IF(NOT CMAKE_BUILD_TYPE)
|
||||
SET(CMAKE_BUILD_TYPE "Release")
|
||||
ENDIF()
|
||||
|
||||
# TODO QtViewer is deprecated ATM as it uses the 0.4 API
|
||||
# OPTION(BUILD_QTVIEWER "Build IfcOpenShell Qt GUI Viewer (requires Qt 4 framework)." OFF)
|
||||
include(GNUInstallDirs)
|
||||
@@ -478,10 +482,6 @@ if(HDF5_SUPPORT)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_HDF5)
|
||||
endif()
|
||||
|
||||
IF(NOT CMAKE_BUILD_TYPE)
|
||||
SET(CMAKE_BUILD_TYPE "Release")
|
||||
ENDIF()
|
||||
|
||||
if(ENABLE_BUILD_OPTIMIZATIONS)
|
||||
if(MSVC)
|
||||
# NOTE: RelWithDebInfo and Release use O2 (= /Ox /Gl /Gy/ = Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy) by default,
|
||||
|
||||
+4
-11
@@ -46,8 +46,7 @@ BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/19/d9/a69c6aff5673554df48120565a14a50eaa41d29ae03b02faa0b023666318/lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
# https://github.com/humanoid-path-planner/hpp-fcl/issues/272
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp-fcl-1.7.5-py39hbcdfc36_0.tar.bz2
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.8.0/download/linux-64/hpp-fcl-1.8.0-py310hdaf7e41_1.tar.bz2
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.11/download/linux-64/eigenpy-2.6.11-py310hf3e5c9c_0.tar.bz2
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py310h7c3ba0c_5.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/25/1e/19b46d8e8881fe0df2e20945d51919eeb1817836d62a90efa8506530e45c/lxml-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl
|
||||
@@ -71,8 +70,7 @@ BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/b8/74/a71f7ad72e8db54ce899efab84507b801660750cbbfa6a39e6717557d36a/lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
# https://github.com/humanoid-path-planner/hpp-fcl/issues/272
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-fcl-1.7.5-py39h1e32b98_0.tar.bz2
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.8.0/download/osx-64/hpp-fcl-1.8.0-py310h651ac30_1.tar.bz2
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.11/download/osx-64/eigenpy-2.6.11-py310hc03097c_0.tar.bz2
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py310h509978a_5.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/a1/44/17b7dac7a18807d30e2fe10c3328c152808f5464565e230bfd0e77f178c6/lxml-4.8.0-cp310-cp310-macosx_10_15_x86_64.whl
|
||||
@@ -96,8 +94,7 @@ BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/72/d4/426ecb8849c47c3e370c87aa0ac05d85768df917ffea27fcd6686a5e6495/lxml-4.6.3-cp39-cp39-win_amd64.whl
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
# https://github.com/humanoid-path-planner/hpp-fcl/issues/272
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-fcl-1.7.5-py39h2e7c763_0.tar.bz2
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.8.0/download/win-64/hpp-fcl-1.8.0-py310hc5a3c62_1.tar.bz2
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.11/download/win-64/eigenpy-2.6.11-py310hbd43d28_0.tar.bz2
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py310hc781a3c_5.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/f6/71/65c80a4caa1617a4c6e8fe1500cebb179db96232e2f623bfe6a1f4294e39/lxml-4.8.0-cp310-cp310-win_amd64.whl
|
||||
@@ -118,7 +115,7 @@ endif
|
||||
cp -r blenderbim/* dist/blenderbim/
|
||||
|
||||
# Provides IfcOpenShell Python functionality
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-883b8a5-$(PLATFORM)64.zip
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-2985bba-$(PLATFORM)64.zip
|
||||
cd dist/working && unzip ifcopenshell-python*
|
||||
cp -r dist/working/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
|
||||
@@ -292,8 +289,6 @@ endif
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by IFCClash
|
||||
# https://github.com/humanoid-path-planner/hpp-fcl/issues/272
|
||||
ifneq ($(PYVERSION), py310)
|
||||
mkdir dist/working
|
||||
cd dist/working && wget $(HPPFCL_URL)
|
||||
cd dist/working && tar -xf hpp-fcl*
|
||||
@@ -385,8 +380,6 @@ ifeq ($(PLATFORM), win)
|
||||
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
|
||||
endif
|
||||
rm -rf dist/working
|
||||
# https://github.com/humanoid-path-planner/hpp-fcl/issues/272
|
||||
endif
|
||||
|
||||
# Required by BIMTester
|
||||
mkdir dist/working
|
||||
|
||||
@@ -79,17 +79,18 @@ for name in modules.keys():
|
||||
|
||||
|
||||
classes = [
|
||||
operator.OpenUri,
|
||||
operator.SelectDataDir,
|
||||
operator.SelectSchemaDir,
|
||||
operator.SelectIfcFile,
|
||||
operator.OpenUpstream,
|
||||
operator.AddIfcFile,
|
||||
operator.BIM_OT_add_section_plane,
|
||||
operator.BIM_OT_remove_section_plane,
|
||||
operator.ReloadIfcFile,
|
||||
operator.AddIfcFile,
|
||||
operator.RemoveIfcFile,
|
||||
operator.ConfigureVisibility,
|
||||
operator.OpenUpstream,
|
||||
operator.OpenUri,
|
||||
operator.ReloadIfcFile,
|
||||
operator.RemoveIfcFile,
|
||||
operator.SelectDataDir,
|
||||
operator.SelectIfcFile,
|
||||
operator.SelectSchemaDir,
|
||||
operator.SelectURIAttribute,
|
||||
prop.StrProperty,
|
||||
prop.ObjProperty,
|
||||
prop.Attribute,
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<script type="text/javascript">
|
||||
var g = new JSGantt.GanttChart(document.getElementById('GanttChartDIV'), 'day');
|
||||
g.setOptions({
|
||||
vCaptionType: 'Complete', // Set to Show Caption : None,Caption,Resource,Duration,Complete,
|
||||
vCaptionType: 'Caption', // Set to Show Caption : None,Caption,Resource,Duration,Complete,
|
||||
vQuarterColWidth: 36,
|
||||
vDateTaskDisplayFormat: 'day dd month yyyy', // Shown in tool tip box
|
||||
vDayMajorDateDisplayFormat: 'mon yyyy - Week ww',// Set format to dates in the "Major" header of the "Day" view
|
||||
@@ -50,6 +50,8 @@ g.setOptions({
|
||||
vFormatArr: ['Day', 'Week', 'Month', 'Quarter'], // Even with setUseSingleCell using Hour format on such a large chart can cause issues in some browsers,
|
||||
vShowRes: false, // Disable the resource column.
|
||||
vShowComp: false, // Disable the completion column.
|
||||
vShowDur: false, // Disable the duration column, because jsgantt doesn't calculate durations the way we want.
|
||||
vAdditionalHeaders: {ifcduration: {title: 'Duration'}},
|
||||
vUseToolTip: false, // Disable tooltips.
|
||||
vTotalHeight: 900,
|
||||
});
|
||||
|
||||
@@ -5,6 +5,20 @@
|
||||
</g>
|
||||
<g id="door-tag">
|
||||
<circle cy="1.75" r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
|
||||
<line x1="-5" y1="1.75" x2="5" y2="1.75" style="stroke:black; stroke-width: 0.25;" />
|
||||
<line x1="-5" y1="1.75" x2="5" y2="1.75" style="stroke: black; stroke-width: 0.25;" />
|
||||
</g>
|
||||
<g id="elevation-arrow">
|
||||
<path style="fill: black;" d="M -7.07106 0 0 -7.07106 7.07106 0" />
|
||||
</g>
|
||||
<g id="elevation-tag">
|
||||
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
|
||||
<line x1="-5" y1="0" x2="5" y2="0" style="stroke: black; stroke-width: 0.25;" />
|
||||
</g>
|
||||
<g id="section-arrow">
|
||||
<path style="fill: black;" d="M -7.07106 0 0 -7.07106 7.07106 0" />
|
||||
</g>
|
||||
<g id="section-tag">
|
||||
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
|
||||
<line x1="-5" y1="0" x2="5" y2="0" style="stroke: black; stroke-width: 0.25;" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 1.2 KiB |
@@ -28,6 +28,7 @@ import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.placement
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.aggregate
|
||||
import blenderbim.core.spatial
|
||||
import blenderbim.core.style
|
||||
@@ -47,7 +48,7 @@ class IfcExporter:
|
||||
self.sync_deletions()
|
||||
self.sync_all_objects()
|
||||
self.sync_edited_objects()
|
||||
extension = self.ifc_export_settings.output_file.split(".")[-1]
|
||||
extension = self.ifc_export_settings.output_file.split(".")[-1].lower()
|
||||
if extension == "ifczip":
|
||||
with tempfile.TemporaryDirectory() as unzipped_path:
|
||||
filename, ext = os.path.splitext(os.path.basename(self.ifc_export_settings.output_file))
|
||||
@@ -179,6 +180,7 @@ class IfcExporter:
|
||||
self.sync_object_placement(grid_obj)
|
||||
if grid_obj.matrix_world != obj.matrix_world:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
tool.Geometry.record_object_position(obj)
|
||||
|
||||
def get_application_name(self):
|
||||
return "BlenderBIM"
|
||||
|
||||
@@ -26,9 +26,7 @@ from blenderbim.bim.module.drawing.prop import RasterStyleProperty
|
||||
from bpy.app.handlers import persistent
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.owner.prop import get_user_person, get_user_organisation
|
||||
from ifcopenshell.api.attribute.data import Data as AttributeData
|
||||
from ifcopenshell.api.material.data import Data as MaterialData
|
||||
from ifcopenshell.api.style.data import Data as StyleData
|
||||
from ifcopenshell.api.type.data import Data as TypeData
|
||||
|
||||
|
||||
@@ -68,11 +66,10 @@ def name_callback(obj, data):
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
if obj.BIMObjectProperties.ifc_definition_id:
|
||||
IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).Name = obj.name
|
||||
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||
MaterialData.load_materials()
|
||||
if obj.BIMMaterialProperties.ifc_style_id:
|
||||
IfcStore.get_file().by_id(obj.BIMMaterialProperties.ifc_style_id).Name = obj.name
|
||||
StyleData.load(IfcStore.get_file(), obj.BIMMaterialProperties.ifc_style_id)
|
||||
refresh_ui_data()
|
||||
return
|
||||
|
||||
if not obj.BIMObjectProperties.ifc_definition_id or "/" not in obj.name:
|
||||
@@ -96,7 +93,7 @@ def name_callback(obj, data):
|
||||
if element.is_a("IfcTypeProduct"):
|
||||
TypeData.purge()
|
||||
element.Name = "/".join(obj.name.split("/")[1:])
|
||||
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||
refresh_ui_data()
|
||||
|
||||
|
||||
def color_callback(obj, data):
|
||||
|
||||
@@ -48,6 +48,9 @@ def draw_attribute(attribute, layout, copy_operator=None):
|
||||
if copy_operator:
|
||||
op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN")
|
||||
op.name = attribute.name
|
||||
if attribute.is_uri:
|
||||
op = layout.operator("bim.select_uri_attribute", text="", icon="FILE_FOLDER")
|
||||
op.data_path = attribute.path_from_id("string_value")
|
||||
|
||||
|
||||
def import_attributes(ifc_class, props, data, callback=None):
|
||||
@@ -78,6 +81,8 @@ def import_attribute(attribute, props, data, callback=None):
|
||||
props.remove(len(props) - 1)
|
||||
elif data_type == "string":
|
||||
new.string_value = "" if new.is_null else data[attribute.name()]
|
||||
if attribute.type_of_attribute().declared_type().name() == "IfcURIReference":
|
||||
new.is_uri = True
|
||||
elif data_type == "boolean":
|
||||
new.bool_value = False if new.is_null else data[attribute.name()]
|
||||
elif data_type == "integer":
|
||||
|
||||
@@ -86,6 +86,7 @@ class IfcStore:
|
||||
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
|
||||
IfcStore.cache_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", f"{ifc_hash}.h5")
|
||||
cache_settings = ifcopenshell.geom.settings()
|
||||
cache_settings.set(cache_settings.STRICT_TOLERANCE, True)
|
||||
try:
|
||||
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(IfcStore.cache_path, cache_settings)
|
||||
except:
|
||||
|
||||
@@ -163,6 +163,7 @@ class IfcImporter:
|
||||
self.settings = ifcopenshell.geom.settings()
|
||||
self.settings.set_deflection_tolerance(self.ifc_import_settings.deflection_tolerance)
|
||||
self.settings.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
|
||||
self.settings.set(self.settings.STRICT_TOLERANCE, True)
|
||||
self.settings_native = ifcopenshell.geom.settings()
|
||||
self.settings_native.set(self.settings_native.INCLUDE_CURVES, True)
|
||||
self.settings_2d = ifcopenshell.geom.settings()
|
||||
@@ -283,11 +284,13 @@ class IfcImporter:
|
||||
if c.ContextIdentifier in ["Body", "Facetation"]
|
||||
]
|
||||
# Ideally, all representations should be in a subcontext, but some BIM programs don't do this correctly
|
||||
self.body_contexts.extend([
|
||||
c.id()
|
||||
for c in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False)
|
||||
if c.ContextType == "Model"
|
||||
])
|
||||
self.body_contexts.extend(
|
||||
[
|
||||
c.id()
|
||||
for c in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False)
|
||||
if c.ContextType == "Model"
|
||||
]
|
||||
)
|
||||
if self.body_contexts:
|
||||
self.settings.set_context_ids(self.body_contexts)
|
||||
# Annotation is to accommodate broken Revit files
|
||||
@@ -363,7 +366,7 @@ class IfcImporter:
|
||||
|
||||
def is_native_swept_disk_solid(self, representations):
|
||||
for representation in representations:
|
||||
items = representation["raw"].Items or [] # Be forgiving of invalid IFCs because Revit :(
|
||||
items = representation["raw"].Items or [] # Be forgiving of invalid IFCs because Revit :(
|
||||
if len(items) == 1 and items[0].is_a("IfcSweptDiskSolid"):
|
||||
return True
|
||||
return False
|
||||
@@ -1295,10 +1298,7 @@ class IfcImporter:
|
||||
name = style.Name or str(style.id())
|
||||
blender_material = bpy.data.materials.new(name)
|
||||
|
||||
old_definition_id = blender_material.BIMObjectProperties.ifc_definition_id
|
||||
if not old_definition_id:
|
||||
self.link_element(style, blender_material)
|
||||
blender_material.BIMObjectProperties.ifc_definition_id = old_definition_id
|
||||
self.link_element(style, blender_material)
|
||||
|
||||
blender_material.BIMMaterialProperties.ifc_style_id = style.id()
|
||||
self.material_creator.styles[style.id()] = blender_material
|
||||
@@ -1352,12 +1352,12 @@ class IfcImporter:
|
||||
elif surface_style.ReflectanceMethod == "FLAT":
|
||||
blender_material.use_nodes = True
|
||||
|
||||
output = {n.type: n for n in self.settings["material"].node_tree.nodes}.get("OUTPUT_MATERIAL", None)
|
||||
output = {n.type: n for n in blender_material.node_tree.nodes}.get("OUTPUT_MATERIAL", None)
|
||||
bsdf = blender_material.node_tree.nodes["Principled BSDF"]
|
||||
|
||||
mix = blender_material.node_tree.nodes.new(type="ShaderNodeMixShader")
|
||||
mix.location = bsdf.location
|
||||
blender_material.node_tree.links.new(lightpath.outputs[0], output.inputs["Surface"])
|
||||
blender_material.node_tree.links.new(mix.outputs[0], output.inputs["Surface"])
|
||||
|
||||
blender_material.node_tree.nodes.remove(bsdf)
|
||||
|
||||
@@ -1722,10 +1722,10 @@ class IfcImporter:
|
||||
):
|
||||
verts = [None] * len(geometry.verts)
|
||||
for i in range(0, len(geometry.verts), 3):
|
||||
verts[i], verts[i+1], verts[i+2] = ifcopenshell.util.geolocation.enh2xyz(
|
||||
verts[i], verts[i + 1], verts[i + 2] = ifcopenshell.util.geolocation.enh2xyz(
|
||||
geometry.verts[i],
|
||||
geometry.verts[i+1],
|
||||
geometry.verts[i+2],
|
||||
geometry.verts[i + 1],
|
||||
geometry.verts[i + 2],
|
||||
float(props.blender_eastings) * self.unit_scale,
|
||||
float(props.blender_northings) * self.unit_scale,
|
||||
float(props.blender_orthogonal_height) * self.unit_scale,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from blenderbim.bim.prop import StrProperty
|
||||
from blenderbim.bim.module.root.prop import getIfcClasses
|
||||
from blenderbim.bim.module.root.prop import get_ifc_classes
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -33,14 +33,11 @@ from bpy.props import (
|
||||
)
|
||||
|
||||
scenarios_enum = []
|
||||
classes_enum = []
|
||||
|
||||
|
||||
def purge():
|
||||
global scenarios_enum
|
||||
global classes_enum
|
||||
scenarios_enum = []
|
||||
classes_enum = []
|
||||
|
||||
|
||||
def getScenarios(self, context):
|
||||
@@ -68,7 +65,7 @@ class BimTesterProperties(PropertyGroup):
|
||||
feature: StringProperty(default="", name="Feature / IDS", update=refreshScenarios)
|
||||
steps: StringProperty(default="", name="Custom Steps")
|
||||
ifc_file: StringProperty(default="", name="IFC File")
|
||||
audit_ifc_class: EnumProperty(items=getIfcClasses, name="Audit Class")
|
||||
audit_ifc_class: EnumProperty(items=get_ifc_classes, name="Audit Class")
|
||||
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
|
||||
scenario: EnumProperty(items=getScenarios, name="Scenario")
|
||||
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
|
||||
|
||||
@@ -44,6 +44,19 @@ class Loader:
|
||||
self.load_settings()
|
||||
self.load_importer()
|
||||
|
||||
def create_mesh(self, boundary):
|
||||
# ConnectionGeometry is optional in IFC schema for some reasons.
|
||||
if not boundary.ConnectionGeometry:
|
||||
return None
|
||||
surface = boundary.ConnectionGeometry.SurfaceOnRelatingElement
|
||||
# workaround for unvalid geometry provided by Revit. See https://github.com/IfcOpenShell/IfcOpenShell/issues/635#issuecomment-770366838
|
||||
if surface.is_a("IfcCurveBoundedPlane") and not getattr(surface, "InnerBoundaries", None):
|
||||
surface.InnerBoundaries = ()
|
||||
shape = ifcopenshell.geom.create_shape(self.settings, surface)
|
||||
mesh = self.ifc_importer.create_mesh(None, shape)
|
||||
self.ifc_importer.link_mesh(shape, mesh)
|
||||
return mesh
|
||||
|
||||
def load_settings(self):
|
||||
self.settings = ifcopenshell.geom.settings()
|
||||
self.settings.set(self.settings.EXCLUDE_SOLIDS_AND_SURFACES, False)
|
||||
@@ -61,13 +74,7 @@ class Loader:
|
||||
obj = tool.Ifc.get_object(boundary)
|
||||
if obj:
|
||||
return obj
|
||||
surface = boundary.ConnectionGeometry.SurfaceOnRelatingElement
|
||||
# workaround for unvalid geometry provided by Revit. See https://github.com/IfcOpenShell/IfcOpenShell/issues/635#issuecomment-770366838
|
||||
if surface.is_a("IfcCurveBoundedPlane") and not getattr(surface, "InnerBoundaries", None):
|
||||
surface.InnerBoundaries = ()
|
||||
shape = ifcopenshell.geom.create_shape(self.settings, surface)
|
||||
mesh = self.ifc_importer.create_mesh(None, shape)
|
||||
self.ifc_importer.link_mesh(shape, mesh)
|
||||
mesh = self.create_mesh(boundary)
|
||||
obj = bpy.data.objects.new(f"{boundary.is_a()}/{boundary.Name}", mesh)
|
||||
obj.matrix_world = blender_space.matrix_world
|
||||
boundaries_collection = get_boundaries_collection(blender_space)
|
||||
|
||||
@@ -20,35 +20,29 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.LoadInformation,
|
||||
operator.LoadDocumentReferences,
|
||||
operator.DisableDocumentEditingUI,
|
||||
operator.EnableEditingDocument,
|
||||
operator.DisableEditingDocument,
|
||||
operator.AddInformation,
|
||||
operator.AddDocumentReference,
|
||||
operator.EditInformation,
|
||||
operator.EditDocumentReference,
|
||||
operator.RemoveDocument,
|
||||
operator.EnableAssigningDocument,
|
||||
operator.DisableAssigningDocument,
|
||||
operator.AddInformation,
|
||||
operator.AssignDocument,
|
||||
operator.DisableDocumentEditingUI,
|
||||
operator.DisableEditingDocument,
|
||||
operator.EditDocument,
|
||||
operator.EnableEditingDocument,
|
||||
operator.LoadDocument,
|
||||
operator.LoadParentDocument,
|
||||
operator.LoadProjectDocuments,
|
||||
operator.RemoveDocument,
|
||||
operator.UnassignDocument,
|
||||
prop.Document,
|
||||
prop.BIMDocumentProperties,
|
||||
prop.BIMObjectDocumentProperties,
|
||||
ui.BIM_PT_documents,
|
||||
ui.BIM_PT_object_documents,
|
||||
ui.BIM_UL_documents,
|
||||
ui.BIM_UL_object_documents,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMDocumentProperties = bpy.props.PointerProperty(type=prop.BIMDocumentProperties)
|
||||
bpy.types.Object.BIMObjectDocumentProperties = bpy.props.PointerProperty(type=prop.BIMObjectDocumentProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMDocumentProperties
|
||||
del bpy.types.Object.BIMObjectDocumentProperties
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.schema
|
||||
@@ -35,17 +36,29 @@ class DocumentData:
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"total_information": cls.total_information(),
|
||||
"total_references": cls.total_references(),
|
||||
"parent_document": cls.parent_document(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def total_information(cls):
|
||||
return len(tool.Ifc.get().by_type("IfcDocumentInformation"))
|
||||
return len(
|
||||
[
|
||||
rel
|
||||
for rel in tool.Ifc.get().by_type("IfcProject")[0].HasAssociations or []
|
||||
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument.is_a("IfcDocumentInformation")
|
||||
]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def total_references(cls):
|
||||
return len(tool.Ifc.get().by_type("IfcDocumentReference"))
|
||||
def parent_document(cls):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
if len(props.breadcrumbs):
|
||||
parent = tool.Ifc.get().by_id(int(props.breadcrumbs[-1].name))
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
return str(parent.DocumentId)
|
||||
return str(parent.Identification)
|
||||
return ""
|
||||
|
||||
|
||||
class ObjectDocumentData:
|
||||
@@ -69,16 +82,35 @@ class ObjectDocumentData:
|
||||
if rel.is_a("IfcRelAssociatesDocument"):
|
||||
if not rel.RelatingDocument.is_a("IfcDocumentReference"):
|
||||
continue
|
||||
|
||||
name = rel.RelatingDocument.Name
|
||||
if not name and rel.RelatingDocument.ReferencedDocument:
|
||||
name = rel.RelatingDocument.ReferencedDocument.Name
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
identification = rel.RelatingDocument.ItemReference
|
||||
if not identification and rel.RelatingDocument.ReferencedDocument:
|
||||
identification = rel.RelatingDocument.ReferencedDocument.DocumentId
|
||||
else:
|
||||
identification = rel.RelatingDocument.Identification
|
||||
if not identification and rel.RelatingDocument.ReferencedDocument:
|
||||
identification = rel.RelatingDocument.ReferencedDocument.Identification
|
||||
|
||||
location = rel.RelatingDocument.Location
|
||||
if location is None and rel.RelatingDocument.ReferencedDocument:
|
||||
location = rel.RelatingDocument.ReferencedDocument.Location
|
||||
if location:
|
||||
if not "://" in location:
|
||||
if not os.path.isabs(location):
|
||||
location = os.path.abspath(os.path.join(os.path.dirname(tool.Ifc.get_path()), location))
|
||||
location = "file://" + location
|
||||
|
||||
results.append(
|
||||
{
|
||||
"type": rel.RelatingDocument.is_a(),
|
||||
"id": rel.RelatingDocument.id(),
|
||||
"identification": identification,
|
||||
"name": rel.RelatingDocument.Name,
|
||||
"name": name,
|
||||
"location": location,
|
||||
}
|
||||
)
|
||||
return results
|
||||
|
||||
@@ -26,22 +26,32 @@ from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.document.data import Data
|
||||
|
||||
|
||||
class LoadInformation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_information"
|
||||
bl_label = "Load Information"
|
||||
class LoadProjectDocuments(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_project_documents"
|
||||
bl_label = "Load Project Documents"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.load_information(tool.Document)
|
||||
core.load_project_documents(tool.Document)
|
||||
|
||||
|
||||
class LoadDocumentReferences(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_document_references"
|
||||
bl_label = "Load Document References"
|
||||
class LoadDocument(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_document"
|
||||
bl_label = "Load Document"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
document: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.load_document(tool.Document, document=tool.Ifc.get().by_id(self.document))
|
||||
|
||||
|
||||
class LoadParentDocument(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_parent_document"
|
||||
bl_label = "Load Parent Document"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.load_references(tool.Document)
|
||||
core.load_parent_document(tool.Document)
|
||||
|
||||
|
||||
class DisableDocumentEditingUI(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -90,24 +100,14 @@ class AddDocumentReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.add_reference(tool.Ifc, tool.Document)
|
||||
|
||||
|
||||
class EditInformation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_information"
|
||||
class EditDocument(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_document"
|
||||
bl_label = "Edit Information"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMDocumentProperties
|
||||
core.edit_information(tool.Ifc, tool.Document, information=tool.Ifc.get().by_id(props.active_document_id))
|
||||
|
||||
|
||||
class EditDocumentReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_document_reference"
|
||||
bl_label = "Edit Document Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMDocumentProperties
|
||||
core.edit_reference(tool.Ifc, tool.Document, reference=tool.Ifc.get().by_id(props.active_document_id))
|
||||
core.edit_document(tool.Ifc, tool.Document, document=tool.Ifc.get().by_id(props.active_document_id))
|
||||
|
||||
|
||||
class RemoveDocument(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -120,28 +120,6 @@ class RemoveDocument(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.remove_document(tool.Ifc, tool.Document, document=tool.Ifc.get().by_id(self.document))
|
||||
|
||||
|
||||
class EnableAssigningDocument(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_assigning_document"
|
||||
bl_label = "Enable Assigning Document"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
core.enable_assigning_document(tool.Document, obj)
|
||||
|
||||
|
||||
class DisableAssigningDocument(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_assigning_document"
|
||||
bl_label = "Disable Assigning Document"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
core.enable_assigning_document(tool.Document, obj)
|
||||
|
||||
|
||||
class AssignDocument(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_document"
|
||||
bl_label = "Assign Document"
|
||||
|
||||
@@ -34,6 +34,7 @@ from bpy.props import (
|
||||
class Document(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
identification: StringProperty(name="Identification")
|
||||
is_information: BoolProperty(name="Is Information")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
|
||||
@@ -41,12 +42,6 @@ class BIMDocumentProperties(PropertyGroup):
|
||||
document_attributes: CollectionProperty(name="Document Attributes", type=Attribute)
|
||||
active_document_id: IntProperty(name="Active Document Id")
|
||||
documents: CollectionProperty(name="Documents", type=Document)
|
||||
breadcrumbs: CollectionProperty(name="Breadcrumbs", type=StrProperty)
|
||||
active_document_index: IntProperty(name="Active Document Index")
|
||||
is_editing: StringProperty(name="Is Editing")
|
||||
|
||||
|
||||
class BIMObjectDocumentProperties(PropertyGroup):
|
||||
is_adding: StringProperty(name="Is Adding")
|
||||
available_document_types: EnumProperty(
|
||||
items=[(d, d, "") for d in ["IfcDocumentInformation", "IfcDocumentReference"]], name="Available Document Types"
|
||||
)
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
|
||||
@@ -29,7 +29,7 @@ class BIM_PT_documents(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_collaboration"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -41,34 +41,38 @@ class BIM_PT_documents(Panel):
|
||||
|
||||
self.props = context.scene.BIMDocumentProperties
|
||||
|
||||
if not self.props.is_editing or self.props.is_editing == "information":
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Documents Found".format(DocumentData.data["total_information"]), icon="FILE")
|
||||
if self.props.is_editing == "information":
|
||||
row.operator("bim.add_information", text="", icon="ADD")
|
||||
row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL")
|
||||
else:
|
||||
row.operator("bim.load_information", text="", icon="IMPORT")
|
||||
|
||||
if not self.props.is_editing or self.props.is_editing == "reference":
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} References Found".format(DocumentData.data["total_references"]), icon="FILE_HIDDEN")
|
||||
if self.props.is_editing == "reference":
|
||||
row.operator("bim.add_document_reference", text="", icon="ADD")
|
||||
row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL")
|
||||
else:
|
||||
row.operator("bim.load_document_references", text="", icon="IMPORT")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Documents Found".format(DocumentData.data["total_information"]), icon="FILE")
|
||||
if self.props.is_editing:
|
||||
self.layout.template_list(
|
||||
"BIM_UL_documents", "", self.props, "documents", self.props, "active_document_index"
|
||||
)
|
||||
row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL")
|
||||
else:
|
||||
row.operator("bim.load_project_documents", text="", icon="IMPORT")
|
||||
|
||||
if not self.props.is_editing:
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
if self.props.breadcrumbs:
|
||||
row.operator("bim.load_parent_document", text="", icon="FRAME_PREV")
|
||||
row.label(text=DocumentData.data["parent_document"])
|
||||
else:
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.add_information", text="", icon="ADD")
|
||||
if self.props.breadcrumbs:
|
||||
row.operator("bim.add_document_reference", text="", icon="FILE_HIDDEN")
|
||||
|
||||
if self.props.active_document_id:
|
||||
self.draw_editable_ui(context)
|
||||
row.operator("bim.edit_document", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_document", text="", icon="CANCEL")
|
||||
elif self.props.documents and self.props.active_document_index < len(self.props.documents):
|
||||
ifc_definition_id = self.props.documents[self.props.active_document_index].ifc_definition_id
|
||||
row.operator("bim.enable_editing_document", text="", icon="GREASEPENCIL").document = ifc_definition_id
|
||||
row.operator("bim.remove_document", text="", icon="X").document = ifc_definition_id
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
draw_attributes(self.props.document_attributes, self.layout)
|
||||
self.layout.template_list("BIM_UL_documents", "", self.props, "documents", self.props, "active_document_index")
|
||||
|
||||
if self.props.active_document_id:
|
||||
draw_attributes(self.props.document_attributes, self.layout)
|
||||
|
||||
|
||||
class BIM_PT_object_documents(Panel):
|
||||
@@ -94,8 +98,7 @@ class BIM_PT_object_documents(Panel):
|
||||
|
||||
obj = context.active_object
|
||||
self.oprops = obj.BIMObjectProperties
|
||||
self.sprops = context.scene.BIMDocumentProperties
|
||||
self.props = obj.BIMObjectDocumentProperties
|
||||
self.props = context.scene.BIMDocumentProperties
|
||||
self.file = IfcStore.get_file()
|
||||
|
||||
self.draw_add_ui()
|
||||
@@ -108,52 +111,46 @@ class BIM_PT_object_documents(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=document["identification"] or "*", icon="FILE")
|
||||
row.label(text=document["name"] or "Unnamed")
|
||||
if document["location"]:
|
||||
row.operator("bim.open_uri", icon="URL", text="").uri = document["location"]
|
||||
row.operator("bim.unassign_document", text="", icon="X").document = document["id"]
|
||||
|
||||
def draw_add_ui(self):
|
||||
if self.props.is_adding:
|
||||
if not self.props.is_editing:
|
||||
row = self.layout.row(align=True)
|
||||
icon = "FILE" if self.props.is_adding == "IfcDocumentInformation" else "FILE_HIDDEN"
|
||||
row.label(text="Adding {}".format(self.props.is_adding), icon=icon)
|
||||
row.operator("bim.disable_assigning_document", text="", icon="CANCEL")
|
||||
self.layout.template_list(
|
||||
"BIM_UL_object_documents",
|
||||
"",
|
||||
self.sprops,
|
||||
"documents",
|
||||
self.sprops,
|
||||
"active_document_index",
|
||||
)
|
||||
row.operator("bim.load_project_documents", text="Assign Document References", icon="ADD")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
if self.props.breadcrumbs:
|
||||
row.operator("bim.load_parent_document", text="", icon="FRAME_PREV")
|
||||
row.label(text=DocumentData.data["parent_document"])
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "available_document_types", text="")
|
||||
row.operator("bim.enable_assigning_document", text="", icon="ADD")
|
||||
row.alignment = "RIGHT"
|
||||
|
||||
if self.props.documents and self.props.active_document_index < len(self.props.documents):
|
||||
document = self.props.documents[self.props.active_document_index]
|
||||
if not document.is_information:
|
||||
row.operator("bim.assign_document", text="", icon="ADD").document = document.ifc_definition_id
|
||||
row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL")
|
||||
|
||||
self.layout.template_list("BIM_UL_documents", "", self.props, "documents", self.props, "active_document_index")
|
||||
|
||||
|
||||
class BIM_UL_documents(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.identification)
|
||||
row.label(text=item.name)
|
||||
if context.scene.BIMDocumentProperties.active_document_id == item.ifc_definition_id:
|
||||
if context.scene.BIMDocumentProperties.is_editing == "information":
|
||||
row.operator("bim.edit_information", text="", icon="CHECKMARK")
|
||||
elif context.scene.BIMDocumentProperties.is_editing == "reference":
|
||||
row.operator("bim.edit_document_reference", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_document", text="", icon="CANCEL")
|
||||
elif context.scene.BIMDocumentProperties.active_document_id:
|
||||
row.operator("bim.remove_document", text="", icon="X").document = item.ifc_definition_id
|
||||
else:
|
||||
op = row.operator("bim.enable_editing_document", text="", icon="GREASEPENCIL")
|
||||
|
||||
if item.is_information:
|
||||
op = row.operator("bim.load_document", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT")
|
||||
op.document = item.ifc_definition_id
|
||||
row.operator("bim.remove_document", text="", icon="X").document = item.ifc_definition_id
|
||||
row.label(text="", icon="FILE")
|
||||
else:
|
||||
row.label(text="", icon="BLANK1")
|
||||
row.label(text="", icon="FILE_HIDDEN")
|
||||
|
||||
|
||||
class BIM_UL_object_documents(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.identification)
|
||||
row.label(text=item.name)
|
||||
row.operator("bim.assign_document", text="", icon="ADD").document = item.ifc_definition_id
|
||||
split1 = row.split(factor=0.1)
|
||||
split1.label(text=item.identification)
|
||||
split2 = split1.split(factor=0.9)
|
||||
split2.label(text=item.name)
|
||||
|
||||
@@ -33,6 +33,7 @@ classes = (
|
||||
operator.AddSheet,
|
||||
operator.BuildSchedule,
|
||||
operator.CleanWireframes,
|
||||
operator.ContractSheet,
|
||||
operator.CopyGrid,
|
||||
operator.CreateDrawing,
|
||||
operator.CreateSheets,
|
||||
@@ -45,12 +46,14 @@ classes = (
|
||||
operator.EditVectorStyle,
|
||||
operator.EnableEditingText,
|
||||
operator.EnableEditingTextProduct,
|
||||
operator.ExpandSheet,
|
||||
operator.GenerateReferences,
|
||||
operator.LoadDrawings,
|
||||
operator.LoadSheets,
|
||||
operator.OpenSheet,
|
||||
operator.OpenView,
|
||||
operator.RemoveDrawing,
|
||||
operator.RemoveDrawingFromSheet,
|
||||
operator.RemoveDrawingStyle,
|
||||
operator.RemoveDrawingStyleAttribute,
|
||||
operator.RemoveSchedule,
|
||||
|
||||
@@ -40,7 +40,7 @@ class BaseDecorator:
|
||||
|
||||
DEF_GLSL = """
|
||||
#define PI 3.141592653589793
|
||||
#define MAX_POINTS 32
|
||||
#define MAX_POINTS 64
|
||||
#define CIRCLE_SEGS 12
|
||||
"""
|
||||
|
||||
@@ -1125,15 +1125,15 @@ class GridDecorator(BaseDecorator):
|
||||
self.draw_label(context, text, p1, dir, vcenter=True, gap=0)
|
||||
|
||||
|
||||
class SectionViewDecorator(LevelDecorator):
|
||||
objecttype = "SECTION"
|
||||
class ElevationDecorator(LevelDecorator):
|
||||
objecttype = "ELEVATION"
|
||||
|
||||
DEF_GLSL = (
|
||||
BaseDecorator.DEF_GLSL
|
||||
+ """
|
||||
#define CIRCLE_SIZE 8.0
|
||||
#define TRIANGLE_L 32.0
|
||||
#define TRIANGLE_W 16.0
|
||||
#define TRIANGLE_L 22.63
|
||||
#define TRIANGLE_W 11.31
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -1144,12 +1144,14 @@ class SectionViewDecorator(LevelDecorator):
|
||||
layout(lines) in;
|
||||
layout(line_strip, max_vertices=MAX_POINTS) out;
|
||||
|
||||
void triangle_head(in vec4 dir, in vec4 side, in float length, in float width, out vec4 head[3]) {
|
||||
vec4 nose = dir * length;
|
||||
vec4 ear = side * width;
|
||||
head[0] = vec4(0);
|
||||
head[1] = nose * .5 + ear;
|
||||
head[2] = nose;
|
||||
void triangle_head(in vec4 side, in vec4 dir, in float length, in float width, in float radius, out vec4 head[5]) {
|
||||
vec4 nose = side * length;
|
||||
vec4 ear = dir * width;
|
||||
head[0] = side * -radius;
|
||||
head[1] = nose * -.5;
|
||||
head[2] = vec4(0) + ear;
|
||||
head[3] = nose * .5;
|
||||
head[4] = side * radius;
|
||||
}
|
||||
|
||||
void main() {
|
||||
@@ -1160,75 +1162,206 @@ class SectionViewDecorator(LevelDecorator):
|
||||
|
||||
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
|
||||
vec4 edge = p1w - p0w, dir = normalize(edge);
|
||||
vec4 gap = dir * viewportDrawingScale * TRIANGLE_L * .5;
|
||||
vec4 side = vec4(cross(vec3(dir.xy, 0), vec3(0, 0, 1)).xy, 0, 0);
|
||||
vec4 p;
|
||||
|
||||
vec4 head[CIRCLE_SEGS];
|
||||
circle_head(viewportDrawingScale * CIRCLE_SIZE, head);
|
||||
|
||||
vec4 head3[3];
|
||||
vec4 head5[5];
|
||||
|
||||
// start edge circle
|
||||
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||
p = p0w + gap + head[i];
|
||||
p = p0w + head[i];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
}
|
||||
p = p0w + gap + head[0];
|
||||
p = p0w + head[0];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// start edge triangle
|
||||
triangle_head(dir, -side, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, head3);
|
||||
p = p0w + head3[0];
|
||||
// edge triangle
|
||||
triangle_head(side, dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5);
|
||||
p = p0w + head5[0];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head3[1];
|
||||
p = p0w + head5[1];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head3[2];
|
||||
p = p0w + head5[2];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head5[3];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head5[4];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// end edge circle
|
||||
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||
p = p1w - gap + head[i];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
}
|
||||
p = p1w - gap + head[0];
|
||||
// reference divider
|
||||
p = p0w + normalize(vec4(-1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// end edge triangle
|
||||
triangle_head(-dir, -side, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, head3);
|
||||
p = p1w + head3[0];
|
||||
p = p0w + normalize(vec4(1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + head3[1];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + head3[2];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// stem
|
||||
gl_Position = p0;
|
||||
EmitVertex();
|
||||
gl_Position = p1;
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
}
|
||||
"""
|
||||
|
||||
def decorate(self, context, obj):
|
||||
verts, _, _ = self.get_path_geom(obj, topo=False)
|
||||
self.draw_lines(context, obj, verts, [(0, 1)])
|
||||
center = obj.matrix_world.translation
|
||||
v1 = obj.matrix_world @ Vector((0, 0, 0))
|
||||
v2 = obj.matrix_world @ Vector((0, 0, -1))
|
||||
self.draw_lines(context, obj, [v1, v2], [(0, 1)])
|
||||
|
||||
|
||||
class SectionDecorator(LevelDecorator):
|
||||
objecttype = "SECTION"
|
||||
|
||||
DEF_GLSL = (
|
||||
BaseDecorator.DEF_GLSL
|
||||
+ """
|
||||
#define CIRCLE_SIZE 8.0
|
||||
#define TRIANGLE_L 22.63
|
||||
#define TRIANGLE_W 11.31
|
||||
"""
|
||||
)
|
||||
|
||||
GEOM_GLSL = """
|
||||
uniform vec2 winsize;
|
||||
uniform float viewportDrawingScale;
|
||||
|
||||
layout(lines) in;
|
||||
layout(line_strip, max_vertices=MAX_POINTS) out;
|
||||
|
||||
void triangle_head(in vec4 side, in vec4 dir, in float length, in float width, in float radius, out vec4 head[5]) {
|
||||
vec4 nose = side * length;
|
||||
vec4 ear = dir * width;
|
||||
head[0] = side * -radius;
|
||||
head[1] = nose * -.5;
|
||||
head[2] = vec4(0) + ear;
|
||||
head[3] = nose * .5;
|
||||
head[4] = side * radius;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 clip2win = matCLIP2WIN();
|
||||
vec4 win2clip = matWIN2CLIP();
|
||||
|
||||
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
||||
|
||||
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
|
||||
vec4 edge = p1w - p0w, side = normalize(edge);
|
||||
vec4 gap = side * viewportDrawingScale * CIRCLE_SIZE;
|
||||
vec4 dir = vec4(cross(vec3(side.xy, 0), vec3(0, 0, 1)).xy, 0, 0);
|
||||
vec4 p;
|
||||
|
||||
vec4 head[CIRCLE_SEGS];
|
||||
circle_head(viewportDrawingScale * CIRCLE_SIZE, head);
|
||||
|
||||
vec4 head5[5];
|
||||
|
||||
// start edge circle
|
||||
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||
p = p0w + head[i];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
}
|
||||
p = p0w + head[0];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// start edge triangle
|
||||
triangle_head(side, dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5);
|
||||
p = p0w + head5[0];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head5[1];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head5[2];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head5[3];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head5[4];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// start reference divider
|
||||
p = p0w + normalize(vec4(-1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + normalize(vec4(1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// end edge circle
|
||||
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||
p = p1w - head[i];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
}
|
||||
p = p1w - head[0];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// end edge triangle
|
||||
triangle_head(side, dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5);
|
||||
p = p1w + head5[0];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + head5[1];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + head5[2];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + head5[3];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + head5[4];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// end reference divider
|
||||
p = p1w + normalize(vec4(-1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + normalize(vec4(1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// stem
|
||||
p = p0w + gap;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w - gap;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
}
|
||||
"""
|
||||
|
||||
def decorate(self, context, obj):
|
||||
if obj.data.is_editmode:
|
||||
verts, idxs = self.get_editmesh_geom(obj)
|
||||
else:
|
||||
verts, idxs = self.get_mesh_geom(obj)
|
||||
vert_map = {v.freeze(): (obj.matrix_world.inverted() @ v).x for v in verts}
|
||||
verts = sorted(verts, key=lambda v: vert_map[v], reverse=True)
|
||||
self.draw_lines(context, obj, verts, idxs)
|
||||
|
||||
|
||||
|
||||
class TextDecorator(BaseDecorator):
|
||||
@@ -1327,7 +1460,8 @@ class DecorationsHandler:
|
||||
SectionLevelDecorator,
|
||||
StairDecorator,
|
||||
BreakDecorator,
|
||||
SectionViewDecorator,
|
||||
SectionDecorator,
|
||||
ElevationDecorator,
|
||||
TextDecorator,
|
||||
]
|
||||
|
||||
|
||||
@@ -260,14 +260,11 @@ def format_distance(value, isArea=False, hide_units=True):
|
||||
def get_active_drawing(scene):
|
||||
"""Get active drawing collection and camera"""
|
||||
props = scene.DocProperties
|
||||
if props.active_drawing_index is None or len(props.drawings) == 0:
|
||||
return None, None
|
||||
try:
|
||||
drawing = props.active_drawing
|
||||
camera = tool.Ifc.get_object(tool.Ifc.get().by_id(drawing.ifc_definition_id))
|
||||
camera = tool.Ifc.get_object(tool.Ifc.get().by_id(props.active_drawing_id))
|
||||
return camera.users_collection[0], camera
|
||||
except (KeyError, IndexError):
|
||||
raise RuntimeError("missing drawing collection")
|
||||
except:
|
||||
return None, None
|
||||
|
||||
|
||||
def get_project_collection(scene):
|
||||
|
||||
@@ -82,6 +82,11 @@ class AddDrawing(bpy.types.Operator, Operator):
|
||||
target_view=self.props.target_view,
|
||||
location_hint=hint,
|
||||
)
|
||||
try:
|
||||
drawing = tool.Ifc.get().by_id(self.props.active_drawing_id)
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class CreateDrawing(bpy.types.Operator):
|
||||
@@ -301,12 +306,13 @@ class CreateDrawing(bpy.types.Operator):
|
||||
elements = set(ifc.by_type("IfcElement")) - set(ifc.by_type("IfcOpeningElement"))
|
||||
|
||||
self.setup_serialiser(ifc)
|
||||
cache_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True)
|
||||
cache = IfcStore.get_cache()
|
||||
[cache.remove(guid) for guid in invalidated_guids]
|
||||
|
||||
if target_view_contexts and elements:
|
||||
geom_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True, INCLUDE_CURVES=True)
|
||||
geom_settings = ifcopenshell.geom.settings(
|
||||
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
||||
)
|
||||
geom_settings.set_context_ids(target_view_contexts)
|
||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, multiprocessing.cpu_count(), include=elements)
|
||||
it.set_cache(cache)
|
||||
@@ -317,18 +323,14 @@ class CreateDrawing(bpy.types.Operator):
|
||||
elements -= processed
|
||||
|
||||
if body_contexts and elements:
|
||||
geom_settings = ifcopenshell.geom.settings(
|
||||
DISABLE_TRIANGULATION=True,
|
||||
)
|
||||
geom_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True)
|
||||
geom_settings.set_context_ids(body_contexts)
|
||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, multiprocessing.cpu_count(), include=elements)
|
||||
it.set_cache(cache)
|
||||
for elem in self.yield_from_iterator(it):
|
||||
self.serialiser.write(elem)
|
||||
|
||||
geom_settings = ifcopenshell.geom.settings(
|
||||
DISABLE_TRIANGULATION=True,
|
||||
)
|
||||
geom_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True)
|
||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, include=[self.camera_element])
|
||||
for elem in self.yield_from_iterator(it):
|
||||
self.serialiser.write(elem)
|
||||
@@ -345,7 +347,9 @@ class CreateDrawing(bpy.types.Operator):
|
||||
return svg_path
|
||||
|
||||
def setup_serialiser(self, ifc):
|
||||
self.svg_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True, INCLUDE_CURVES=True)
|
||||
self.svg_settings = ifcopenshell.geom.settings(
|
||||
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
||||
)
|
||||
self.svg_buffer = ifcopenshell.geom.serializers.buffer()
|
||||
self.serialiser = ifcopenshell.geom.serializers.svg(self.svg_buffer, self.svg_settings)
|
||||
self.serialiser.setFile(ifc)
|
||||
@@ -453,6 +457,10 @@ class CreateDrawing(bpy.types.Operator):
|
||||
svg_writer.annotations.setdefault("equal_objs", []).append(obj)
|
||||
elif element.ObjectType == "DIMENSION":
|
||||
svg_writer.annotations.setdefault("dimension_objs", []).append(obj)
|
||||
elif element.ObjectType == "ELEVATION":
|
||||
svg_writer.annotations.setdefault("elevation_objs", []).append(obj)
|
||||
elif element.ObjectType == "SECTION":
|
||||
svg_writer.annotations.setdefault("section_objs", []).append(obj)
|
||||
elif element.ObjectType == "BREAKLINE":
|
||||
svg_writer.annotations["break_obj"] = obj
|
||||
elif element.ObjectType == "HIDDEN_LINE":
|
||||
@@ -563,7 +571,6 @@ class AddDrawingToSheet(bpy.types.Operator):
|
||||
bl_idname = "bim.add_drawing_to_sheet"
|
||||
bl_label = "Add Drawing To Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
# TODO: check undo redo
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -572,9 +579,78 @@ class AddDrawingToSheet(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
active_drawing = props.drawings[props.active_drawing_index]
|
||||
active_sheet = props.sheets[props.active_sheet_index]
|
||||
drawing = tool.Ifc.get().by_id(active_drawing.ifc_definition_id)
|
||||
sheet = tool.Ifc.get().by_id(active_sheet.ifc_definition_id)
|
||||
if not sheet.is_a("IfcDocumentInformation"):
|
||||
return {"FINISHED"}
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
references = sheet.DocumentReferences or []
|
||||
else:
|
||||
references = sheet.HasDocumentReferences or []
|
||||
|
||||
has_drawing = False
|
||||
for reference in references:
|
||||
new = props.sheets.add()
|
||||
new.ifc_definition_id = reference.id()
|
||||
new.is_sheet = False
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
new.identification = reference.ItemReference or "X"
|
||||
element = [r for r in tool.Ifc.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference][
|
||||
0
|
||||
].RelatedObjects[0]
|
||||
else:
|
||||
new.identification = reference.Identification or "X"
|
||||
element = reference.DocumentRefForObjects[0].RelatedObjects[0]
|
||||
if element == drawing:
|
||||
has_drawing = True
|
||||
break
|
||||
|
||||
if has_drawing:
|
||||
return {"FINISHED"}
|
||||
|
||||
reference = tool.Ifc.run("document.add_reference", information=sheet)
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
attributes = {"ItemReference": str(len(sheet.DocumentReferences or []))}
|
||||
else:
|
||||
attributes = {"Identification": str(len(sheet.HasDocumentReferences or []))}
|
||||
tool.Ifc.run("document.edit_reference", reference=reference, attributes=attributes)
|
||||
tool.Ifc.run("document.assign_document", product=drawing, document=reference)
|
||||
sheet_builder = sheeter.SheetBuilder()
|
||||
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
|
||||
sheet_builder.add_drawing(props.active_drawing.name, props.active_sheet.name)
|
||||
sheet_builder.add_drawing(drawing, sheet)
|
||||
|
||||
tool.Drawing.import_sheets()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveDrawingFromSheet(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_drawing_from_sheet"
|
||||
bl_label = "Remove Drawing From Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
reference: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
reference = tool.Ifc.get().by_id(self.reference)
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
sheet = reference.ReferenceToDocument[0]
|
||||
drawing = [r for r in tool.Ifc.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference][
|
||||
0
|
||||
].RelatedObjects[0]
|
||||
else:
|
||||
sheet = reference.ReferencedDocument
|
||||
drawing = reference.DocumentRefForObjects[0].RelatedObjects[0]
|
||||
|
||||
tool.Ifc.run("document.unassign_document", product=drawing, document=reference)
|
||||
tool.Ifc.run("document.remove_reference", reference=reference)
|
||||
|
||||
sheet_builder = sheeter.SheetBuilder()
|
||||
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
|
||||
sheet_builder.remove_drawing(drawing, sheet)
|
||||
tool.Drawing.import_sheets()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -590,7 +666,8 @@ class CreateSheets(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
props = scene.DocProperties
|
||||
name = props.active_sheet.name
|
||||
active_sheet = props.sheets[props.active_sheet_index]
|
||||
name = active_sheet.name
|
||||
sheet_builder = sheeter.SheetBuilder()
|
||||
sheet_builder.data_dir = scene.BIMProperties.data_dir
|
||||
sheet_builder.build(name)
|
||||
@@ -677,7 +754,9 @@ class ActivateView(bpy.types.Operator):
|
||||
|
||||
project_collection.children["Views"].children[camera.users_collection[0].name].hide_viewport = False
|
||||
bpy.data.collections.get(camera.users_collection[0].name).hide_render = False
|
||||
bpy.context.scene.DocProperties.active_drawing_id = self.drawing
|
||||
bpy.ops.bim.activate_drawing_style()
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=tool.Ifc.get().by_id(self.drawing))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -777,12 +856,10 @@ class RemoveDrawing(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_drawing"
|
||||
bl_label = "Remove Drawing"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
index: bpy.props.IntProperty()
|
||||
drawing: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
drawing = tool.Ifc.get().by_id(props.drawings[self.index].ifc_definition_id)
|
||||
core.remove_drawing(tool.Ifc, tool.Drawing, drawing=drawing)
|
||||
core.remove_drawing(tool.Ifc, tool.Drawing, drawing=tool.Ifc.get().by_id(self.drawing))
|
||||
|
||||
|
||||
class AddDrawingStyle(bpy.types.Operator):
|
||||
@@ -935,12 +1012,10 @@ class RemoveSheet(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_sheet"
|
||||
bl_label = "Remove Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
index: bpy.props.IntProperty()
|
||||
sheet: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
|
||||
core.remove_sheet(tool.Ifc, tool.Drawing, sheet=sheet)
|
||||
core.remove_sheet(tool.Ifc, tool.Drawing, sheet=tool.Ifc.get().by_id(self.sheet))
|
||||
|
||||
|
||||
class AddSchedule(bpy.types.Operator):
|
||||
@@ -1070,8 +1145,10 @@ class CopyGrid(bpy.types.Operator):
|
||||
return helper.get_active_drawing(context.scene)[0] is not None
|
||||
|
||||
def execute(self, context):
|
||||
drawing = tool.Ifc.get_entity(context.scene.camera)
|
||||
target_view = tool.Drawing.get_drawing_target_view(drawing)
|
||||
subcontext = ifcopenshell.util.representation.get_context(
|
||||
IfcStore.get_file(), "Plan", "Annotation", context.scene.camera.data.BIMCameraProperties.target_view
|
||||
IfcStore.get_file(), "Plan", "Annotation", target_view
|
||||
)
|
||||
if not subcontext:
|
||||
return {"FINISHED"}
|
||||
@@ -1080,8 +1157,8 @@ class CopyGrid(bpy.types.Operator):
|
||||
view_coll, camera = helper.get_active_drawing(context.scene)
|
||||
is_ortho = camera.data.type == "ORTHO"
|
||||
bounds = helper.ortho_view_frame(camera.data) if is_ortho else None
|
||||
clipping = is_ortho and camera.data.BIMCameraProperties.target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW")
|
||||
elevating = is_ortho and camera.data.BIMCameraProperties.target_view in ("ELEVATION_VIEW", "SECTION_VIEW")
|
||||
clipping = is_ortho and target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW")
|
||||
elevating = is_ortho and target_view in ("ELEVATION_VIEW", "SECTION_VIEW")
|
||||
|
||||
def grep(coll):
|
||||
results = []
|
||||
@@ -1313,3 +1390,31 @@ class DisableEditingDrawings(bpy.types.Operator, Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
core.disable_editing_drawings(tool.Drawing)
|
||||
|
||||
|
||||
class ExpandSheet(bpy.types.Operator):
|
||||
bl_idname = "bim.expand_sheet"
|
||||
bl_label = "Expand Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
sheet: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
for sheet in [s for s in props.sheets if s.ifc_definition_id == self.sheet]:
|
||||
sheet.is_expanded = True
|
||||
core.load_sheets(tool.Drawing)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ContractSheet(bpy.types.Operator):
|
||||
bl_idname = "bim.contract_sheet"
|
||||
bl_label = "Contract Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
sheet: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
for sheet in [s for s in props.sheets if s.ifc_definition_id == self.sheet]:
|
||||
sheet.is_expanded = False
|
||||
core.load_sheets(tool.Drawing)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -199,6 +199,7 @@ class Variable(PropertyGroup):
|
||||
class Drawing(PropertyGroup):
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
name: StringProperty(name="Name", update=update_drawing_name)
|
||||
target_view: StringProperty(name="Target View")
|
||||
|
||||
|
||||
class Schedule(PropertyGroup):
|
||||
@@ -220,8 +221,9 @@ class Sheet(PropertyGroup):
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
identification: StringProperty(name="Identification")
|
||||
name: StringProperty(name="Name", get=get_name, set=set_name)
|
||||
drawings: CollectionProperty(name="Drawings", type=Drawing)
|
||||
active_drawing_index: IntProperty(name="Active Drawing Index")
|
||||
is_sheet: BoolProperty(name="Is Sheet", default=False)
|
||||
reference_type: StringProperty(name="Reference Type")
|
||||
is_expanded: BoolProperty(name="Is Expanded", default=False)
|
||||
|
||||
|
||||
class DrawingStyle(PropertyGroup):
|
||||
@@ -292,6 +294,7 @@ class DocProperties(PropertyGroup):
|
||||
)
|
||||
location_hint: EnumProperty(items=get_location_hint, name="Location Hint")
|
||||
drawings: CollectionProperty(name="Drawings", type=Drawing)
|
||||
active_drawing_id: IntProperty(name="Active Drawing Id")
|
||||
active_drawing_index: IntProperty(name="Active Drawing Index")
|
||||
current_drawing_index: IntProperty(name="Current Drawing Index")
|
||||
schedules: CollectionProperty(name="Schedules", type=Schedule)
|
||||
@@ -311,14 +314,6 @@ class DocProperties(PropertyGroup):
|
||||
def active_schedule(self):
|
||||
return self.schedules[self.active_schedule_index]
|
||||
|
||||
@property
|
||||
def active_drawing(self):
|
||||
return self.drawings[self.active_drawing_index]
|
||||
|
||||
@property
|
||||
def active_sheet(self):
|
||||
return self.sheets[self.active_sheet_index]
|
||||
|
||||
|
||||
class BIMCameraProperties(PropertyGroup):
|
||||
representation: StringProperty(name="Representation")
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
import urllib.parse
|
||||
import pystache
|
||||
import ntpath
|
||||
import os
|
||||
import ntpath
|
||||
import pystache
|
||||
import urllib.parse
|
||||
import xml.etree.ElementTree as ET
|
||||
import blenderbim.tool as tool
|
||||
from shutil import copy
|
||||
from xml.dom import minidom
|
||||
|
||||
@@ -59,11 +60,16 @@ class SheetBuilder:
|
||||
with open(sheet_path, "w") as f:
|
||||
f.write(minidom.parseString(ET.tostring(root)).toprettyxml(indent=" "))
|
||||
|
||||
def add_drawing(self, view_name, sheet_name):
|
||||
sheet_path = os.path.join(self.data_dir, "sheets", sheet_name + ".svg")
|
||||
view_path = os.path.join(self.data_dir, "diagrams", view_name + ".svg")
|
||||
def add_drawing(self, drawing, sheet):
|
||||
filename = drawing.Name
|
||||
sheet_name = tool.Drawing.get_sheet_filename(sheet)
|
||||
sheet_dir = os.path.join(self.data_dir, "sheets")
|
||||
drawing_dir = os.path.join(self.data_dir, "diagrams")
|
||||
sheet_path = os.path.join(sheet_dir, sheet_name + ".svg")
|
||||
drawing_path = os.path.join(drawing_dir, filename + ".svg")
|
||||
underlay_path = os.path.join(drawing_dir, filename + ".png")
|
||||
|
||||
if not os.path.isfile(view_path):
|
||||
if not os.path.isfile(sheet_path):
|
||||
raise FileNotFoundError
|
||||
|
||||
ET.register_namespace("", "http://www.w3.org/2000/svg")
|
||||
@@ -72,7 +78,7 @@ class SheetBuilder:
|
||||
sheet_tree = ET.parse(sheet_path)
|
||||
sheet_root = sheet_tree.getroot()
|
||||
|
||||
view_tree = ET.parse(view_path)
|
||||
view_tree = ET.parse(drawing_path)
|
||||
view_root = view_tree.getroot()
|
||||
|
||||
# The view is placed into a group with a background image element.
|
||||
@@ -80,26 +86,48 @@ class SheetBuilder:
|
||||
# here to accommodate browsers which do not nest images.
|
||||
view = ET.SubElement(sheet_root, "g")
|
||||
view.attrib["data-type"] = "drawing"
|
||||
view.attrib["data-guid"] = drawing.GlobalId
|
||||
view_width = self.convert_to_mm(view_root.attrib.get("width"))
|
||||
view_height = self.convert_to_mm(view_root.attrib.get("height"))
|
||||
|
||||
background = ET.SubElement(view, "image")
|
||||
background.attrib["xlink:href"] = "../diagrams/{}.png".format(view_name)
|
||||
background.attrib["x"] = "30"
|
||||
background.attrib["y"] = "30"
|
||||
background.attrib["width"] = str(view_width)
|
||||
background.attrib["height"] = str(view_height)
|
||||
if os.path.isfile(underlay_path):
|
||||
background = ET.SubElement(view, "image")
|
||||
background.attrib["xlink:href"] = os.path.relpath(underlay_path, sheet_dir)
|
||||
background.attrib["x"] = "30"
|
||||
background.attrib["y"] = "30"
|
||||
background.attrib["width"] = str(view_width)
|
||||
background.attrib["height"] = str(view_height)
|
||||
|
||||
foreground = ET.SubElement(view, "image")
|
||||
foreground.attrib["xlink:href"] = "../diagrams/{}.svg".format(view_name)
|
||||
foreground.attrib["x"] = "30"
|
||||
foreground.attrib["y"] = "30"
|
||||
foreground.attrib["width"] = str(view_width)
|
||||
foreground.attrib["height"] = str(view_height)
|
||||
if os.path.isfile(drawing_path):
|
||||
foreground = ET.SubElement(view, "image")
|
||||
foreground.attrib["xlink:href"] = os.path.relpath(drawing_path, sheet_dir)
|
||||
foreground.attrib["x"] = "30"
|
||||
foreground.attrib["y"] = "30"
|
||||
foreground.attrib["width"] = str(view_width)
|
||||
foreground.attrib["height"] = str(view_height)
|
||||
|
||||
self.add_view_title(30, view_height + 35, view)
|
||||
sheet_tree.write(sheet_path)
|
||||
|
||||
def remove_drawing(self, drawing, sheet):
|
||||
sheet_name = tool.Drawing.get_sheet_filename(sheet)
|
||||
sheet_dir = os.path.join(self.data_dir, "sheets")
|
||||
sheet_path = os.path.join(sheet_dir, sheet_name + ".svg")
|
||||
|
||||
ET.register_namespace("", "http://www.w3.org/2000/svg")
|
||||
|
||||
sheet_tree = ET.parse(sheet_path)
|
||||
sheet_root = sheet_tree.getroot()
|
||||
|
||||
print('removing drawing', sheet_root.findall("{http://www.w3.org/2000/svg}g"))
|
||||
for g in sheet_root.findall("{http://www.w3.org/2000/svg}g"):
|
||||
print('checking g', g)
|
||||
if g.attrib["data-type"] == "drawing" and g.attrib["data-guid"] == drawing.GlobalId:
|
||||
sheet_root.remove(g)
|
||||
break
|
||||
|
||||
sheet_tree.write(sheet_path)
|
||||
|
||||
def add_schedule(self, schedule_name, sheet_name):
|
||||
sheet_path = os.path.join(self.data_dir, "sheets", sheet_name + ".svg")
|
||||
view_path = os.path.join(self.data_dir, "schedules", schedule_name + ".svg")
|
||||
|
||||
@@ -286,7 +286,15 @@ class SvgWriter:
|
||||
},
|
||||
)
|
||||
)
|
||||
self.draw_text_annotations()
|
||||
|
||||
for obj in self.annotations.get("elevation_objs", []):
|
||||
self.draw_elevation_annotation(obj)
|
||||
|
||||
for obj in self.annotations.get("section_objs", []):
|
||||
self.draw_section_annotation(obj)
|
||||
|
||||
for text_obj in self.annotations.get("text_objs", []):
|
||||
self.draw_text_annotation(text_obj, text_obj.location)
|
||||
|
||||
def draw_ifc_annotation(self):
|
||||
x_offset = self.raw_width / 2
|
||||
@@ -425,9 +433,48 @@ class SvgWriter:
|
||||
position = Vector((0, 0, 0))
|
||||
self.draw_text_annotation(obj, position)
|
||||
|
||||
def draw_text_annotations(self):
|
||||
for text_obj in self.annotations.get("text_objs", []):
|
||||
self.draw_text_annotation(text_obj, text_obj.location)
|
||||
def draw_section_annotation(self, obj):
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
|
||||
self.draw_line_annotation((obj, obj.data), ["solid"])
|
||||
|
||||
v1 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, 0)))
|
||||
v2 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, -1)))
|
||||
angle = -math.degrees((v2 - v1).xy.angle_signed(Vector((0, 1))))
|
||||
|
||||
for vert in obj.data.vertices:
|
||||
point = obj.matrix_world @ vert.co
|
||||
symbol_position = self.project_point_onto_camera(point)
|
||||
symbol_position = Vector(((x_offset + symbol_position.x), (y_offset - symbol_position.y)))
|
||||
transform = "rotate({}, {}, {})".format(
|
||||
angle,
|
||||
(symbol_position * self.scale)[0],
|
||||
(symbol_position * self.scale)[1],
|
||||
)
|
||||
|
||||
self.svg.add(self.svg.use("#section-arrow", insert=tuple(symbol_position * self.scale), transform=transform))
|
||||
self.svg.add(self.svg.use("#section-tag", insert=tuple(symbol_position * self.scale)))
|
||||
|
||||
def draw_elevation_annotation(self, obj):
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
symbol_position = self.project_point_onto_camera(obj.location)
|
||||
symbol_position = Vector(((x_offset + symbol_position.x), (y_offset - symbol_position.y)))
|
||||
|
||||
v1 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, 0)))
|
||||
v2 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, -1)))
|
||||
angle = -math.degrees((v2 - v1).xy.angle_signed(Vector((0, 1))))
|
||||
|
||||
transform = "rotate({}, {}, {})".format(
|
||||
angle,
|
||||
(symbol_position * self.scale)[0],
|
||||
(symbol_position * self.scale)[1],
|
||||
)
|
||||
|
||||
self.svg.add(self.svg.use("#elevation-arrow", insert=tuple(symbol_position * self.scale), transform=transform))
|
||||
self.svg.add(self.svg.use("#elevation-tag", insert=tuple(symbol_position * self.scale)))
|
||||
|
||||
|
||||
def draw_text_annotation(self, text_obj, position):
|
||||
x_offset = self.raw_width / 2
|
||||
@@ -646,6 +693,7 @@ class SvgWriter:
|
||||
)
|
||||
|
||||
def project_point_onto_camera(self, point):
|
||||
# TODO is this needlessly complex?
|
||||
return self.camera.matrix_world.inverted() @ geometry.intersect_line_plane(
|
||||
point.xyz,
|
||||
point.xyz - Vector(self.camera_projection),
|
||||
|
||||
@@ -172,14 +172,15 @@ class BIM_PT_drawings(Panel):
|
||||
|
||||
if self.props.drawings:
|
||||
if self.props.active_drawing_index < len(self.props.drawings):
|
||||
active_drawing = self.props.drawings[self.props.active_drawing_index]
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
op = row.operator("bim.open_view", icon="URL", text="")
|
||||
op.view = self.props.active_drawing.name
|
||||
op.view = active_drawing.name
|
||||
op = row.operator("bim.activate_view", icon="OUTLINER_OB_CAMERA", text="")
|
||||
op.drawing = self.props.active_drawing.ifc_definition_id
|
||||
op.drawing = active_drawing.ifc_definition_id
|
||||
row.operator("bim.create_drawing", text="", icon="OUTPUT")
|
||||
row.operator("bim.remove_drawing", icon="X", text="").index = self.props.active_drawing_index
|
||||
row.operator("bim.remove_drawing", icon="X", text="").drawing = active_drawing.ifc_definition_id
|
||||
self.layout.template_list(
|
||||
"BIM_UL_drawinglist", "", self.props, "drawings", self.props, "active_drawing_index"
|
||||
)
|
||||
@@ -245,14 +246,19 @@ class BIM_PT_sheets(Panel):
|
||||
row.operator("bim.add_sheet", text="", icon="ADD")
|
||||
row.operator("bim.disable_editing_sheets", text="", icon="CANCEL")
|
||||
|
||||
if self.props.sheets:
|
||||
if self.props.sheets and self.props.active_sheet_index < len(self.props.sheets):
|
||||
active_sheet = self.props.sheets[self.props.active_sheet_index]
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.open_sheet", icon="URL", text="")
|
||||
row.operator("bim.add_drawing_to_sheet", icon="IMAGE_PLANE", text="")
|
||||
row.operator("bim.add_schedule_to_sheet", icon="PRESET_NEW", text="")
|
||||
row.operator("bim.create_sheets", icon="FILE_REFRESH", text="")
|
||||
row.operator("bim.remove_sheet", icon="X", text="").index = self.props.active_sheet_index
|
||||
if active_sheet.is_sheet:
|
||||
row.operator("bim.remove_sheet", icon="X", text="").sheet = active_sheet.ifc_definition_id
|
||||
else:
|
||||
op = row.operator("bim.remove_drawing_from_sheet", icon="X", text="")
|
||||
op.reference = active_sheet.ifc_definition_id
|
||||
|
||||
self.layout.template_list("BIM_UL_sheets", "", self.props, "sheets", self.props, "active_sheet_index")
|
||||
|
||||
@@ -369,7 +375,16 @@ class BIM_UL_drawinglist(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.prop(item, "name", text="", emboss=False)
|
||||
icon = "UV_FACESEL"
|
||||
if item.target_view == "ELEVATION_VIEW":
|
||||
icon = "UV_VERTEXSEL"
|
||||
elif item.target_view == "SECTION_VIEW":
|
||||
icon = "UV_EDGESEL"
|
||||
elif item.target_view == "REFLECTED_PLAN_VIEW":
|
||||
icon = "XRAY"
|
||||
elif item.target_view == "MODEL_VIEW":
|
||||
icon = "SNAP_VOLUME"
|
||||
row.prop(item, "name", text="", icon=icon, emboss=False)
|
||||
else:
|
||||
layout.label(text="", translate=False)
|
||||
|
||||
@@ -378,9 +393,22 @@ class BIM_UL_sheets(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
split1 = row.split(factor=0.2)
|
||||
split1.label(text=item.identification or "X")
|
||||
split2 = split1.split(factor=0.8)
|
||||
split2.label(text=item.name or "Unnamed")
|
||||
|
||||
if item.is_sheet:
|
||||
if item.is_expanded:
|
||||
row.operator(
|
||||
"bim.contract_sheet", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN"
|
||||
).sheet = item.ifc_definition_id
|
||||
else:
|
||||
row.operator(
|
||||
"bim.expand_sheet", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
|
||||
).sheet = item.ifc_definition_id
|
||||
else:
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.reference_type == "DRAWING":
|
||||
row.label(text="", icon="IMAGE_DATA")
|
||||
|
||||
name = "{} - {}".format(item.identification or "X", item.name or "Unnamed")
|
||||
row.label(text=name)
|
||||
else:
|
||||
layout.label(text="", translate=False)
|
||||
|
||||
@@ -157,6 +157,9 @@ class UpdateRepresentation(bpy.types.Operator):
|
||||
def update_obj_mesh_representation(self, context, obj):
|
||||
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
|
||||
if not product.is_a("IfcGridAxis"):
|
||||
tool.Geometry.clear_cache(product)
|
||||
|
||||
if product.is_a("IfcGridAxis"):
|
||||
ifcopenshell.api.run("grid.create_axis_curve", self.file, **{"axis_curve": obj, "grid_axis": product})
|
||||
return
|
||||
@@ -355,6 +358,7 @@ class OverrideDeleteTrait:
|
||||
class OverrideDelete(bpy.types.Operator, OverrideDeleteTrait):
|
||||
bl_idname = "object.delete"
|
||||
bl_label = "Delete"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
use_global: bpy.props.BoolProperty(default=False)
|
||||
confirm: bpy.props.BoolProperty(default=True)
|
||||
|
||||
@@ -368,21 +372,29 @@ class OverrideDelete(bpy.types.Operator, OverrideDeleteTrait):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
for obj in context.selected_objects:
|
||||
bpy.data.objects.remove(obj)
|
||||
# Required otherwise gizmos are still visible
|
||||
context.view_layer.objects.active = None
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_confirm(self, event)
|
||||
if self.confirm:
|
||||
return context.window_manager.invoke_confirm(self, event)
|
||||
self.confirm = True
|
||||
return self.execute(context)
|
||||
|
||||
def _execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
self.delete_ifc_object(obj)
|
||||
bpy.data.objects.remove(obj)
|
||||
# Required otherwise gizmos are still visible
|
||||
context.view_layer.objects.active = None
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class OverrideOutlinerDelete(bpy.types.Operator, OverrideDeleteTrait):
|
||||
bl_idname = "outliner.delete"
|
||||
bl_label = "Delete"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
hierarchy: bpy.props.BoolProperty(default=False)
|
||||
|
||||
@classmethod
|
||||
@@ -405,7 +417,6 @@ class OverrideOutlinerDelete(bpy.types.Operator, OverrideDeleteTrait):
|
||||
if item.bl_rna.identifier == "Collection":
|
||||
collection = bpy.data.collections.get(item.name)
|
||||
collection_data = self.get_collection_objects_and_children(collection)
|
||||
print(collection_data)
|
||||
objects_to_delete |= collection_data["objects"]
|
||||
collections_to_delete |= collection_data["children"]
|
||||
collections_to_delete.add(collection)
|
||||
@@ -453,6 +464,7 @@ class OverrideOutlinerDelete(bpy.types.Operator, OverrideDeleteTrait):
|
||||
class OverrideDuplicateMove(bpy.types.Operator):
|
||||
bl_idname = "object.duplicate_move"
|
||||
bl_label = "Duplicate Objects"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -21,37 +21,47 @@ from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddConstituent,
|
||||
operator.AddDefaultMaterial,
|
||||
operator.AddLayer,
|
||||
operator.AddListItem,
|
||||
operator.AddMaterial,
|
||||
operator.AddMaterialSet,
|
||||
operator.AddProfile,
|
||||
operator.AssignMaterial,
|
||||
operator.AssignParameterizedProfile,
|
||||
operator.CopyMaterial,
|
||||
operator.DisableEditingAssignedMaterial,
|
||||
operator.DisableEditingMaterialSetItem,
|
||||
operator.DisableEditingMaterials,
|
||||
operator.EditAssignedMaterial,
|
||||
operator.EditMaterialSetItem,
|
||||
operator.EnableEditingAssignedMaterial,
|
||||
operator.EnableEditingMaterialSetItem,
|
||||
operator.LoadMaterials,
|
||||
operator.RemoveConstituent,
|
||||
operator.RemoveLayer,
|
||||
operator.RemoveListItem,
|
||||
operator.RemoveMaterial,
|
||||
operator.RemoveMaterialSet,
|
||||
operator.RemoveProfile,
|
||||
operator.ReorderMaterialSetItem,
|
||||
operator.SelectByMaterial,
|
||||
operator.UnassignMaterial,
|
||||
operator.UnlinkMaterial,
|
||||
prop.Material,
|
||||
prop.BIMMaterialProperties,
|
||||
prop.BIMObjectMaterialProperties,
|
||||
ui.BIM_PT_materials,
|
||||
ui.BIM_PT_material,
|
||||
ui.BIM_PT_object_material,
|
||||
ui.BIM_UL_materials,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
|
||||
bpy.types.Object.BIMObjectMaterialProperties = bpy.props.PointerProperty(type=prop.BIMObjectMaterialProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMMaterialProperties
|
||||
del bpy.types.Object.BIMObjectMaterialProperties
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.schema
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def refresh():
|
||||
MaterialsData.is_loaded = False
|
||||
ObjectMaterialData.is_loaded = False
|
||||
|
||||
|
||||
class MaterialsData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"total_materials": cls.total_materials(),
|
||||
"material_types": cls.material_types(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def total_materials(cls):
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
return (
|
||||
len(tool.Ifc.get().by_type("IfcMaterial"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialLayerSet"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialList"))
|
||||
)
|
||||
return (
|
||||
len(tool.Ifc.get().by_type("IfcMaterial"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialConstituentSet"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialLayerSet"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialProfileSet"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialList"))
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def material_types(cls):
|
||||
material_types = [
|
||||
"IfcMaterial",
|
||||
"IfcMaterialConstituentSet",
|
||||
"IfcMaterialLayerSet",
|
||||
"IfcMaterialProfileSet",
|
||||
"IfcMaterialList",
|
||||
]
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
material_types = ["IfcMaterial", "IfcMaterialLayerSet", "IfcMaterialList"]
|
||||
return [(m, m, "") for m in material_types]
|
||||
|
||||
|
||||
class ObjectMaterialData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"materials": cls.materials(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def materials(cls):
|
||||
return sorted([(str(m.id()), m.Name or "Unnamed", "") for m in tool.Ifc.get().by_type("IfcMaterial")], key=lambda x: x[1])
|
||||
@@ -31,11 +31,32 @@ from ifcopenshell.api.material.data import Data
|
||||
from ifcopenshell.api.profile.data import Data as ProfileData
|
||||
|
||||
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
class LoadMaterials(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_materials"
|
||||
bl_label = "Load Materials"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.load_materials(tool.Material, context.scene.BIMMaterialProperties.material_type)
|
||||
|
||||
|
||||
class DisableEditingMaterials(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_materials"
|
||||
bl_label = "Disable Editing Materials"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.disable_editing_materials(tool.Material)
|
||||
|
||||
|
||||
class SelectByMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.select_by_material"
|
||||
bl_label = "Select By Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
material: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.select_by_material(tool.Material, material=tool.Ifc.get().by_id(self.material))
|
||||
|
||||
|
||||
class AssignParameterizedProfile(bpy.types.Operator):
|
||||
@@ -68,70 +89,54 @@ class AssignParameterizedProfile(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddDefaultMaterial(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_default_material"
|
||||
bl_label = "Add Default Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_default_material(tool.Ifc, tool.Material)
|
||||
Data.load(IfcStore.get_file())
|
||||
|
||||
|
||||
class AddMaterial(bpy.types.Operator):
|
||||
class AddMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_material"
|
||||
bl_label = "Add Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.materials.get(self.obj) if self.obj else context.active_object.active_material
|
||||
self.file = IfcStore.get_file()
|
||||
result = ifcopenshell.api.run("material.add_material", self.file, **{"name": obj.name})
|
||||
IfcStore.link_element(result, obj)
|
||||
if obj.BIMMaterialProperties.ifc_style_id:
|
||||
context = ifcopenshell.util.representation.get_context(self.file, "Model", "Body", "MODEL_VIEW")
|
||||
if context:
|
||||
ifcopenshell.api.run(
|
||||
"style.assign_material_style",
|
||||
self.file,
|
||||
**{
|
||||
"material": result,
|
||||
"style": self.file.by_id(obj.BIMMaterialProperties.ifc_style_id),
|
||||
"context": context,
|
||||
},
|
||||
)
|
||||
obj = bpy.data.materials.get(self.obj) if self.obj else None
|
||||
core.add_material(tool.Ifc, tool.Material, tool.Style, obj=obj)
|
||||
Data.load(IfcStore.get_file())
|
||||
material_prop_purge()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveMaterial(bpy.types.Operator):
|
||||
class AddMaterialSet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_material_set"
|
||||
bl_label = "Add Material Set"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
set_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_material_set(tool.Ifc, tool.Material, set_type=self.set_type)
|
||||
Data.load(IfcStore.get_file())
|
||||
material_prop_purge()
|
||||
|
||||
|
||||
class RemoveMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_material"
|
||||
bl_label = "Remove Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
material: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.materials.get(self.obj) if self.obj else context.active_object.active_material
|
||||
self.file = IfcStore.get_file()
|
||||
result = ifcopenshell.api.run(
|
||||
"material.remove_material",
|
||||
self.file,
|
||||
**{"material": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)},
|
||||
)
|
||||
obj.BIMObjectProperties.ifc_definition_id = 0
|
||||
core.remove_material(tool.Ifc, tool.Material, tool.Style, material=tool.Ifc.get().by_id(self.material))
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class UnlinkMaterial(bpy.types.Operator, Operator):
|
||||
class RemoveMaterialSet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_material_set"
|
||||
bl_label = "Remove Material Set"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
material: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.remove_material_set(tool.Ifc, tool.Material, material=tool.Ifc.get().by_id(self.material))
|
||||
Data.load(IfcStore.get_file())
|
||||
|
||||
|
||||
class UnlinkMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.unlink_material"
|
||||
bl_label = "Unlink Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import bpy
|
||||
from ifcopenshell.api.material.data import Data
|
||||
from blenderbim.bim.module.material.data import MaterialsData, ObjectMaterialData
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
@@ -32,18 +33,15 @@ from bpy.props import (
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
materials_enum = []
|
||||
materialtypes_enum = []
|
||||
profileclasses_enum = []
|
||||
parameterizedprofileclasses_enum = []
|
||||
|
||||
|
||||
def purge():
|
||||
global materials_enum
|
||||
global materialtypes_enum
|
||||
global profileclasses_enum
|
||||
global parameterizedprofileclasses_enum
|
||||
materials_enum = []
|
||||
materialtypes_enum = []
|
||||
profileclasses_enum = []
|
||||
parameterizedprofileclasses_enum = []
|
||||
@@ -77,12 +75,10 @@ def getParameterizedProfileClasses(self, context):
|
||||
return parameterizedprofileclasses_enum
|
||||
|
||||
|
||||
def getMaterials(self, context):
|
||||
global materials_enum
|
||||
if len(materials_enum) == 0 and IfcStore.get_file():
|
||||
materials_enum.clear()
|
||||
materials_enum = [(str(m_id), m["Name"], "") for m_id, m in Data.materials.items()]
|
||||
return materials_enum
|
||||
def get_materials(self, context):
|
||||
if not ObjectMaterialData.is_loaded:
|
||||
ObjectMaterialData.load()
|
||||
return ObjectMaterialData.data["materials"]
|
||||
|
||||
|
||||
def getMaterialTypes(self, context):
|
||||
@@ -104,9 +100,28 @@ def getMaterialTypes(self, context):
|
||||
return materialtypes_enum
|
||||
|
||||
|
||||
def get_material_types(self, context):
|
||||
if not MaterialsData.is_loaded:
|
||||
MaterialsData.load()
|
||||
return MaterialsData.data["material_types"]
|
||||
|
||||
|
||||
class Material(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
total_elements: IntProperty(name="Total Elements")
|
||||
|
||||
|
||||
class BIMMaterialProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
material_type: EnumProperty(items=get_material_types, name="Material Type")
|
||||
materials: CollectionProperty(name="Materials", type=Material)
|
||||
active_material_index: IntProperty(name="Active Material Index")
|
||||
|
||||
|
||||
class BIMObjectMaterialProperties(PropertyGroup):
|
||||
material_type: EnumProperty(items=getMaterialTypes, name="Material Type")
|
||||
material: EnumProperty(items=getMaterials, name="Material")
|
||||
material: EnumProperty(items=get_materials, name="Material")
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
material_set_usage_attributes: CollectionProperty(name="Material Set Usage Attributes", type=Attribute)
|
||||
material_set_attributes: CollectionProperty(name="Material Set Attributes", type=Attribute)
|
||||
@@ -115,7 +130,7 @@ class BIMObjectMaterialProperties(PropertyGroup):
|
||||
material_set_item_profile_attributes: CollectionProperty(
|
||||
name="Material Set Item Profile Attributes", type=Attribute
|
||||
)
|
||||
material_set_item_material: EnumProperty(items=getMaterials, name="Material")
|
||||
material_set_item_material: EnumProperty(items=get_materials, name="Material")
|
||||
profile_classes: EnumProperty(items=getProfileClasses, name="Profile Classes")
|
||||
parameterized_profile_classes: EnumProperty(
|
||||
items=getParameterizedProfileClasses, name="Parameterized Profile Classes"
|
||||
|
||||
@@ -17,11 +17,62 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blenderbim.bim.helper
|
||||
from bpy.types import Panel
|
||||
from bpy.types import Panel, UIList
|
||||
from ifcopenshell.api.material.data import Data
|
||||
from ifcopenshell.api.profile.data import Data as ProfileData
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import draw_attributes
|
||||
from blenderbim.bim.module.material.data import MaterialsData, ObjectMaterialData
|
||||
|
||||
|
||||
class BIM_PT_materials(Panel):
|
||||
bl_label = "IFC Materials"
|
||||
bl_idname = "BIM_PT_materials"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_geometry"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
if not MaterialsData.is_loaded:
|
||||
MaterialsData.load()
|
||||
|
||||
self.props = context.scene.BIMMaterialProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Materials Found".format(MaterialsData.data["total_materials"]), icon="MATERIAL")
|
||||
if self.props.is_editing:
|
||||
row.operator("bim.disable_editing_materials", text="", icon="CANCEL")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "material_type", text="")
|
||||
row.operator("bim.load_materials", text="", icon="IMPORT")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
|
||||
if self.props.material_type == "IfcMaterial":
|
||||
row.operator("bim.add_material", text="", icon="ADD")
|
||||
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||
material = self.props.materials[self.props.active_material_index]
|
||||
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.material = material.ifc_definition_id
|
||||
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
|
||||
else:
|
||||
row.operator("bim.add_material_set", text="", icon="ADD").set_type = self.props.material_type
|
||||
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||
material = self.props.materials[self.props.active_material_index]
|
||||
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.material = material.ifc_definition_id
|
||||
row.operator("bim.remove_material_set", text="", icon="X").material = material.ifc_definition_id
|
||||
|
||||
self.layout.template_list("BIM_UL_materials", "", self.props, "materials", self.props, "active_material_index")
|
||||
|
||||
|
||||
class BIM_PT_material(Panel):
|
||||
@@ -37,11 +88,13 @@ class BIM_PT_material(Panel):
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout.row(align=True)
|
||||
if bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id):
|
||||
row.operator("bim.remove_material", icon="X", text="Remove IFC Material")
|
||||
material_id = context.active_object.active_material.BIMObjectProperties.ifc_definition_id
|
||||
if bool(material_id):
|
||||
row.operator("bim.remove_material", icon="X", text="Remove IFC Material").material = material_id
|
||||
row.operator("bim.unlink_material", icon="UNLINKED", text="")
|
||||
else:
|
||||
row.operator("bim.add_material", icon="ADD", text="Create IFC Material")
|
||||
op = row.operator("bim.add_material", icon="ADD", text="Create IFC Material")
|
||||
op.obj = context.active_object.active_material.name
|
||||
|
||||
|
||||
class BIM_PT_object_material(Panel):
|
||||
@@ -66,6 +119,9 @@ class BIM_PT_object_material(Panel):
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
if not ObjectMaterialData.is_loaded:
|
||||
ObjectMaterialData.load()
|
||||
|
||||
self.file = IfcStore.get_file()
|
||||
self.oprops = context.active_object.BIMObjectProperties
|
||||
self.props = context.active_object.BIMObjectMaterialProperties
|
||||
@@ -77,10 +133,10 @@ class BIM_PT_object_material(Panel):
|
||||
ProfileData.load(self.file)
|
||||
self.product_data = Data.products[self.oprops.ifc_definition_id]
|
||||
|
||||
if not Data.materials:
|
||||
if not ObjectMaterialData.data["materials"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No Materials Available")
|
||||
row.operator("bim.add_default_material", icon="ADD", text="")
|
||||
row.operator("bim.add_material", icon="ADD", text="").obj = ""
|
||||
return
|
||||
|
||||
if self.product_data:
|
||||
@@ -333,3 +389,13 @@ class BIM_PT_object_material(Panel):
|
||||
if total_thickness:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"Total Thickness: {total_thickness:.3f}")
|
||||
|
||||
|
||||
class BIM_UL_materials(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.name)
|
||||
row2 = row.row()
|
||||
row2.alignment = "RIGHT"
|
||||
row2.label(text=str(item.total_elements))
|
||||
|
||||
@@ -39,7 +39,12 @@ class AuthoringData:
|
||||
@classmethod
|
||||
def ifc_classes(cls):
|
||||
results = []
|
||||
classes = {e.is_a() for e in tool.Ifc.get().by_type("IfcElementType")}
|
||||
classes = {
|
||||
e.is_a()
|
||||
for e in tool.Ifc.get().by_type("IfcElementType")
|
||||
+ tool.Ifc.get().by_type("IfcDoorStyle")
|
||||
+ tool.Ifc.get().by_type("IfcWindowStyle")
|
||||
}
|
||||
results.extend([(c, c, "") for c in sorted(classes)])
|
||||
return results
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ from bpy.app.handlers import persistent
|
||||
@persistent
|
||||
def load_post(*args):
|
||||
ifcopenshell.api.add_pre_listener("attribute.edit_attributes", "BlenderBIM.Root.SyncName", root.sync_name)
|
||||
ifcopenshell.api.add_pre_listener("style.edit_presentation_style", "BlenderBIM.Root.SyncStyleName", root.sync_name)
|
||||
|
||||
ifcopenshell.api.add_post_listener(
|
||||
"geometry.add_representation", "BlenderBIM.Product.GenerateBox", product.generate_box
|
||||
|
||||
@@ -34,7 +34,6 @@ from math import pi, degrees
|
||||
from mathutils import Vector, Matrix
|
||||
|
||||
|
||||
|
||||
class MepGenerator:
|
||||
def __init__(self, relating_type):
|
||||
self.relating_type = relating_type
|
||||
|
||||
@@ -17,24 +17,34 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.style.data import Data as StyleData
|
||||
|
||||
|
||||
def sync_name(usecase_path, ifc_file, settings):
|
||||
if usecase_path == "attribute.edit_attributes":
|
||||
element = settings["product"]
|
||||
elif usecase_path == "style.edit_presentation_style":
|
||||
element = settings["style"]
|
||||
|
||||
if "Name" not in settings["attributes"]:
|
||||
return
|
||||
obj = IfcStore.get_element(settings["product"].id())
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if not obj:
|
||||
return
|
||||
if isinstance(obj, bpy.types.Object):
|
||||
new_name = "{}/{}".format(settings["product"].is_a(), settings["attributes"]["Name"] or "Unnamed")
|
||||
new_name = "{}/{}".format(element.is_a(), settings["attributes"]["Name"] or "Unnamed")
|
||||
elif isinstance(obj, bpy.types.Material):
|
||||
new_name = settings["attributes"]["Name"] or "Unnamed"
|
||||
if obj.BIMMaterialProperties.ifc_style_id:
|
||||
IfcStore.get_file().by_id(obj.BIMMaterialProperties.ifc_style_id).Name = new_name
|
||||
StyleData.load(IfcStore.get_file(), obj.BIMMaterialProperties.ifc_style_id)
|
||||
material = tool.Ifc.get_entity(obj)
|
||||
if material and material != element:
|
||||
material.Name = new_name
|
||||
style = tool.Style.get_style(obj)
|
||||
if style and style != element:
|
||||
style.Name = new_name
|
||||
collection = bpy.data.collections.get(obj.name)
|
||||
if collection:
|
||||
collection.name = new_name
|
||||
obj.name = new_name
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
|
||||
@@ -611,10 +611,14 @@ class LinkIfc(bpy.types.Operator):
|
||||
bl_description = "Link a Blender file"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.blend;*.blend1", options={"HIDDEN"})
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||
|
||||
def execute(self, context):
|
||||
new = context.scene.BIMProjectProperties.links.add()
|
||||
new.name = self.filepath
|
||||
filepath = self.filepath
|
||||
if self.use_relative_path:
|
||||
filepath = os.path.relpath(filepath, bpy.path.abspath("//"))
|
||||
new.name = filepath
|
||||
bpy.ops.bim.load_link(filepath=self.filepath)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -646,13 +650,16 @@ class UnloadLink(bpy.types.Operator):
|
||||
filepath: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
filepath = self.filepath
|
||||
if not os.path.isabs(filepath):
|
||||
filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), filepath))
|
||||
for collection in context.scene.collection.children:
|
||||
if collection.library and collection.library.filepath == self.filepath:
|
||||
if collection.library and collection.library.filepath == filepath:
|
||||
context.scene.collection.children.unlink(collection)
|
||||
for scene in bpy.data.scenes:
|
||||
if scene.library and scene.library.filepath == self.filepath:
|
||||
if scene.library and scene.library.filepath == filepath:
|
||||
bpy.data.scenes.remove(scene)
|
||||
link = context.scene.BIMProjectProperties.links.get(self.filepath)
|
||||
link = context.scene.BIMProjectProperties.links.get(filepath)
|
||||
link.is_loaded = False
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -665,16 +672,19 @@ class LoadLink(bpy.types.Operator):
|
||||
filepath: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
filepath = self.filepath
|
||||
if not os.path.isabs(filepath):
|
||||
filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), filepath))
|
||||
with bpy.data.libraries.load(self.filepath, link=True) as (data_from, data_to):
|
||||
data_to.scenes = data_from.scenes
|
||||
for scene in bpy.data.scenes:
|
||||
if not scene.library or scene.library.filepath != self.filepath:
|
||||
if not scene.library or scene.library.filepath != filepath:
|
||||
continue
|
||||
for child in scene.collection.children:
|
||||
if "IfcProject" not in child.name:
|
||||
continue
|
||||
bpy.data.scenes[0].collection.children.link(child)
|
||||
link = context.scene.BIMProjectProperties.links.get(self.filepath)
|
||||
link = context.scene.BIMProjectProperties.links.get(filepath)
|
||||
link.is_loaded = True
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -284,11 +284,12 @@ class BIM_UL_links(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.name)
|
||||
if item.is_loaded:
|
||||
row.label(text=item.name)
|
||||
op = row.operator("bim.unload_link", text="", icon="UNLINKED")
|
||||
op.filepath = item.name
|
||||
else:
|
||||
row.prop(item, "name", text="")
|
||||
op = row.operator("bim.load_link", text="", icon="LINKED")
|
||||
op.filepath = item.name
|
||||
op = row.operator("bim.unlink_ifc", text="", icon="X")
|
||||
|
||||
@@ -32,12 +32,47 @@ class IfcClassData:
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.data = {
|
||||
"contexts": cls.contexts(),
|
||||
"has_entity": cls.has_entity(),
|
||||
"name": cls.name(),
|
||||
"ifc_class": cls.ifc_class(),
|
||||
}
|
||||
cls.data = {}
|
||||
cls.data["ifc_products"] = cls.ifc_products()
|
||||
cls.data["ifc_classes"] = cls.ifc_classes()
|
||||
cls.data["contexts"] = cls.contexts()
|
||||
cls.data["has_entity"] = cls.has_entity()
|
||||
cls.data["name"] = cls.name()
|
||||
cls.data["ifc_class"] = cls.ifc_class()
|
||||
|
||||
@classmethod
|
||||
def ifc_products(cls):
|
||||
products = [
|
||||
"IfcElement",
|
||||
"IfcElementType",
|
||||
"IfcSpatialElement",
|
||||
"IfcGroup",
|
||||
"IfcStructuralItem",
|
||||
"IfcContext",
|
||||
"IfcAnnotation",
|
||||
"IfcRelSpaceBoundary",
|
||||
]
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
products = [
|
||||
"IfcElement",
|
||||
"IfcElementType",
|
||||
"IfcSpatialStructureElement",
|
||||
"IfcGroup",
|
||||
"IfcStructuralItem",
|
||||
"IfcAnnotation",
|
||||
"IfcRelSpaceBoundary",
|
||||
]
|
||||
return [(e, e, "") for e in products]
|
||||
|
||||
@classmethod
|
||||
def ifc_classes(cls):
|
||||
ifc_product = bpy.context.scene.BIMRootProperties.ifc_product
|
||||
declaration = tool.Ifc.schema().declaration_by_name(ifc_product)
|
||||
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||
names = [d.name() for d in declarations]
|
||||
if ifc_product == "IfcElementType":
|
||||
names.extend(("IfcDoorStyle", "IfcWindowStyle"))
|
||||
return [(c, c, "") for c in sorted(names)]
|
||||
|
||||
@classmethod
|
||||
def contexts(cls):
|
||||
|
||||
@@ -33,17 +33,11 @@ from bpy.props import (
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
products_enum = []
|
||||
classes_enum = []
|
||||
types_enum = []
|
||||
|
||||
|
||||
def purge():
|
||||
global products_enum
|
||||
global classes_enum
|
||||
global types_enum
|
||||
products_enum = []
|
||||
classes_enum = []
|
||||
types_enum = []
|
||||
|
||||
|
||||
@@ -61,10 +55,9 @@ def getIfcPredefinedTypes(self, context):
|
||||
return types_enum
|
||||
|
||||
|
||||
def refreshClasses(self, context):
|
||||
global classes_enum
|
||||
classes_enum.clear()
|
||||
enum = getIfcClasses(self, context)
|
||||
def refresh_classes(self, context):
|
||||
IfcClassData.load()
|
||||
enum = get_ifc_classes(self, context)
|
||||
context.scene.BIMRootProperties.ifc_class = enum[0][0]
|
||||
|
||||
|
||||
@@ -76,38 +69,16 @@ def refreshPredefinedTypes(self, context):
|
||||
context.scene.BIMRootProperties.ifc_predefined_type = enum[0][0]
|
||||
|
||||
|
||||
def getIfcProducts(self, context):
|
||||
global products_enum
|
||||
file = IfcStore.get_file()
|
||||
if len(products_enum) < 1:
|
||||
products_enum.extend(
|
||||
[
|
||||
(e, e, "")
|
||||
for e in [
|
||||
"IfcElement",
|
||||
"IfcElementType",
|
||||
"IfcSpatialElement",
|
||||
"IfcGroup",
|
||||
"IfcStructuralItem",
|
||||
"IfcContext",
|
||||
"IfcAnnotation",
|
||||
"IfcRelSpaceBoundary",
|
||||
]
|
||||
]
|
||||
)
|
||||
if file.schema == "IFC2X3":
|
||||
products_enum[2] = ("IfcSpatialStructureElement", "IfcSpatialStructureElement", "")
|
||||
return products_enum
|
||||
def get_ifc_products(self, context):
|
||||
if not IfcClassData.is_loaded:
|
||||
IfcClassData.load()
|
||||
return IfcClassData.data["ifc_products"]
|
||||
|
||||
|
||||
def getIfcClasses(self, context):
|
||||
global classes_enum
|
||||
file = IfcStore.get_file()
|
||||
if len(classes_enum) < 1 and file:
|
||||
declaration = IfcStore.get_schema().declaration_by_name(context.scene.BIMRootProperties.ifc_product)
|
||||
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||
classes_enum.extend([(c, c, "") for c in sorted([d.name() for d in declarations])])
|
||||
return classes_enum
|
||||
def get_ifc_classes(self, context):
|
||||
if not IfcClassData.is_loaded:
|
||||
IfcClassData.load()
|
||||
return IfcClassData.data["ifc_classes"]
|
||||
|
||||
|
||||
def get_contexts(self, context):
|
||||
@@ -118,7 +89,7 @@ def get_contexts(self, context):
|
||||
|
||||
class BIMRootProperties(PropertyGroup):
|
||||
contexts: EnumProperty(items=get_contexts, name="Contexts")
|
||||
ifc_product: EnumProperty(items=getIfcProducts, name="Products", update=refreshClasses)
|
||||
ifc_class: EnumProperty(items=getIfcClasses, name="Class", update=refreshPredefinedTypes)
|
||||
ifc_product: EnumProperty(items=get_ifc_products, name="Products", update=refresh_classes)
|
||||
ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=refreshPredefinedTypes)
|
||||
ifc_predefined_type: EnumProperty(items=getIfcPredefinedTypes, name="Predefined Type", default=None)
|
||||
ifc_userdefined_type: StringProperty(name="Userdefined Type")
|
||||
|
||||
@@ -989,6 +989,7 @@ class GenerateGanttChart(bpy.types.Operator):
|
||||
data = {
|
||||
"pID": task.id(),
|
||||
"pName": task.Name,
|
||||
"pCaption": task.Name,
|
||||
"pStart": task.TaskTime.ScheduleStart if task.TaskTime else "",
|
||||
"pEnd": task.TaskTime.ScheduleFinish if task.TaskTime else "",
|
||||
"pPlanStart": task.TaskTime.ScheduleStart if task.TaskTime else "",
|
||||
@@ -999,6 +1000,7 @@ class GenerateGanttChart(bpy.types.Operator):
|
||||
"pParent": task.Nests[0].RelatingObject.id() if task.Nests else 0,
|
||||
"pOpen": 1,
|
||||
"pCost": 1,
|
||||
"ifcduration": task.TaskTime.ScheduleDuration if task.TaskTime else "",
|
||||
}
|
||||
if task.TaskTime and task.TaskTime.IsCritical:
|
||||
data["pClass"] = "gtaskred"
|
||||
@@ -1230,7 +1232,7 @@ class ExportMSP(bpy.types.Operator, ImportHelper):
|
||||
start = time.time()
|
||||
ifc2msp = Ifc2Msp()
|
||||
ifc2msp.work_schedule = self.file.by_type("IfcWorkSchedule")[0]
|
||||
ifc2msp.xml = self.filepath
|
||||
ifc2msp.xml = bpy.path.ensure_ext(self.filepath, ".xml")
|
||||
ifc2msp.file = self.file
|
||||
ifc2msp.holiday_start_date = parser.parse(self.holiday_start_date).date()
|
||||
ifc2msp.holiday_finish_date = parser.parse(self.holiday_finish_date).date()
|
||||
@@ -1259,7 +1261,7 @@ class ExportP6(bpy.types.Operator, ImportHelper):
|
||||
self.file = IfcStore.get_file()
|
||||
start = time.time()
|
||||
ifc2p6 = Ifc2P6()
|
||||
ifc2p6.xml = self.filepath
|
||||
ifc2p6.xml = bpy.path.ensure_ext(self.filepath, ".xml")
|
||||
ifc2p6.file = self.file
|
||||
ifc2p6.holiday_start_date = parser.parse(self.holiday_start_date).date()
|
||||
ifc2p6.holiday_finish_date = parser.parse(self.holiday_finish_date).date()
|
||||
@@ -1679,6 +1681,10 @@ class EnableEditingSequenceTimeLag(bpy.types.Operator):
|
||||
|
||||
def import_attributes(self, name, prop, data):
|
||||
if name == "LagValue":
|
||||
prop = self.props.time_lag_attributes.add()
|
||||
prop.name = name
|
||||
prop.is_null = data[name] is None
|
||||
prop.is_optional = False
|
||||
if isinstance(data[name], isodate.Duration):
|
||||
prop.data_type = "string"
|
||||
prop.string_value = (
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import isodate
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.attribute
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -184,6 +185,16 @@ def updateTaskDuration(self, context):
|
||||
props = context.scene.BIMWorkScheduleProperties
|
||||
if not props.is_task_update_enabled:
|
||||
return
|
||||
|
||||
if self.duration == "-":
|
||||
return
|
||||
|
||||
try:
|
||||
isodate.parse_duration(self.duration),
|
||||
except:
|
||||
self.duration = "-"
|
||||
return
|
||||
|
||||
self.file = IfcStore.get_file()
|
||||
task = self.file.by_id(self.ifc_definition_id)
|
||||
if task.TaskTime:
|
||||
|
||||
@@ -21,22 +21,30 @@ from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddStyle,
|
||||
operator.EnableEditingStyle,
|
||||
operator.DisableEditingStyle,
|
||||
operator.DisableEditingStyles,
|
||||
operator.EditStyle,
|
||||
operator.EnableEditingStyle,
|
||||
operator.LoadStyles,
|
||||
operator.RemoveStyle,
|
||||
operator.UnlinkStyle,
|
||||
operator.UpdateStyleColours,
|
||||
operator.UpdateStyleTextures,
|
||||
operator.UnlinkStyle,
|
||||
prop.Style,
|
||||
prop.BIMStylesProperties,
|
||||
prop.BIMStyleProperties,
|
||||
ui.BIM_PT_styles,
|
||||
ui.BIM_PT_style,
|
||||
ui.BIM_PT_style_attributes,
|
||||
ui.BIM_UL_styles,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMStylesProperties = bpy.props.PointerProperty(type=prop.BIMStylesProperties)
|
||||
bpy.types.Material.BIMStyleProperties = bpy.props.PointerProperty(type=prop.BIMStyleProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMStylesProperties
|
||||
del bpy.types.Material.BIMStyleProperties
|
||||
|
||||
@@ -18,12 +18,33 @@
|
||||
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def refresh():
|
||||
StylesData.is_loaded = False
|
||||
StyleAttributesData.is_loaded = False
|
||||
|
||||
|
||||
class StylesData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"style_types": cls.style_types(), "total_styles": cls.total_styles()}
|
||||
|
||||
@classmethod
|
||||
def style_types(cls):
|
||||
declaration = tool.Ifc.schema().declaration_by_name("IfcPresentationStyle")
|
||||
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||
return [(c, c, "") for c in sorted([d.name() for d in declarations])]
|
||||
|
||||
@classmethod
|
||||
def total_styles(cls):
|
||||
return len(tool.Ifc.get().by_type("IfcPresentationStyle"))
|
||||
|
||||
|
||||
class StyleAttributesData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@@ -59,9 +59,10 @@ class RemoveStyle(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_style"
|
||||
bl_label = "Remove Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
style: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.remove_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
|
||||
core.remove_style(tool.Ifc, tool.Material, tool.Style, style=tool.Ifc.get().by_id(self.style))
|
||||
|
||||
|
||||
class AddStyle(bpy.types.Operator, Operator):
|
||||
@@ -107,3 +108,22 @@ class EditStyle(bpy.types.Operator, Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
core.edit_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
|
||||
|
||||
|
||||
class DisableEditingStyles(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.disable_editing_styles"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_label = "Disable Editing Styles"
|
||||
|
||||
def _execute(self, context):
|
||||
core.disable_editing_styles(tool.Style)
|
||||
|
||||
|
||||
class LoadStyles(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.load_styles"
|
||||
bl_label = "Load Styles"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
style_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.load_styles(tool.Style, style_type=self.style_type)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.module.style.data import StylesData
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -32,6 +33,25 @@ from bpy.props import (
|
||||
)
|
||||
|
||||
|
||||
def get_style_types(self, context):
|
||||
if not StylesData.is_loaded:
|
||||
StylesData.load()
|
||||
return StylesData.data["style_types"]
|
||||
|
||||
|
||||
class Style(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
total_elements: IntProperty(name="Total Elements")
|
||||
|
||||
|
||||
class BIMStylesProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
style_type: EnumProperty(items=get_style_types, name="Style Type")
|
||||
styles: CollectionProperty(name="Styles", type=Style)
|
||||
active_style_index: IntProperty(name="Active Style Index")
|
||||
|
||||
|
||||
class BIMStyleProperties(PropertyGroup):
|
||||
attributes: CollectionProperty(name="Attributes", type=Attribute)
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
|
||||
@@ -17,9 +17,51 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blenderbim.bim.helper
|
||||
from bpy.types import Panel
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.style.data import StyleAttributesData
|
||||
from blenderbim.bim.module.style.data import StylesData, StyleAttributesData
|
||||
|
||||
|
||||
class BIM_PT_styles(Panel):
|
||||
bl_label = "IFC Styles"
|
||||
bl_idname = "BIM_PT_styles"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_geometry"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
if not StylesData.is_loaded:
|
||||
StylesData.load()
|
||||
|
||||
self.props = context.scene.BIMStylesProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Styles Found".format(StylesData.data["total_styles"]), icon="MATERIAL")
|
||||
row = self.layout.row(align=True)
|
||||
if self.props.is_editing:
|
||||
row.operator("bim.disable_editing_styles", text="", icon="CANCEL")
|
||||
else:
|
||||
row.prop(self.props, "style_type", text="")
|
||||
row.operator("bim.load_styles", text="", icon="IMPORT").style_type = self.props.style_type
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
|
||||
#row.operator("bim.add_presentation_style", text="", icon="ADD")
|
||||
if self.props.styles and self.props.active_style_index < len(self.props.styles):
|
||||
style = self.props.styles[self.props.active_style_index]
|
||||
# op = row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF")
|
||||
# op.style = style.ifc_definition_id
|
||||
row.operator("bim.remove_style", text="", icon="X").style = style.ifc_definition_id
|
||||
|
||||
self.layout.template_list("BIM_UL_styles", "", self.props, "styles", self.props, "active_style_index")
|
||||
|
||||
|
||||
class BIM_PT_style(Panel):
|
||||
@@ -44,7 +86,7 @@ class BIM_PT_style(Panel):
|
||||
row.operator("bim.update_style_colours", icon="GREASEPENCIL")
|
||||
row.operator("bim.update_style_textures", icon="TEXTURE", text="")
|
||||
row.operator("bim.unlink_style", icon="UNLINKED", text="")
|
||||
row.operator("bim.remove_style", icon="X", text="")
|
||||
row.operator("bim.remove_style", icon="X", text="").style = props.ifc_style_id
|
||||
else:
|
||||
row.operator("bim.add_style", icon="ADD")
|
||||
|
||||
@@ -89,3 +131,13 @@ class BIM_PT_style_attributes(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=attribute["name"])
|
||||
row.label(text=attribute["value"])
|
||||
|
||||
|
||||
class BIM_UL_styles(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.name)
|
||||
#row2 = row.row()
|
||||
#row2.alignment = "RIGHT"
|
||||
#row2.label(text=str(item.total_elements))
|
||||
|
||||
@@ -20,18 +20,19 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AssignSceneUnits,
|
||||
operator.AssignUnit,
|
||||
operator.UnassignUnit,
|
||||
operator.LoadUnits,
|
||||
operator.DisableUnitEditingUI,
|
||||
operator.RemoveUnit,
|
||||
operator.AddContextDependentUnit,
|
||||
operator.AddConversionBasedUnit,
|
||||
operator.AddMonetaryUnit,
|
||||
operator.AddSIUnit,
|
||||
operator.AddContextDependentUnit,
|
||||
operator.EnableEditingUnit,
|
||||
operator.AssignSceneUnits,
|
||||
operator.AssignUnit,
|
||||
operator.DisableEditingUnit,
|
||||
operator.DisableUnitEditingUI,
|
||||
operator.EditUnit,
|
||||
operator.EnableEditingUnit,
|
||||
operator.LoadUnits,
|
||||
operator.RemoveUnit,
|
||||
operator.UnassignUnit,
|
||||
prop.Unit,
|
||||
prop.BIMUnitProperties,
|
||||
ui.BIM_PT_units,
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.schema
|
||||
import ifcopenshell.util.attribute
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
@@ -29,9 +33,32 @@ class UnitsData:
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"total_units": cls.get_total_units()}
|
||||
cls.data = {
|
||||
"unit_classes": cls.unit_classes(),
|
||||
"named_unit_types": cls.named_unit_types(),
|
||||
"conversion_unit_types": cls.conversion_unit_types(),
|
||||
"total_units": cls.get_total_units()
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def unit_classes(cls):
|
||||
declarations = ifcopenshell.util.schema.get_subtypes(tool.Ifc.schema().declaration_by_name("IfcNamedUnit"))
|
||||
results = [(c, c, "") for c in sorted([d.name() for d in declarations])]
|
||||
results.extend([("IfcDerivedUnit", "IfcDerivedUnit", ""), ("IfcMonetaryUnit", "IfcMonetaryUnit", "")])
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def named_unit_types(cls):
|
||||
values = ifcopenshell.util.attribute.get_enum_items(
|
||||
tool.Ifc.schema().declaration_by_name("IfcNamedUnit").all_attributes()[1]
|
||||
)
|
||||
return [(c, c, "") for c in sorted(values)]
|
||||
|
||||
@classmethod
|
||||
def conversion_unit_types(cls):
|
||||
return [(u, u, "") for u in ifcopenshell.util.unit.si_conversions.keys()]
|
||||
|
||||
@classmethod
|
||||
def get_total_units(cls):
|
||||
ifc = tool.Ifc.get()
|
||||
|
||||
@@ -94,6 +94,16 @@ class RemoveUnit(bpy.types.Operator, Operator):
|
||||
core.remove_unit(tool.Ifc, tool.Unit, unit=tool.Ifc.get().by_id(self.unit))
|
||||
|
||||
|
||||
class AddConversionBasedUnit(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_conversion_based_unit"
|
||||
bl_label = "Add Conversion Based Unit"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_conversion_based_unit(tool.Ifc, tool.Unit, name=self.name)
|
||||
|
||||
|
||||
class AddMonetaryUnit(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_monetary_unit"
|
||||
bl_label = "Add Monetary Unit"
|
||||
|
||||
@@ -17,11 +17,9 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.schema
|
||||
import ifcopenshell.util.attribute
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.module.unit.data import UnitsData
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -35,34 +33,22 @@ from bpy.props import (
|
||||
)
|
||||
|
||||
|
||||
unitclasses_enum = []
|
||||
namedunittypes_enum = []
|
||||
def get_unit_classes(self, context):
|
||||
if not UnitsData.is_loaded:
|
||||
UnitsData.load()
|
||||
return UnitsData.data["unit_classes"]
|
||||
|
||||
|
||||
def purge():
|
||||
global unitclasses_enum
|
||||
global namedunittypes_enum
|
||||
unitclasses_enum = []
|
||||
namedunittypes_enum = []
|
||||
def get_conversion_unit_types(self, context):
|
||||
if not UnitsData.is_loaded:
|
||||
UnitsData.load()
|
||||
return UnitsData.data["conversion_unit_types"]
|
||||
|
||||
|
||||
def getUnitClasses(self, context):
|
||||
global unitclasses_enum
|
||||
if not len(unitclasses_enum) and IfcStore.get_file():
|
||||
declarations = ifcopenshell.util.schema.get_subtypes(IfcStore.get_schema().declaration_by_name("IfcNamedUnit"))
|
||||
unitclasses_enum.extend([(c, c, "") for c in sorted([d.name() for d in declarations])])
|
||||
unitclasses_enum.extend([("IfcDerivedUnit", "IfcDerivedUnit", ""), ("IfcMonetaryUnit", "IfcMonetaryUnit", "")])
|
||||
return unitclasses_enum
|
||||
|
||||
|
||||
def getNamedUnitTypes(self, context):
|
||||
global namedunittypes_enum
|
||||
if not len(namedunittypes_enum) and IfcStore.get_file():
|
||||
values = ifcopenshell.util.attribute.get_enum_items(
|
||||
IfcStore.get_schema().declaration_by_name("IfcNamedUnit").all_attributes()[1]
|
||||
)
|
||||
namedunittypes_enum.extend([(c, c, "") for c in sorted(values)])
|
||||
return namedunittypes_enum
|
||||
def get_named_unit_types(self, context):
|
||||
if not UnitsData.is_loaded:
|
||||
UnitsData.load()
|
||||
return UnitsData.data["named_unit_types"]
|
||||
|
||||
|
||||
class Unit(PropertyGroup):
|
||||
@@ -78,6 +64,7 @@ class BIMUnitProperties(PropertyGroup):
|
||||
units: CollectionProperty(name="Units", type=Unit)
|
||||
active_unit_index: IntProperty(name="Active Unit Index")
|
||||
active_unit_id: IntProperty(name="Active Unit Id")
|
||||
unit_classes: EnumProperty(items=getUnitClasses, name="Unit Classes")
|
||||
named_unit_types: EnumProperty(items=getNamedUnitTypes, name="Named Unit Types")
|
||||
unit_classes: EnumProperty(items=get_unit_classes, name="Unit Classes")
|
||||
conversion_unit_types: EnumProperty(items=get_conversion_unit_types, name="Conversion Unit Types")
|
||||
named_unit_types: EnumProperty(items=get_named_unit_types, name="Named Unit Types")
|
||||
unit_attributes: CollectionProperty(name="Unit Attributes", type=Attribute)
|
||||
|
||||
@@ -57,6 +57,10 @@ class BIM_PT_units(Panel):
|
||||
|
||||
if self.props.unit_classes == "IfcMonetaryUnit":
|
||||
row.operator("bim.add_monetary_unit", text="", icon="ADD")
|
||||
elif self.props.unit_classes in ("IfcConversionBasedUnit", "IfcConversionBasedUnitWithOffset"):
|
||||
row.prop(self.props, "conversion_unit_types", text="")
|
||||
op = row.operator("bim.add_conversion_based_unit", text="", icon="ADD")
|
||||
op.name = self.props.conversion_unit_types
|
||||
elif self.props.unit_classes == "IfcDerivedUnit":
|
||||
pass # TODO
|
||||
elif self.props.unit_classes == "IfcSIUnit":
|
||||
|
||||
@@ -44,6 +44,7 @@ class AddOpening(bpy.types.Operator):
|
||||
if not element_id:
|
||||
return {"FINISHED"}
|
||||
element = self.file.by_id(element_id)
|
||||
tool.Geometry.clear_cache(element)
|
||||
if element.is_a("IfcOpeningElement"):
|
||||
self.report({"WARNING"}, "An IfcOpeningElement can't be voided")
|
||||
return {"FINISHED"}
|
||||
@@ -132,6 +133,7 @@ class RemoveOpening(bpy.types.Operator):
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
|
||||
tool.Geometry.clear_cache(tool.Ifc.get_entity(obj))
|
||||
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -49,7 +49,11 @@ class BIM_PT_voids(Panel):
|
||||
if len(context.selected_objects) == 2:
|
||||
op = row.operator("bim.add_opening", icon="ADD", text="Add Opening")
|
||||
for obj in context.selected_objects:
|
||||
if "IfcOpeningElement" in obj.name or not obj.BIMObjectProperties.ifc_definition_id:
|
||||
if (
|
||||
"IfcOpeningElement" in obj.name
|
||||
or "IfcOpeningStandardCase" in obj.name
|
||||
or not obj.BIMObjectProperties.ifc_definition_id
|
||||
):
|
||||
op.opening = obj.name
|
||||
elif len(obj.children) == 1 and not obj.children[0].BIMObjectProperties.ifc_definition_id:
|
||||
op.opening = obj.children[0].name
|
||||
|
||||
@@ -22,6 +22,7 @@ import json
|
||||
import webbrowser
|
||||
import ifcopenshell
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
from . import schema
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.ui import IFCFileSelector
|
||||
@@ -39,6 +40,43 @@ class OpenUri(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectURIAttribute(bpy.types.Operator):
|
||||
bl_idname = "bim.select_uri_attribute"
|
||||
bl_label = "Select URI Attribute"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Select a local file"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
data_path: bpy.props.StringProperty(name="Data Path")
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||
|
||||
def execute(self, context):
|
||||
# data_path contains the latter half of the path to the string_value property
|
||||
# I have no idea how to find out the former half, so let's just use brute force.
|
||||
data_path = self.data_path.replace(".string_value", "")
|
||||
attribute = None
|
||||
try:
|
||||
attribute = eval(f"bpy.context.scene.{data_path}")
|
||||
except:
|
||||
try:
|
||||
attribute = eval(f"bpy.context.active_object.{data_path}")
|
||||
except:
|
||||
try:
|
||||
attribute = eval(f"bpy.context.active_object.active_material.{data_path}")
|
||||
except:
|
||||
# Do you know a better way?
|
||||
pass
|
||||
if attribute:
|
||||
filepath = self.filepath
|
||||
if self.use_relative_path:
|
||||
filepath = os.path.relpath(filepath, os.path.dirname(tool.Ifc.get_path()))
|
||||
attribute.string_value = filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class SelectIfcFile(bpy.types.Operator, IFCFileSelector):
|
||||
bl_idname = "bim.select_ifc_file"
|
||||
bl_label = "Select IFC File"
|
||||
|
||||
@@ -190,10 +190,11 @@ class Attribute(PropertyGroup):
|
||||
bool_value: BoolProperty(name="Value", update=updateAttributeValue)
|
||||
int_value: IntProperty(name="Value", update=updateAttributeValue)
|
||||
float_value: FloatProperty(name="Value", update=updateAttributeValue)
|
||||
is_null: BoolProperty(name="Is Null")
|
||||
is_optional: BoolProperty(name="Is Optional")
|
||||
enum_items: StringProperty(name="Value")
|
||||
enum_value: EnumProperty(items=getAttributeEnumValues, name="Value", update=updateAttributeValue)
|
||||
is_null: BoolProperty(name="Is Null")
|
||||
is_optional: BoolProperty(name="Is Optional")
|
||||
is_uri: BoolProperty(name="Is Uri", default=False)
|
||||
|
||||
def get_value(self):
|
||||
if self.is_null:
|
||||
|
||||
@@ -233,7 +233,7 @@ class BIM_PT_geometry(Panel):
|
||||
|
||||
|
||||
class BIM_PT_4D5D(Panel):
|
||||
bl_label = "IFC 4D/5D"
|
||||
bl_label = "IFC Costing and Scheduling"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@@ -17,16 +17,31 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
def load_information(document):
|
||||
document.import_information()
|
||||
document.enable_information_editing_ui()
|
||||
document.disable_editing_document()
|
||||
def load_project_documents(document):
|
||||
document.clear_document_tree()
|
||||
document.import_project_documents()
|
||||
document.clear_breadcrumbs()
|
||||
document.enable_editing_ui()
|
||||
|
||||
|
||||
def load_references(document):
|
||||
document.import_references()
|
||||
document.enable_reference_editing_ui()
|
||||
document.disable_editing_document()
|
||||
def load_document(document_tool, document=None):
|
||||
document_tool.clear_document_tree()
|
||||
document_tool.import_subdocuments(document)
|
||||
document_tool.import_references(document)
|
||||
document_tool.disable_editing_document()
|
||||
document_tool.add_breadcrumb(document)
|
||||
|
||||
|
||||
def load_parent_document(document):
|
||||
document.clear_document_tree()
|
||||
document.remove_latest_breadcrumb()
|
||||
parent = document.get_active_breadcrumb()
|
||||
if parent:
|
||||
document.import_subdocuments(parent)
|
||||
document.import_references(parent)
|
||||
document.disable_editing_document()
|
||||
else:
|
||||
document.import_project_documents()
|
||||
|
||||
|
||||
def disable_document_editing_ui(document):
|
||||
@@ -44,52 +59,53 @@ def disable_editing_document(document):
|
||||
|
||||
|
||||
def add_information(ifc, document):
|
||||
result = ifc.run("document.add_information")
|
||||
document.import_information()
|
||||
document.import_document_attributes(result)
|
||||
document.set_active_document(result)
|
||||
document.clear_document_tree()
|
||||
parent = document.get_active_breadcrumb()
|
||||
information = ifc.run("document.add_information", parent=parent)
|
||||
ifc.run("document.add_reference", information=information)
|
||||
if parent:
|
||||
document.import_subdocuments(parent)
|
||||
document.import_references(parent)
|
||||
else:
|
||||
document.import_project_documents()
|
||||
|
||||
|
||||
def add_reference(ifc, document):
|
||||
result = ifc.run("document.add_reference")
|
||||
document.import_references()
|
||||
document.import_document_attributes(result)
|
||||
document.set_active_document(result)
|
||||
parent = document.get_active_breadcrumb()
|
||||
ifc.run("document.add_reference", information=parent)
|
||||
document.clear_document_tree()
|
||||
document.import_subdocuments(parent)
|
||||
document.import_references(parent)
|
||||
|
||||
|
||||
def edit_information(ifc, document, information=None):
|
||||
attributes = document.export_document_attributes()
|
||||
ifc.run("document.edit_information", information=information, attributes=attributes)
|
||||
document.disable_editing_document()
|
||||
document.import_information()
|
||||
|
||||
|
||||
def edit_reference(ifc, document, reference=None):
|
||||
attributes = document.export_document_attributes()
|
||||
ifc.run("document.edit_reference", reference=reference, attributes=attributes)
|
||||
document.disable_editing_document()
|
||||
document.import_references()
|
||||
def edit_document(ifc, document_tool, document=None):
|
||||
attributes = document_tool.export_document_attributes()
|
||||
if document_tool.is_document_information(document):
|
||||
ifc.run("document.edit_information", information=document, attributes=attributes)
|
||||
else:
|
||||
ifc.run("document.edit_reference", reference=document, attributes=attributes)
|
||||
document_tool.disable_editing_document()
|
||||
document_tool.clear_document_tree()
|
||||
parent = document_tool.get_active_breadcrumb()
|
||||
if parent:
|
||||
document_tool.import_subdocuments(parent)
|
||||
document_tool.import_references(parent)
|
||||
else:
|
||||
document_tool.import_project_documents()
|
||||
|
||||
|
||||
def remove_document(ifc, document_tool, document=None):
|
||||
document_tool.clear_document_tree()
|
||||
if document_tool.is_document_information(document):
|
||||
ifc.run("document.remove_document", document=document)
|
||||
document_tool.import_information()
|
||||
ifc.run("document.remove_information", information=document)
|
||||
else:
|
||||
ifc.run("document.remove_document", document=document)
|
||||
document_tool.import_references()
|
||||
document_tool.disable_editing_document()
|
||||
|
||||
|
||||
def enable_assigning_document(document, obj=None):
|
||||
document.import_references()
|
||||
document.enable_reference_editing_ui()
|
||||
document.disable_editing_document()
|
||||
document.enable_document_assignment_ui(obj)
|
||||
|
||||
|
||||
def disable_assigning_document(document, obj=None):
|
||||
document.disable_document_assignment_ui(obj)
|
||||
ifc.run("document.remove_reference", reference=document)
|
||||
parent = document_tool.get_active_breadcrumb()
|
||||
if parent:
|
||||
document_tool.import_subdocuments(parent)
|
||||
document_tool.import_references(parent)
|
||||
else:
|
||||
document_tool.import_project_documents()
|
||||
|
||||
|
||||
def assign_document(ifc, product=None, document=None):
|
||||
|
||||
@@ -85,7 +85,7 @@ def open_sheet(drawing, sheet=None):
|
||||
|
||||
|
||||
def remove_sheet(ifc, drawing, sheet=None):
|
||||
ifc.run("document.remove_document", document=sheet)
|
||||
ifc.run("document.remove_information", information=sheet)
|
||||
drawing.import_sheets()
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ def disable_editing_drawings(drawing):
|
||||
|
||||
|
||||
def add_drawing(ifc, collector, drawing, target_view=None, location_hint=None):
|
||||
drawing_name = drawing.ensure_unique_drawing_name("UNTITLED")
|
||||
drawing_name = drawing.ensure_unique_drawing_name(drawing.generate_drawing_name(target_view, location_hint))
|
||||
drawing_matrix = drawing.generate_drawing_matrix(target_view, location_hint)
|
||||
camera = drawing.create_camera(drawing_name, drawing_matrix)
|
||||
element = drawing.run_root_assign_class(
|
||||
@@ -158,3 +158,48 @@ def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None)
|
||||
ifc.run("group.assign_group", group=drawing_tool.get_drawing_group(drawing), product=element)
|
||||
collector.assign(obj)
|
||||
drawing_tool.enable_editing(obj)
|
||||
|
||||
|
||||
def sync_references(ifc, collector, drawing_tool, drawing=None):
|
||||
context = drawing_tool.get_annotation_context(drawing_tool.get_drawing_target_view(drawing))
|
||||
if not context:
|
||||
return
|
||||
|
||||
group = drawing_tool.get_drawing_group(drawing)
|
||||
for reference_element in drawing_tool.get_potential_reference_elements(drawing):
|
||||
reference_obj = ifc.get_object(reference_element)
|
||||
annotation = drawing_tool.get_drawing_reference_annotation(drawing, reference_element)
|
||||
|
||||
should_delete_existing_annotation = False
|
||||
should_create_annotation = False
|
||||
|
||||
if annotation and (
|
||||
not reference_obj
|
||||
or ifc.is_moved(reference_obj)
|
||||
or ifc.is_edited(reference_obj)
|
||||
or ifc.is_deleted(reference_element)
|
||||
or ifc.is_deleted(annotation)
|
||||
):
|
||||
should_delete_existing_annotation = True
|
||||
|
||||
if reference_obj and (should_delete_existing_annotation or not annotation):
|
||||
should_create_annotation = True
|
||||
|
||||
if should_delete_existing_annotation:
|
||||
annotation_obj = ifc.get_object(annotation)
|
||||
if annotation_obj:
|
||||
drawing_tool.delete_object(annotation_obj)
|
||||
ifc.run("root.remove_product", product=annotation)
|
||||
|
||||
if should_create_annotation:
|
||||
annotation = drawing_tool.generate_reference_annotation(drawing, reference_element, context)
|
||||
if annotation:
|
||||
ifc.run("drawing.assign_product", relating_product=reference_element, related_object=annotation)
|
||||
ifc.run("group.assign_group", group=group, product=annotation)
|
||||
collector.assign(ifc.get_object(annotation))
|
||||
|
||||
if reference_obj and ifc.is_moved(reference_obj):
|
||||
drawing_tool.sync_object_placement(reference_obj)
|
||||
|
||||
if reference_obj and ifc.is_edited(reference_obj):
|
||||
drawing_tool.sync_object_representation(reference_obj)
|
||||
|
||||
@@ -21,7 +21,52 @@ def unlink_material(ifc, obj=None):
|
||||
ifc.unlink(obj=obj)
|
||||
|
||||
|
||||
def add_default_material(ifc, material):
|
||||
obj = material.add_default_material_object()
|
||||
ifc.link(ifc.run("material.add_material", name="Default"), obj)
|
||||
return obj
|
||||
def add_material(ifc, material, style, obj=None):
|
||||
if not obj:
|
||||
obj = material.add_default_material_object()
|
||||
ifc_material = ifc.run("material.add_material", name=material.get_name(obj))
|
||||
ifc.link(ifc_material, obj)
|
||||
ifc_style = style.get_style(obj)
|
||||
if ifc_style:
|
||||
context = style.get_context(obj)
|
||||
if context:
|
||||
ifc.run("style.assign_material_style", material=ifc_material, style=ifc_style, context=context)
|
||||
if material.is_editing_materials():
|
||||
material.import_material_definitions(material.get_active_material_type())
|
||||
return ifc_material
|
||||
|
||||
|
||||
def add_material_set(ifc, material, set_type=None):
|
||||
ifc_material = ifc.run("material.add_material_set", name="Unnamed", set_type=set_type)
|
||||
if material.is_editing_materials():
|
||||
material.import_material_definitions(material.get_active_material_type())
|
||||
return ifc_material
|
||||
|
||||
|
||||
def remove_material(ifc, material_tool, style, material=None):
|
||||
obj = ifc.get_object(material)
|
||||
ifc.unlink(element=material)
|
||||
ifc.run("material.remove_material", material=material)
|
||||
if obj and not style.get_style(obj):
|
||||
material_tool.delete_object(obj)
|
||||
if material_tool.is_editing_materials():
|
||||
material_tool.import_material_definitions(material_tool.get_active_material_type())
|
||||
|
||||
|
||||
def remove_material_set(ifc, material_tool, material=None):
|
||||
ifc.run("material.remove_material_set", material=material)
|
||||
if material_tool.is_editing_materials():
|
||||
material_tool.import_material_definitions(material_tool.get_active_material_type())
|
||||
|
||||
|
||||
def load_materials(material, material_type=None):
|
||||
material.import_material_definitions(material_type)
|
||||
material.enable_editing_materials()
|
||||
|
||||
|
||||
def disable_editing_materials(material):
|
||||
material.disable_editing_materials()
|
||||
|
||||
|
||||
def select_by_material(material_tool, material=None):
|
||||
material_tool.select_elements(material_tool.get_elements_by_material(material))
|
||||
|
||||
@@ -33,10 +33,14 @@ def add_style(ifc, style, obj=None):
|
||||
return element
|
||||
|
||||
|
||||
def remove_style(ifc, style, obj=None):
|
||||
element = style.get_style(obj)
|
||||
ifc.unlink(obj=obj, element=element)
|
||||
ifc.run("style.remove_style", style=element)
|
||||
def remove_style(ifc, material, style_tool, style=None):
|
||||
obj = ifc.get_object(style)
|
||||
ifc.unlink(obj=obj, element=style)
|
||||
ifc.run("style.remove_style", style=style)
|
||||
if obj and not ifc.get_entity(obj):
|
||||
material.delete_object(obj)
|
||||
if style_tool.is_editing_styles():
|
||||
style_tool.import_presentation_styles(style_tool.get_active_style_type())
|
||||
|
||||
|
||||
def update_style_colours(ifc, style, obj=None):
|
||||
@@ -97,3 +101,12 @@ def edit_style(ifc, style, obj=None):
|
||||
attributes = style.export_surface_attributes(obj)
|
||||
ifc.run("style.edit_presentation_style", style=style.get_style(obj), attributes=attributes)
|
||||
style.disable_editing(obj)
|
||||
|
||||
|
||||
def load_styles(style, style_type=None):
|
||||
style.import_presentation_styles(style_type)
|
||||
style.enable_editing_styles()
|
||||
|
||||
|
||||
def disable_editing_styles(style):
|
||||
style.disable_editing_styles()
|
||||
|
||||
@@ -133,6 +133,25 @@ class Debug:
|
||||
def purge_hdf5_cache(cls): pass
|
||||
|
||||
|
||||
@interface
|
||||
class Document:
|
||||
def add_breadcrumb(cls, document): pass
|
||||
def clear_breadcrumbs(cls): pass
|
||||
def clear_document_tree(cls): pass
|
||||
def disable_editing_document(cls): pass
|
||||
def disable_editing_ui(cls): pass
|
||||
def enable_editing_ui(cls): pass
|
||||
def export_document_attributes(cls): pass
|
||||
def get_active_breadcrumb(cls): pass
|
||||
def import_document_attributes(cls, document): pass
|
||||
def import_project_documents(cls): pass
|
||||
def import_references(cls, document): pass
|
||||
def import_subdocuments(cls, document): pass
|
||||
def is_document_information(cls, document): pass
|
||||
def remove_latest_breadcrumb(cls): pass
|
||||
def set_active_document(cls, document): pass
|
||||
|
||||
|
||||
@interface
|
||||
class Drawing:
|
||||
def create_annotation_object(cls, object_type): pass
|
||||
@@ -140,6 +159,7 @@ class Drawing:
|
||||
def create_svg_sheet(cls, document, titleblock): pass
|
||||
def delete_collection(cls, collection): pass
|
||||
def delete_drawing_elements(cls, elements): pass
|
||||
def delete_object(cls, obj): pass
|
||||
def disable_editing_drawings(cls): pass
|
||||
def disable_editing_sheets(cls): pass
|
||||
def disable_editing_text(cls, obj): pass
|
||||
@@ -153,6 +173,7 @@ class Drawing:
|
||||
def ensure_unique_identification(cls, identification): pass
|
||||
def export_text_literal_attributes(cls, obj): pass
|
||||
def generate_drawing_matrix(cls, target_view, location_hint): pass
|
||||
def generate_drawing_name(cls, target_view, location_hint): pass
|
||||
def generate_sheet_identification(cls): pass
|
||||
def get_annotation_context(cls, target_view): pass
|
||||
def get_body_context(cls): pass
|
||||
@@ -173,6 +194,7 @@ class Drawing:
|
||||
def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass
|
||||
def set_drawing_collection_name(cls, group, collection): pass
|
||||
def show_decorations(cls): pass
|
||||
def sync_object_placement(cls, obj): pass
|
||||
def update_text_value(cls, obj): pass
|
||||
|
||||
|
||||
@@ -227,6 +249,9 @@ class Ifc:
|
||||
def get_entity(cls, obj): pass
|
||||
def get_object(cls, entity): pass
|
||||
def get_schema(cls): pass
|
||||
def is_deleted(cls, element): pass
|
||||
def is_edited(cls, obj): pass
|
||||
def is_moved(cls, obj): pass
|
||||
def link(cls, element, obj): pass
|
||||
def run(cls, command, **kwargs): pass
|
||||
def set(cls, ifc): pass
|
||||
@@ -251,6 +276,15 @@ class Library:
|
||||
@interface
|
||||
class Material:
|
||||
def add_default_material_object(cls): pass
|
||||
def delete_object(cls, obj): pass
|
||||
def disable_editing_materials(cls): pass
|
||||
def enable_editing_materials(cls): pass
|
||||
def get_active_material_type(cls): pass
|
||||
def get_elements_by_material(cls, material): pass
|
||||
def get_name(cls, obj): pass
|
||||
def import_material_definitions(cls, material_type): pass
|
||||
def is_editing_materials(cls): pass
|
||||
def select_elements(cls, elements): pass
|
||||
|
||||
|
||||
@interface
|
||||
@@ -398,8 +432,11 @@ class Structural:
|
||||
class Style:
|
||||
def can_support_rendering_style(cls, obj): pass
|
||||
def disable_editing(cls, obj): pass
|
||||
def disable_editing_styles(cls): pass
|
||||
def enable_editing(cls, obj): pass
|
||||
def enable_editing_styles(cls): pass
|
||||
def export_surface_attributes(cls, obj): pass
|
||||
def get_active_style_type(cls): pass
|
||||
def get_context(cls, obj): pass
|
||||
def get_name(cls, obj): pass
|
||||
def get_style(cls, obj): pass
|
||||
@@ -409,7 +446,9 @@ class Style:
|
||||
def get_surface_shading_style(cls, obj): pass
|
||||
def get_surface_texture_style(cls, obj): pass
|
||||
def get_uv_maps(cls, representation): pass
|
||||
def import_presentation_styles(cls, style_type): pass
|
||||
def import_surface_attributes(cls, style, obj): pass
|
||||
def is_editing_styles(cls): pass
|
||||
|
||||
|
||||
@interface
|
||||
|
||||
@@ -32,7 +32,9 @@ def assign_scene_units(ifc, unit):
|
||||
areaunit = ifc.run("unit.add_conversion_based_unit", name=area_name)
|
||||
volumeunit = ifc.run("unit.add_conversion_based_unit", name=volume_name)
|
||||
|
||||
ifc.run("unit.assign_unit", units=[lengthunit, areaunit, volumeunit])
|
||||
planeangleunit = ifc.run("unit.add_conversion_based_unit", name="degree")
|
||||
|
||||
ifc.run("unit.assign_unit", units=[lengthunit, areaunit, volumeunit, planeangleunit])
|
||||
|
||||
|
||||
def assign_unit(ifc, unit_tool, unit=None):
|
||||
@@ -77,6 +79,12 @@ def add_context_dependent_unit(ifc, unit, unit_type=None, name=None):
|
||||
return result
|
||||
|
||||
|
||||
def add_conversion_based_unit(ifc, unit, name=None):
|
||||
result = ifc.run("unit.add_conversion_based_unit", name=name)
|
||||
unit.import_units()
|
||||
return result
|
||||
|
||||
|
||||
def enable_editing_unit(unit_tool, unit=None):
|
||||
unit_tool.set_active_unit(unit)
|
||||
unit_tool.import_unit_attributes(unit)
|
||||
|
||||
@@ -25,8 +25,20 @@ from blenderbim.bim import import_ifc
|
||||
|
||||
class Document(blenderbim.core.tool.Document):
|
||||
@classmethod
|
||||
def disable_document_assignment_ui(cls, obj):
|
||||
obj.BIMObjectDocumentProperties.is_adding = ""
|
||||
def add_breadcrumb(cls, document):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
new = props.breadcrumbs.add()
|
||||
new.name = str(document.id())
|
||||
|
||||
@classmethod
|
||||
def clear_breadcrumbs(cls):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
props.breadcrumbs.clear()
|
||||
|
||||
@classmethod
|
||||
def clear_document_tree(cls):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
props.documents.clear()
|
||||
|
||||
@classmethod
|
||||
def disable_editing_document(cls):
|
||||
@@ -34,24 +46,22 @@ class Document(blenderbim.core.tool.Document):
|
||||
|
||||
@classmethod
|
||||
def disable_editing_ui(cls):
|
||||
bpy.context.scene.BIMDocumentProperties.is_editing = ""
|
||||
bpy.context.scene.BIMDocumentProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def enable_document_assignment_ui(cls, obj):
|
||||
obj.BIMObjectDocumentProperties.is_adding = "IfcDocumentReference"
|
||||
|
||||
@classmethod
|
||||
def enable_information_editing_ui(cls):
|
||||
bpy.context.scene.BIMDocumentProperties.is_editing = "information"
|
||||
|
||||
@classmethod
|
||||
def enable_reference_editing_ui(cls):
|
||||
bpy.context.scene.BIMDocumentProperties.is_editing = "reference"
|
||||
def enable_editing_ui(cls):
|
||||
bpy.context.scene.BIMDocumentProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def export_document_attributes(cls):
|
||||
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMDocumentProperties.document_attributes)
|
||||
|
||||
@classmethod
|
||||
def get_active_breadcrumb(cls):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
if len(props.breadcrumbs):
|
||||
return tool.Ifc.get().by_id(int(props.breadcrumbs[-1].name))
|
||||
|
||||
@classmethod
|
||||
def import_document_attributes(cls, document):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
@@ -59,35 +69,64 @@ class Document(blenderbim.core.tool.Document):
|
||||
blenderbim.bim.helper.import_attributes2(document, props.document_attributes)
|
||||
|
||||
@classmethod
|
||||
def import_information(cls):
|
||||
def import_project_documents(cls):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
props.documents.clear()
|
||||
for element in tool.Ifc.get().by_type("IfcDocumentInformation"):
|
||||
new = props.documents.add()
|
||||
new.ifc_definition_id = element.id()
|
||||
new.name = element.Name or "Unnamed"
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
new.identification = element.DocumentId or "*"
|
||||
else:
|
||||
new.identification = element.Identification or "*"
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
for rel in project.HasAssociations or []:
|
||||
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument.is_a("IfcDocumentInformation"):
|
||||
element = rel.RelatingDocument
|
||||
new = props.documents.add()
|
||||
new.ifc_definition_id = element.id()
|
||||
new.name = element.Name or "Unnamed"
|
||||
new.is_information = True
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
new.identification = element.DocumentId or "*"
|
||||
else:
|
||||
new.identification = element.Identification or "*"
|
||||
|
||||
@classmethod
|
||||
def import_references(cls):
|
||||
def import_references(cls, document):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
props.documents.clear()
|
||||
for element in tool.Ifc.get().by_type("IfcDocumentReference"):
|
||||
new = props.documents.add()
|
||||
new.ifc_definition_id = element.id()
|
||||
new.name = element.Name or "Unnamed"
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
for element in document.DocumentReferences or []:
|
||||
new = props.documents.add()
|
||||
new.ifc_definition_id = element.id()
|
||||
new.name = element.Name or "Unnamed"
|
||||
new.identification = element.ItemReference or "*"
|
||||
else:
|
||||
new.is_information = False
|
||||
else:
|
||||
for element in document.HasDocumentReferences:
|
||||
new = props.documents.add()
|
||||
new.ifc_definition_id = element.id()
|
||||
new.name = element.Name or "Unnamed"
|
||||
new.identification = element.Identification or "*"
|
||||
new.is_information = False
|
||||
|
||||
@classmethod
|
||||
def import_subdocuments(cls, document):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
if document.IsPointer:
|
||||
for element in document.IsPointer[0].RelatedDocuments or []:
|
||||
new = props.documents.add()
|
||||
new.ifc_definition_id = element.id()
|
||||
new.name = element.Name or "Unnamed"
|
||||
new.is_information = True
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
new.identification = element.DocumentId or "*"
|
||||
else:
|
||||
new.identification = element.Identification or "*"
|
||||
|
||||
@classmethod
|
||||
def is_document_information(cls, document):
|
||||
return document.is_a("IfcDocumentInformation")
|
||||
|
||||
@classmethod
|
||||
def remove_latest_breadcrumb(cls):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
if len(props.breadcrumbs):
|
||||
props.breadcrumbs.remove(len(props.breadcrumbs) - 1)
|
||||
|
||||
@classmethod
|
||||
def set_active_document(cls, document):
|
||||
bpy.context.scene.BIMDocumentProperties.active_document_id = document.id()
|
||||
|
||||
@@ -19,13 +19,18 @@
|
||||
import os
|
||||
import re
|
||||
import bpy
|
||||
import math
|
||||
import bmesh
|
||||
import mathutils
|
||||
import webbrowser
|
||||
import numpy as np
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.bim.module.drawing.sheeter as sheeter
|
||||
import blenderbim.bim.module.drawing.annotation as annotation
|
||||
import blenderbim.bim.module.drawing.helper as helper
|
||||
|
||||
|
||||
class Drawing(blenderbim.core.tool.Drawing):
|
||||
@@ -83,6 +88,10 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
if obj:
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
@classmethod
|
||||
def delete_object(cls, obj):
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
@classmethod
|
||||
def disable_editing_drawings(cls):
|
||||
bpy.context.scene.DocProperties.is_editing_drawings = False
|
||||
@@ -260,19 +269,49 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
new = bpy.context.scene.DocProperties.drawings.add()
|
||||
new.ifc_definition_id = drawing.id()
|
||||
new.name = drawing.Name or "Unnamed"
|
||||
new.target_view = cls.get_drawing_target_view(drawing)
|
||||
|
||||
@classmethod
|
||||
def import_sheets(cls):
|
||||
bpy.context.scene.DocProperties.sheets.clear()
|
||||
props = bpy.context.scene.DocProperties
|
||||
expanded_sheets = {s.ifc_definition_id for s in props.sheets if s.is_expanded}
|
||||
props.sheets.clear()
|
||||
sheets = [d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "DOCUMENTATION"]
|
||||
for sheet in sheets:
|
||||
new = bpy.context.scene.DocProperties.sheets.add()
|
||||
new = props.sheets.add()
|
||||
new.ifc_definition_id = sheet.id()
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
new.identification = sheet.DocumentId
|
||||
else:
|
||||
new.identification = sheet.Identification
|
||||
new.name = sheet.Name
|
||||
new.is_sheet = True
|
||||
new.is_expanded = sheet.id() in expanded_sheets
|
||||
|
||||
if not new.is_expanded:
|
||||
continue
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
references = sheet.DocumentReferences or []
|
||||
else:
|
||||
references = sheet.HasDocumentReferences or []
|
||||
|
||||
for reference in references:
|
||||
new = props.sheets.add()
|
||||
new.ifc_definition_id = reference.id()
|
||||
new.is_sheet = False
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
new.identification = reference.ItemReference or "X"
|
||||
element = [
|
||||
r for r in tool.Ifc.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference
|
||||
][0].RelatedObjects[0]
|
||||
else:
|
||||
new.identification = reference.Identification or "X"
|
||||
element = reference.DocumentRefForObjects[0].RelatedObjects[0]
|
||||
|
||||
new.name = element.Name
|
||||
new.reference_type = "DRAWING"
|
||||
|
||||
@classmethod
|
||||
def import_text_attributes(cls, obj):
|
||||
@@ -349,3 +388,335 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
for variable in re.findall("{{.*?}}", value):
|
||||
value = value.replace(variable, selector.get_element_value(product, variable[2:-2]) or "")
|
||||
obj.BIMTextProperties.value = value
|
||||
|
||||
# TODO below this point is highly experimental prototype code with no tests
|
||||
|
||||
@classmethod
|
||||
def generate_drawing_name(cls, target_view, location_hint):
|
||||
if target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW") and location_hint:
|
||||
location = tool.Ifc.get().by_id(location_hint)
|
||||
if target_view == "REFLECTED_PLAN_VIEW":
|
||||
target_view = "RCP_VIEW"
|
||||
return (location.Name or "UNNAMED").upper() + " " + target_view.split("_")[0]
|
||||
elif target_view in ("SECTION_VIEW", "ELEVATION_VIEW") and location_hint:
|
||||
return location_hint + " " + target_view.split("_")[0]
|
||||
return target_view
|
||||
|
||||
@classmethod
|
||||
def get_potential_reference_elements(cls, drawing):
|
||||
elements = []
|
||||
existing_references = cls.get_group_elements(cls.get_drawing_group(drawing))
|
||||
for element in tool.Ifc.get().by_type("IfcAnnotation"):
|
||||
if element in existing_references or element == drawing:
|
||||
continue
|
||||
if element.ObjectType == "DRAWING":
|
||||
psets = ifcopenshell.util.element.get_psets(element)
|
||||
if psets.get("EPset_Drawing", {}).get("TargetView", None) in ("SECTION_VIEW", "ELEVATION_VIEW"):
|
||||
elements.append(element)
|
||||
for element in tool.Ifc.get().by_type("IfcGridAxis"):
|
||||
elements.append(element)
|
||||
return elements
|
||||
|
||||
@classmethod
|
||||
def get_drawing_reference_annotation(cls, drawing, reference_element):
|
||||
if drawing == reference_element:
|
||||
return True
|
||||
for element in cls.get_group_elements(cls.get_drawing_group(drawing)):
|
||||
if element.is_a("IfcAnnotation"):
|
||||
for rel in element.HasAssignments:
|
||||
if rel.is_a("IfcRelAssignsToProduct"):
|
||||
if rel.RelatingProduct == reference_element:
|
||||
return element
|
||||
# We cannot associate IfcGridAxis directly, so we establish a convention:
|
||||
# IfcRelAssignsToProduct.RelatingProduct = IfcGrid
|
||||
# IfcRelAssignsToProduct.Name = IfcGridAxis.AxisTag
|
||||
elif reference_element.is_a("IfcGridAxis") and rel.Name == reference_element.AxisTag:
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def generate_reference_annotation(cls, drawing, reference_element, context):
|
||||
if reference_element.is_a("IfcGridAxis"):
|
||||
return cls.generate_grid_axis_reference_annotation(drawing, reference_element, context)
|
||||
elif reference_element.is_a("IfcAnnotation") and reference_element.ObjectType == "DRAWING":
|
||||
psets = ifcopenshell.util.element.get_psets(reference_element)
|
||||
target_view = psets.get("EPset_Drawing", {}).get("TargetView", None)
|
||||
if target_view == "ELEVATION_VIEW":
|
||||
return cls.generate_elevation_reference_annotation(drawing, reference_element, context)
|
||||
elif target_view == "SECTION_VIEW":
|
||||
return cls.generate_section_reference_annotation(drawing, reference_element, context)
|
||||
|
||||
@classmethod
|
||||
def generate_section_reference_annotation(cls, drawing, reference_element, context):
|
||||
reference_obj = tool.Ifc.get_object(reference_element)
|
||||
reference_obj.matrix_world
|
||||
camera = tool.Ifc.get_object(drawing)
|
||||
bounds = helper.ortho_view_frame(camera.data) if camera.data.type == "ORTHO" else None
|
||||
|
||||
def to_camera_coords(camera, reference_obj):
|
||||
mat = reference_obj.matrix_world.copy()
|
||||
xyz = camera.matrix_world.inverted() @ reference_obj.matrix_world.translation
|
||||
xyz[2] = 0
|
||||
xyz = camera.matrix_world @ xyz
|
||||
mat[0][3] = xyz[0]
|
||||
mat[1][3] = xyz[1]
|
||||
mat[2][3] = xyz[2]
|
||||
annotation_offset = mathutils.Vector((0, 0, -camera.data.clip_start - 0.05))
|
||||
annotation_offset = camera.matrix_world.to_quaternion() @ annotation_offset
|
||||
mat[0][3] += annotation_offset[0]
|
||||
mat[1][3] += annotation_offset[1]
|
||||
mat[2][3] += annotation_offset[2]
|
||||
return mat
|
||||
|
||||
def clip_to_camera_boundary(mesh, bounds):
|
||||
mesh.verts.ensure_lookup_table()
|
||||
points = [v.co for v in mesh.verts[0:2]]
|
||||
points = helper.clip_segment(bounds, points)
|
||||
if points is None:
|
||||
return None
|
||||
mesh.verts[0].co = points[0]
|
||||
mesh.verts[1].co = points[1]
|
||||
return mesh
|
||||
|
||||
if cls.is_perpendicular(camera, reference_obj) and cls.is_intersecting(camera, reference_obj):
|
||||
reference_mesh = cls.get_camera_block(reference_obj)
|
||||
obj_matrix = to_camera_coords(camera, reference_obj)
|
||||
|
||||
# The reference mesh vertices represent a view cube. To convert this into a section line we:
|
||||
# 1. Select the 4 +Z vertices local to the reference element. This is the cutting plane.
|
||||
verts_local_to_reference = [reference_obj.matrix_world.inverted() @ v for v in reference_mesh["verts"]]
|
||||
cutting_plane_verts = sorted(verts_local_to_reference, key=lambda x: x.z)[-4:]
|
||||
global_cutting_plane_verts = [reference_obj.matrix_world @ v for v in cutting_plane_verts]
|
||||
# 2. Project the cutting plane onto our viewing camera.
|
||||
verts_local_to_camera = [camera.matrix_world.inverted() @ v for v in global_cutting_plane_verts]
|
||||
# 3. Collapse verts with the same XY coords, and set Z to be just below the clip_start so it's visible
|
||||
collapsed_verts = []
|
||||
for vert in verts_local_to_camera:
|
||||
if not [True for v in collapsed_verts if (vert.xy - v.xy).length < 1e-2]:
|
||||
collapsed_verts.append(mathutils.Vector((vert.x, vert.y, -camera.data.clip_start - 0.05)))
|
||||
# 4. The first two vertices is the section line
|
||||
section_line = collapsed_verts[0:2]
|
||||
global_section_line = [camera.matrix_world @ v for v in section_line]
|
||||
local_section_line = [obj_matrix.inverted() @ v for v in global_section_line]
|
||||
|
||||
mesh = bpy.data.meshes.new(name="Annotation")
|
||||
mesh.from_pydata(local_section_line, [(0, 1)], [])
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
bm = clip_to_camera_boundary(bm, bounds)
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
|
||||
obj = bpy.data.objects.new(reference_obj.name, mesh)
|
||||
obj.matrix_world = obj_matrix
|
||||
|
||||
element = cls.run_root_assign_class(
|
||||
obj=obj,
|
||||
ifc_class="IfcAnnotation",
|
||||
predefined_type="SECTION",
|
||||
should_add_representation=True,
|
||||
context=context,
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def generate_elevation_reference_annotation(cls, drawing, reference_element, context):
|
||||
reference_obj = tool.Ifc.get_object(reference_element)
|
||||
reference_obj.matrix_world
|
||||
camera = tool.Ifc.get_object(drawing)
|
||||
|
||||
def to_camera_coords(camera, reference_obj):
|
||||
mat = reference_obj.matrix_world.copy()
|
||||
xyz = camera.matrix_world.inverted() @ reference_obj.matrix_world.translation
|
||||
xyz[2] = 0
|
||||
xyz = camera.matrix_world @ xyz
|
||||
mat[0][3] = xyz[0]
|
||||
mat[1][3] = xyz[1]
|
||||
mat[2][3] = xyz[2]
|
||||
annotation_offset = mathutils.Vector((0, 0, -camera.data.clip_start - 0.05))
|
||||
annotation_offset = camera.matrix_world.to_quaternion() @ annotation_offset
|
||||
mat[0][3] += annotation_offset[0]
|
||||
mat[1][3] += annotation_offset[1]
|
||||
mat[2][3] += annotation_offset[2]
|
||||
return mat
|
||||
|
||||
if cls.is_perpendicular(camera, reference_obj) and cls.is_intersecting(camera, reference_obj):
|
||||
obj = bpy.data.objects.new(reference_obj.name, None)
|
||||
obj.empty_display_size = 0.1
|
||||
obj.matrix_world = to_camera_coords(camera, reference_obj)
|
||||
|
||||
element = cls.run_root_assign_class(
|
||||
obj=obj,
|
||||
ifc_class="IfcAnnotation",
|
||||
predefined_type="ELEVATION",
|
||||
should_add_representation=False,
|
||||
context=context,
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def generate_grid_axis_reference_annotation(cls, drawing, reference_element, context):
|
||||
target_view = tool.Drawing.get_drawing_target_view(drawing)
|
||||
|
||||
camera = tool.Ifc.get_object(drawing)
|
||||
|
||||
is_ortho = camera.data.type == "ORTHO"
|
||||
bounds = helper.ortho_view_frame(camera.data) if is_ortho else None
|
||||
clipping = is_ortho and target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW")
|
||||
elevating = is_ortho and target_view in ("ELEVATION_VIEW", "SECTION_VIEW")
|
||||
|
||||
def clone(src):
|
||||
dst = src.copy()
|
||||
dst.data = dst.data.copy()
|
||||
dst.name = dst.name.replace("IfcGridAxis/", "")
|
||||
dst.BIMObjectProperties.ifc_definition_id = 0
|
||||
dst.data.BIMMeshProperties.ifc_definition_id = 0
|
||||
return dst
|
||||
|
||||
def disassemble(obj):
|
||||
mesh = bmesh.new()
|
||||
mesh.verts.ensure_lookup_table()
|
||||
mesh.from_mesh(obj.data)
|
||||
return obj, mesh
|
||||
|
||||
def assemble(obj, mesh):
|
||||
mesh.to_mesh(obj.data)
|
||||
return obj
|
||||
|
||||
def to_camera_coords(obj, mesh):
|
||||
mesh.transform(camera.matrix_world.inverted() @ obj.matrix_world)
|
||||
obj.matrix_world = camera.matrix_world
|
||||
annotation_offset = mathutils.Vector((0, 0, -camera.data.clip_start - 0.05))
|
||||
annotation_offset = camera.matrix_world.to_quaternion() @ annotation_offset
|
||||
obj.matrix_world[0][3] += annotation_offset[0]
|
||||
obj.matrix_world[1][3] += annotation_offset[1]
|
||||
obj.matrix_world[2][3] += annotation_offset[2]
|
||||
return obj, mesh
|
||||
|
||||
def clip_to_camera_boundary(mesh):
|
||||
mesh.verts.ensure_lookup_table()
|
||||
points = [v.co for v in mesh.verts[0:2]]
|
||||
points = helper.clip_segment(bounds, points)
|
||||
if points is None:
|
||||
return None
|
||||
mesh.verts[0].co = points[0]
|
||||
mesh.verts[1].co = points[1]
|
||||
return mesh
|
||||
|
||||
def draw_grids_vertically(mesh):
|
||||
mesh.verts.ensure_lookup_table()
|
||||
points = [v.co for v in mesh.verts[0:2]]
|
||||
points = helper.elevate_segment(bounds, points)
|
||||
if points is None:
|
||||
return None
|
||||
points = helper.clip_segment(bounds, points)
|
||||
if points is None:
|
||||
return None
|
||||
mesh.verts[0].co = points[0]
|
||||
mesh.verts[1].co = points[1]
|
||||
return mesh
|
||||
|
||||
obj = tool.Ifc.get_object(reference_element)
|
||||
if not obj:
|
||||
return
|
||||
obj, mesh = to_camera_coords(*disassemble(clone(obj)))
|
||||
|
||||
if clipping:
|
||||
mesh = clip_to_camera_boundary(mesh)
|
||||
elif elevating:
|
||||
mesh = draw_grids_vertically(mesh)
|
||||
|
||||
if mesh is None:
|
||||
return
|
||||
|
||||
assemble(obj, mesh)
|
||||
|
||||
element = cls.run_root_assign_class(
|
||||
obj=obj,
|
||||
ifc_class="IfcAnnotation",
|
||||
predefined_type="GRID",
|
||||
should_add_representation=True,
|
||||
context=context,
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def is_perpendicular(cls, a, b):
|
||||
axes = [mathutils.Vector((1, 0, 0)), mathutils.Vector((0, 1, 0)), mathutils.Vector((0, 0, 1))]
|
||||
a_quaternion = a.matrix_world.to_quaternion()
|
||||
b_quaternion = b.matrix_world.to_quaternion()
|
||||
for axis in axes:
|
||||
if abs((a_quaternion @ axis).angle((b_quaternion @ axis)) - (math.pi / 2)) < 1e-5:
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get_camera_block(cls, obj):
|
||||
raster_x = obj.data.BIMCameraProperties.raster_x
|
||||
raster_y = obj.data.BIMCameraProperties.raster_y
|
||||
|
||||
if raster_x > raster_y:
|
||||
width = obj.data.ortho_scale
|
||||
height = width / raster_x * raster_y
|
||||
else:
|
||||
height = obj.data.ortho_scale
|
||||
width = height / raster_y * raster_x
|
||||
depth = obj.data.clip_end
|
||||
|
||||
verts = (
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, -height / 2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, -height / 2, 0)),
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, height / 2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, height / 2, 0)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, -height / 2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, -height / 2, 0)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, height / 2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, height / 2, 0)),
|
||||
)
|
||||
faces = [
|
||||
[0, 1, 3, 2],
|
||||
[2, 3, 7, 6],
|
||||
[6, 7, 5, 4],
|
||||
[4, 5, 1, 0],
|
||||
[2, 6, 4, 0],
|
||||
[7, 3, 1, 5],
|
||||
]
|
||||
return {"verts": verts, "faces": faces}
|
||||
|
||||
@classmethod
|
||||
def is_intersecting(cls, a, b):
|
||||
a_block = cls.get_camera_block(a)
|
||||
a_tree = mathutils.bvhtree.BVHTree.FromPolygons(a_block["verts"], a_block["faces"])
|
||||
b_block = cls.get_camera_block(b)
|
||||
b_tree = mathutils.bvhtree.BVHTree.FromPolygons(b_block["verts"], b_block["faces"])
|
||||
return bool(a_tree.overlap(b_tree))
|
||||
|
||||
@classmethod
|
||||
def sync_object_representation(cls, obj):
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
@classmethod
|
||||
def sync_object_placement(cls, obj):
|
||||
blender_matrix = np.array(obj.matrix_world)
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if (obj.scale - mathutils.Vector((1.0, 1.0, 1.0))).length > 1e-4:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
return element
|
||||
if element.is_a("IfcGridAxis"):
|
||||
return cls.sync_grid_axis_object_placement(obj, element)
|
||||
if not hasattr(element, "ObjectPlacement"):
|
||||
return
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def sync_grid_axis_object_placement(cls, obj, element):
|
||||
grid = (element.PartOfU or element.PartOfV or element.PartOfW)[0]
|
||||
grid_obj = tool.Ifc.get_object(grid)
|
||||
if grid_obj:
|
||||
cls.sync_object_placement(grid_obj)
|
||||
if grid_obj.matrix_world != obj.matrix_world:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
tool.Geometry.record_object_position(obj)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import numpy as np
|
||||
import ifcopenshell.api
|
||||
import blenderbim.core.tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -44,6 +45,26 @@ class Ifc(blenderbim.core.tool.Ifc):
|
||||
if IfcStore.get_file():
|
||||
return IfcStore.get_file().schema
|
||||
|
||||
@classmethod
|
||||
def is_deleted(cls, element):
|
||||
return element.id() in IfcStore.deleted_ids
|
||||
|
||||
@classmethod
|
||||
def is_edited(cls, obj):
|
||||
return list(obj.scale) != [1.0, 1.0, 1.0] or obj in IfcStore.edited_objs
|
||||
|
||||
@classmethod
|
||||
def is_moved(cls, obj):
|
||||
if not obj.BIMObjectProperties.location_checksum:
|
||||
return True # Let's be conservative
|
||||
loc_check = np.frombuffer(eval(obj.BIMObjectProperties.location_checksum))
|
||||
rot_check = np.frombuffer(eval(obj.BIMObjectProperties.rotation_checksum))
|
||||
loc_real = np.array(obj.matrix_world.translation).flatten()
|
||||
rot_real = np.array(obj.matrix_world.to_3x3()).flatten()
|
||||
if np.allclose(loc_check, loc_real, atol=1e-4) and np.allclose(rot_check, rot_real, atol=1e-2):
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def schema(cls):
|
||||
return IfcStore.get_schema()
|
||||
|
||||
@@ -27,3 +27,53 @@ class Material(blenderbim.core.tool.Material):
|
||||
@classmethod
|
||||
def add_default_material_object(cls):
|
||||
return bpy.data.materials.new("Default")
|
||||
|
||||
@classmethod
|
||||
def delete_object(cls, obj):
|
||||
bpy.data.materials.remove(obj)
|
||||
|
||||
@classmethod
|
||||
def disable_editing_materials(cls):
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def enable_editing_materials(cls):
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def get_active_material_type(cls):
|
||||
return bpy.context.scene.BIMMaterialProperties.material_type
|
||||
|
||||
@classmethod
|
||||
def get_elements_by_material(cls, material):
|
||||
return set(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material))
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, obj):
|
||||
return obj.name
|
||||
|
||||
@classmethod
|
||||
def import_material_definitions(cls, material_type):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.materials.clear()
|
||||
for material in tool.Ifc.get().by_type(material_type):
|
||||
new = props.materials.add()
|
||||
new.ifc_definition_id = material.id()
|
||||
if material.is_a("IfcMaterialLayerSet"):
|
||||
new.name = material.LayerSetName or "Unnamed"
|
||||
elif material.is_a("IfcMaterialList"):
|
||||
new.name = "Unnamed"
|
||||
else:
|
||||
new.name = material.Name or "Unnamed"
|
||||
new.total_elements = len(set(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)))
|
||||
|
||||
@classmethod
|
||||
def is_editing_materials(cls):
|
||||
return bpy.context.scene.BIMMaterialProperties.is_editing
|
||||
|
||||
@classmethod
|
||||
def select_elements(cls, elements):
|
||||
for element in elements:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
@@ -31,14 +32,26 @@ class Style(blenderbim.core.tool.Style):
|
||||
def disable_editing(cls, obj):
|
||||
obj.BIMStyleProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_styles(cls):
|
||||
bpy.context.scene.BIMStylesProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def enable_editing(cls, obj):
|
||||
obj.BIMStyleProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_styles(cls):
|
||||
bpy.context.scene.BIMStylesProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def export_surface_attributes(cls, obj):
|
||||
return blenderbim.bim.helper.export_attributes(obj.BIMStyleProperties.attributes)
|
||||
|
||||
@classmethod
|
||||
def get_active_style_type(cls):
|
||||
return bpy.context.scene.BIMStylesProperties.style_type
|
||||
|
||||
@classmethod
|
||||
def get_context(cls, obj):
|
||||
return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
@@ -166,7 +179,20 @@ class Style(blenderbim.core.tool.Style):
|
||||
results.append(uv_map)
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def import_presentation_styles(cls, style_type):
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
props.styles.clear()
|
||||
for style in tool.Ifc.get().by_type(style_type):
|
||||
new = props.styles.add()
|
||||
new.ifc_definition_id = style.id()
|
||||
new.name = style.Name or "Unnamed"
|
||||
|
||||
@classmethod
|
||||
def import_surface_attributes(cls, style, obj):
|
||||
obj.BIMStyleProperties.attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(style, obj.BIMStyleProperties.attributes)
|
||||
|
||||
@classmethod
|
||||
def is_editing_styles(cls):
|
||||
return bpy.context.scene.BIMStylesProperties.is_editing
|
||||
|
||||
@@ -63,7 +63,7 @@ architecture flow charts and diagrams. The code and its comments will guide you
|
||||
through the process.
|
||||
|
||||
Before playing with the demo module, you may want to switch to using a source
|
||||
installation. See :ref:`blenderbim/installation` for details.
|
||||
installation. See `blenderbim/installation <./installation.rst>`_ for details.
|
||||
|
||||
To see the demo module in action, you'll need to enable it. In
|
||||
``src/blenderbim/blenderbim/bim/__init__.py``, uncomment the line for the demo
|
||||
@@ -95,8 +95,8 @@ and how to test and structure it so that you can build incredibly complex
|
||||
features in a maintainable way.
|
||||
|
||||
Tests for quality checking also exist. The system is designed so that you can
|
||||
do "Test Driven Development". For reference on how to run these tests, see
|
||||
:ref:`blenderbim/running_tests` for details. You can find the tests here:
|
||||
do "Test Driven Development". For reference on how to run these tests, see `blenderbim/running_tests <./running_tests.rst>`_
|
||||
for details. You can find the tests here:
|
||||
|
||||
::
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ markers =
|
||||
context
|
||||
debug
|
||||
demo
|
||||
document
|
||||
drawing
|
||||
geometry
|
||||
library
|
||||
|
||||
@@ -49,7 +49,7 @@ Scenario: Add aggregate
|
||||
When I press "bim.add_aggregate"
|
||||
Then the object "IfcWall/Cube" is in the collection "IfcElementAssembly/Assembly"
|
||||
And the object "IfcElementAssembly/Assembly" is in the collection "IfcElementAssembly/Assembly"
|
||||
And the collection "IfcElementAssembly/Assembly" is in the collection "IfcProject/My Project"
|
||||
And the collection "IfcElementAssembly/Assembly" is in the collection "IfcBuildingStorey/My Storey"
|
||||
|
||||
Scenario: Add aggregate - with the aggregate inheriting the existing spatial collection
|
||||
Given an empty IFC project
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
@document
|
||||
Feature: Document
|
||||
|
||||
Scenario: Load project documents
|
||||
Given an empty IFC project
|
||||
When I press "bim.load_project_documents"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Load document
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_project_documents"
|
||||
And I press "bim.add_information"
|
||||
And the variable "information" is "{ifc}.by_type('IfcDocumentInformation')[-1].id()"
|
||||
When I press "bim.load_document(document={information})"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Load parent document
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_project_documents"
|
||||
And I press "bim.add_information"
|
||||
And the variable "information" is "{ifc}.by_type('IfcDocumentInformation')[-1].id()"
|
||||
And I press "bim.load_document(document={information})"
|
||||
When I press "bim.load_parent_document"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Disable document editing UI
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_project_documents"
|
||||
When I press "bim.disable_document_editing_ui"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Enable editing document
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_project_documents"
|
||||
And I press "bim.add_information"
|
||||
And the variable "information" is "{ifc}.by_type('IfcDocumentInformation')[-1].id()"
|
||||
When I press "bim.enable_editing_document(document={information})"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Disable editing document
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_project_documents"
|
||||
And I press "bim.add_information"
|
||||
And the variable "information" is "{ifc}.by_type('IfcDocumentInformation')[-1].id()"
|
||||
And I press "bim.enable_editing_document(document={information})"
|
||||
When I press "bim.disable_editing_document"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Add information
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_project_documents"
|
||||
When I press "bim.add_information"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Add document reference
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_project_documents"
|
||||
And I press "bim.add_information"
|
||||
And the variable "information" is "{ifc}.by_type('IfcDocumentInformation')[-1].id()"
|
||||
And I press "bim.load_document(document={information})"
|
||||
When I press "bim.add_document_reference"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Edit document
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_project_documents"
|
||||
And I press "bim.add_information"
|
||||
And the variable "information" is "{ifc}.by_type('IfcDocumentInformation')[-1].id()"
|
||||
And I press "bim.enable_editing_document(document={information})"
|
||||
When I press "bim.edit_document"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Remove document
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_project_documents"
|
||||
And I press "bim.add_information"
|
||||
And the variable "information" is "{ifc}.by_type('IfcDocumentInformation')[-1].id()"
|
||||
When I press "bim.remove_document(document={information})"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Assign document
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_project_documents"
|
||||
And I press "bim.add_information"
|
||||
And the variable "information" is "{ifc}.by_type('IfcDocumentInformation')[-1].id()"
|
||||
And I press "bim.load_document(document={information})"
|
||||
And I press "bim.add_document_reference"
|
||||
And the variable "reference" is "{ifc}.by_type('IfcDocumentReference')[-1].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
When I press "bim.assign_document(document={reference})"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Unassign document
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_project_documents"
|
||||
And I press "bim.add_information"
|
||||
And the variable "information" is "{ifc}.by_type('IfcDocumentInformation')[-1].id()"
|
||||
And I press "bim.load_document(document={information})"
|
||||
And I press "bim.add_document_reference"
|
||||
And the variable "reference" is "{ifc}.by_type('IfcDocumentReference')[-1].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.assign_document(document={reference})"
|
||||
When I press "bim.unassign_document(document={reference})"
|
||||
Then nothing happens
|
||||
@@ -19,7 +19,7 @@ Scenario: Add representation
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
Then the object "IfcWall/Cube" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||
When the variable "context" is "{ifc}.by_type('IfcGeometricRepresentationSubContext')[-1].id()"
|
||||
When the variable "context" is "[c for c in {ifc}.by_type('IfcGeometricRepresentationSubContext') if c.TargetView == 'PLAN_VIEW'][0].id()"
|
||||
And I set "scene.BIMRootProperties.contexts" to "{context}"
|
||||
And I press "bim.add_representation"
|
||||
Then the object "IfcWall/Cube" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
@@ -36,7 +36,7 @@ Scenario: Add representation - add a new representation to a typed instance
|
||||
Then the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||
And the object "IfcWall/Wall.001" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||
When the object "IfcWall/Wall" is selected
|
||||
And the variable "context" is "{ifc}.by_type('IfcGeometricRepresentationSubContext')[-1].id()"
|
||||
And the variable "context" is "[c for c in {ifc}.by_type('IfcGeometricRepresentationSubContext') if c.TargetView == 'PLAN_VIEW'][0].id()"
|
||||
And I set "scene.BIMRootProperties.contexts" to "{context}"
|
||||
And I press "bim.add_representation"
|
||||
Then the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
@@ -195,7 +195,7 @@ Scenario: Update representation - updating a layered extrusion
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_default_material"
|
||||
And I press "bim.add_material(obj='')"
|
||||
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
|
||||
And I press "bim.assign_material"
|
||||
And I press "bim.enable_editing_assigned_material"
|
||||
@@ -222,7 +222,7 @@ Scenario: Update representation - updating a profiled extrusion
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_default_material"
|
||||
And I press "bim.add_material(obj='')"
|
||||
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
|
||||
And I press "bim.assign_material"
|
||||
And I press "bim.enable_editing_assigned_material"
|
||||
@@ -357,7 +357,7 @@ Scenario: Override duplicate move - copying a layered extrusion
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_default_material"
|
||||
And I press "bim.add_material(obj='')"
|
||||
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
|
||||
And I press "bim.assign_material"
|
||||
And I press "bim.enable_editing_assigned_material"
|
||||
@@ -386,7 +386,7 @@ Scenario: Override duplicate move - copying a profiled extrusion
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_default_material"
|
||||
And I press "bim.add_material(obj='')"
|
||||
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
|
||||
And I press "bim.assign_material"
|
||||
And I press "bim.enable_editing_assigned_material"
|
||||
|
||||
@@ -1,9 +1,35 @@
|
||||
@material
|
||||
Feature: Material
|
||||
|
||||
Scenario: Load materials
|
||||
Given an empty IFC project
|
||||
And I press "bim.add_material(obj='')"
|
||||
When I press "bim.load_materials"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Disable editing materials
|
||||
Given an empty IFC project
|
||||
And I press "bim.add_material(obj='')"
|
||||
And I press "bim.load_materials"
|
||||
When I press "bim.disable_editing_materials"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Load materials - then add material
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_materials"
|
||||
When I press "bim.add_material(obj='')"
|
||||
Then the material "Default" exists
|
||||
|
||||
Scenario: Load materials - then add material set
|
||||
Given an empty IFC project
|
||||
And I set "scene.BIMMaterialProperties.material_type" to "IfcMaterialLayerSet"
|
||||
And I press "bim.load_materials"
|
||||
When I press "bim.add_material_set(set_type='IfcMaterialLayerSet')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Add default material
|
||||
Given an empty IFC project
|
||||
When I press "bim.add_default_material"
|
||||
When I press "bim.add_material(obj='')"
|
||||
Then the material "Default" exists
|
||||
|
||||
Scenario: Add material
|
||||
@@ -11,7 +37,7 @@ Scenario: Add material
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I add a material
|
||||
When I press "bim.add_material"
|
||||
When I press "bim.add_material(obj='Material')"
|
||||
Then the material "Material" is an IFC material
|
||||
|
||||
Scenario: Remove material
|
||||
@@ -19,16 +45,26 @@ Scenario: Remove material
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I add a material
|
||||
And I press "bim.add_material"
|
||||
When I press "bim.remove_material"
|
||||
Then the material "Material" is not an IFC material
|
||||
And I press "bim.add_material(obj='Material')"
|
||||
And the variable "material" is "{ifc}.by_type('IfcMaterial')[0].id()"
|
||||
When I press "bim.remove_material(material={material})"
|
||||
Then the material "Material" does not exist
|
||||
|
||||
Scenario: Remove material set
|
||||
Given an empty IFC project
|
||||
And I set "scene.BIMMaterialProperties.material_type" to "IfcMaterialLayerSet"
|
||||
And I press "bim.load_materials"
|
||||
And I press "bim.add_material_set(set_type='IfcMaterialLayerSet')"
|
||||
And the variable "material" is "{ifc}.by_type('IfcMaterialLayerSet')[0].id()"
|
||||
When I press "bim.remove_material_set(material={material})"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Unlink material
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I add a material
|
||||
And I press "bim.add_material"
|
||||
And I press "bim.add_material(obj='Material')"
|
||||
When I press "bim.unlink_material"
|
||||
Then the material "Material" is not an IFC material
|
||||
|
||||
@@ -38,7 +74,7 @@ Scenario: Assign material - Assign a material
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_default_material"
|
||||
And I press "bim.add_material(obj='')"
|
||||
When I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterial"
|
||||
And I press "bim.assign_material"
|
||||
Then the object "IfcWall/Cube" has the material "Default"
|
||||
@@ -54,7 +90,7 @@ Scenario: Assign material - Assign a material layer set
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_default_material"
|
||||
And I press "bim.add_material(obj='')"
|
||||
When I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
|
||||
And I press "bim.assign_material"
|
||||
Then nothing happens
|
||||
@@ -70,7 +106,15 @@ Scenario: Assign material - Assign a material profile set
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_default_material"
|
||||
And I press "bim.add_material(obj='')"
|
||||
When I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
|
||||
And I press "bim.assign_material"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Select by material
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_materials"
|
||||
And I press "bim.add_material(obj='')"
|
||||
And the variable "material" is "{ifc}.by_type('IfcMaterial')[0].id()"
|
||||
When I press "bim.select_by_material(material={material})"
|
||||
Then nothing happens
|
||||
|
||||
@@ -40,7 +40,7 @@ Scenario: Add type instance - add a mesh where existing instances have changed c
|
||||
And I press "bim.add_type_instance"
|
||||
And the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||
And the object "IfcWall/Wall" is selected
|
||||
And the variable "context" is "{ifc}.by_type('IfcGeometricRepresentationSubContext')[-1].id()"
|
||||
And the variable "context" is "[c for c in {ifc}.by_type('IfcGeometricRepresentationSubContext') if c.TargetView == 'PLAN_VIEW'][0].id()"
|
||||
And I set "scene.BIMRootProperties.contexts" to "{context}"
|
||||
And I press "bim.add_representation"
|
||||
And the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
|
||||
@@ -43,7 +43,7 @@ Scenario: Assign a class to a cube
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
Then the object "IfcWall/Cube" is an "IfcWall"
|
||||
And the object "IfcWall/Cube" is in the collection "IfcProject/My Project"
|
||||
And the object "IfcWall/Cube" is in the collection "IfcBuildingStorey/My Storey"
|
||||
And the object "IfcWall/Cube" has a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||
|
||||
Scenario: Assign a type class to a cube
|
||||
|
||||
@@ -79,7 +79,7 @@ Scenario: Animate the construction of a wall
|
||||
Then "scene.objects.get('IfcWall/Cube').hide_viewport" is "False"
|
||||
And "scene.objects.get('IfcWall/Cube').hide_render" is "False"
|
||||
And "scene.objects.get('IfcWall/Cube').color" is "[0.0, 1.0, 0.0, 1]"
|
||||
When I am on frame "6"
|
||||
When I am on frame "7"
|
||||
Then "scene.objects.get('IfcWall/Cube').color" is "[1.0, 1.0, 1.0, 1]"
|
||||
|
||||
Scenario: Animate the demolition of a wall
|
||||
@@ -116,7 +116,7 @@ Scenario: Animate the demolition of a wall
|
||||
Then "scene.objects.get('IfcWall/Cube').hide_viewport" is "False"
|
||||
And "scene.objects.get('IfcWall/Cube').hide_render" is "False"
|
||||
And "scene.objects.get('IfcWall/Cube').color" is "[1.0, 0.0, 0.0, 1]"
|
||||
When I am on frame "6"
|
||||
When I am on frame "7"
|
||||
Then "scene.objects.get('IfcWall/Cube').hide_viewport" is "True"
|
||||
And "scene.objects.get('IfcWall/Cube').hide_render" is "True"
|
||||
And "scene.objects.get('IfcWall/Cube').color" is "[0.0, 0.0, 0.0, 1]"
|
||||
@@ -151,7 +151,7 @@ Scenario: Animate the operation of a wall
|
||||
Then "scene.objects.get('IfcWall/Cube').color" is "[1.0, 1.0, 1.0, 1]"
|
||||
When I am on frame "2"
|
||||
Then "scene.objects.get('IfcWall/Cube').color" is "[0.0, 0.0, 1.0, 1]"
|
||||
When I am on frame "6"
|
||||
When I am on frame "7"
|
||||
Then "scene.objects.get('IfcWall/Cube').color" is "[1.0, 1.0, 1.0, 1]"
|
||||
|
||||
Scenario: Animate the movement of a wall
|
||||
@@ -200,7 +200,7 @@ Scenario: Animate the movement of a wall
|
||||
And "scene.objects.get('IfcWall/ToObject').color" is "[1.0, 1.0, 0.0, 1]"
|
||||
And "scene.objects.get('IfcWall/ToObject').hide_viewport" is "False"
|
||||
And "scene.objects.get('IfcWall/ToObject').hide_render" is "False"
|
||||
When I am on frame "6"
|
||||
When I am on frame "7"
|
||||
Then "scene.objects.get('IfcWall/FromObject').color" is "[0.0, 0.0, 0.0, 1]"
|
||||
Then "scene.objects.get('IfcWall/FromObject').hide_viewport" is "True"
|
||||
Then "scene.objects.get('IfcWall/FromObject').hide_render" is "True"
|
||||
@@ -239,7 +239,7 @@ Scenario: Animate the consumption of a wall
|
||||
Then "scene.objects.get('IfcWall/Cube').color" is "[0.0, 1.0, 1.0, 1]"
|
||||
And "scene.objects.get('IfcWall/Cube').hide_viewport" is "False"
|
||||
And "scene.objects.get('IfcWall/Cube').hide_render" is "False"
|
||||
When I am on frame "6"
|
||||
When I am on frame "7"
|
||||
Then "scene.objects.get('IfcWall/Cube').color" is "[0.0, 0.0, 0.0, 1]"
|
||||
Then "scene.objects.get('IfcWall/Cube').hide_viewport" is "True"
|
||||
Then "scene.objects.get('IfcWall/Cube').hide_render" is "True"
|
||||
|
||||
@@ -14,7 +14,8 @@ Scenario: Remove style
|
||||
And I add a cube
|
||||
And I add a material
|
||||
And I press "bim.add_style"
|
||||
When I press "bim.remove_style"
|
||||
And the variable "style" is "{ifc}.by_type('IfcSurfaceStyle')[0].id()"
|
||||
When I press "bim.remove_style(style={style})"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Add style
|
||||
@@ -57,3 +58,20 @@ Scenario: Edit style
|
||||
And I press "bim.enable_editing_style"
|
||||
When I press "bim.edit_style"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Load styles
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And I add a material
|
||||
And I press "bim.add_style"
|
||||
When I press "bim.load_styles(style_type='IfcSurfaceStyle')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Disable editing styles
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And I add a material
|
||||
And I press "bim.add_style"
|
||||
And I press "bim.load_styles(style_type='IfcSurfaceStyle')"
|
||||
When I press "bim.disable_editing_style"
|
||||
Then nothing happens
|
||||
|
||||
@@ -42,7 +42,7 @@ Scenario: Assign type - assign to a type with a material layer set
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_default_material"
|
||||
And I press "bim.add_material(obj='')"
|
||||
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
|
||||
And I press "bim.assign_material"
|
||||
And I press "bim.enable_editing_assigned_material"
|
||||
@@ -68,7 +68,7 @@ Scenario: Assign type - assign to a type with a material profile set
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_default_material"
|
||||
And I press "bim.add_material(obj='')"
|
||||
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
|
||||
And I press "bim.assign_material"
|
||||
And I press "bim.enable_editing_assigned_material"
|
||||
|
||||
@@ -50,6 +50,16 @@ Scenario: Add context dependent unit
|
||||
When I press "bim.add_context_dependent_unit(name='THINGAMAJIGS', unit_type='LENGTHUNIT')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Add conversion based unit
|
||||
Given an empty IFC project
|
||||
When I press "bim.add_conversion_based_unit(name='inch')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Add conversion based unit with offset
|
||||
Given an empty IFC project
|
||||
When I press "bim.add_conversion_based_unit(name='fahrenheit')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Enable editing unit
|
||||
Given an empty IFC project
|
||||
And the variable "unit" is "{ifc}.by_type('IfcSIUnit')[0].id()"
|
||||
|
||||
@@ -297,6 +297,11 @@ def the_material_name_exists(name) -> bpy.types.Material:
|
||||
return obj
|
||||
|
||||
|
||||
@then(parsers.parse('the material "{name}" does not exist'))
|
||||
def the_material_name_does_not_exist(name):
|
||||
assert bpy.data.materials.get(name) is None, "Material exists"
|
||||
|
||||
|
||||
@then("an IFC file does not exist")
|
||||
def an_ifc_file_does_not_exist():
|
||||
ifc = IfcStore.get_file()
|
||||
|
||||
@@ -21,20 +21,23 @@ import blenderbim.core.document as subject
|
||||
from test.core.bootstrap import ifc, document
|
||||
|
||||
|
||||
class TestLoadInformation:
|
||||
class TestLoadProjectDocuments:
|
||||
def test_run(self, document):
|
||||
document.import_information().should_be_called()
|
||||
document.enable_information_editing_ui().should_be_called()
|
||||
document.disable_editing_document().should_be_called()
|
||||
subject.load_information(document)
|
||||
document.clear_document_tree().should_be_called()
|
||||
document.import_project_documents().should_be_called()
|
||||
document.clear_breadcrumbs().should_be_called()
|
||||
document.enable_editing_ui().should_be_called()
|
||||
subject.load_project_documents(document)
|
||||
|
||||
|
||||
class TestLoadReferences:
|
||||
class TestLoadDocument:
|
||||
def test_run(self, document):
|
||||
document.import_references().should_be_called()
|
||||
document.enable_reference_editing_ui().should_be_called()
|
||||
document.clear_document_tree().should_be_called()
|
||||
document.import_subdocuments("document").should_be_called()
|
||||
document.import_references("document").should_be_called()
|
||||
document.disable_editing_document().should_be_called()
|
||||
subject.load_references(document)
|
||||
document.add_breadcrumb("document").should_be_called()
|
||||
subject.load_document(document, document="document")
|
||||
|
||||
|
||||
class TestDisableDocumentEditingUi:
|
||||
@@ -58,72 +61,76 @@ class TestDisableEditingDocument:
|
||||
|
||||
|
||||
class TestAddInformation:
|
||||
def test_run(self, ifc, document):
|
||||
ifc.run("document.add_information").should_be_called().will_return("information")
|
||||
document.import_information().should_be_called()
|
||||
document.import_document_attributes("information").should_be_called()
|
||||
document.set_active_document("information").should_be_called()
|
||||
def test_add_and_reload_tree_at_project_root(self, ifc, document):
|
||||
document.clear_document_tree().should_be_called()
|
||||
document.get_active_breadcrumb().should_be_called().will_return(None)
|
||||
ifc.run("document.add_information", parent=None).should_be_called().will_return("information")
|
||||
ifc.run("document.add_reference", information="information").should_be_called()
|
||||
document.import_project_documents().should_be_called()
|
||||
subject.add_information(ifc, document)
|
||||
|
||||
def test_add_and_reload_tree_at_current_parent(self, ifc, document):
|
||||
document.clear_document_tree().should_be_called()
|
||||
document.get_active_breadcrumb().should_be_called().will_return("parent")
|
||||
ifc.run("document.add_information", parent="parent").should_be_called().will_return("information")
|
||||
ifc.run("document.add_reference", information="information").should_be_called()
|
||||
document.import_subdocuments("parent").should_be_called()
|
||||
document.import_references("parent").should_be_called()
|
||||
subject.add_information(ifc, document)
|
||||
|
||||
|
||||
class TestAddReference:
|
||||
def test_run(self, ifc, document):
|
||||
ifc.run("document.add_reference").should_be_called().will_return("reference")
|
||||
document.import_references().should_be_called()
|
||||
document.import_document_attributes("reference").should_be_called()
|
||||
document.set_active_document("reference").should_be_called()
|
||||
document.get_active_breadcrumb().should_be_called().will_return("parent")
|
||||
ifc.run("document.add_reference", information="parent").should_be_called()
|
||||
document.clear_document_tree().should_be_called()
|
||||
document.import_subdocuments("parent").should_be_called()
|
||||
document.import_references("parent").should_be_called()
|
||||
subject.add_reference(ifc, document)
|
||||
|
||||
|
||||
class TestEditInformation:
|
||||
def test_run(self, ifc, document):
|
||||
class TestEditDocument:
|
||||
def test_edit_information(self, ifc, document):
|
||||
document.export_document_attributes().should_be_called().will_return("attributes")
|
||||
ifc.run("document.edit_information", information="information", attributes="attributes").should_be_called()
|
||||
document.is_document_information("document").should_be_called().will_return(True)
|
||||
ifc.run("document.edit_information", information="document", attributes="attributes").should_be_called()
|
||||
document.disable_editing_document().should_be_called()
|
||||
document.import_information().should_be_called()
|
||||
subject.edit_information(ifc, document, information="information")
|
||||
document.clear_document_tree().should_be_called()
|
||||
document.get_active_breadcrumb().should_be_called().will_return(None)
|
||||
document.import_project_documents().should_be_called()
|
||||
subject.edit_document(ifc, document, document="document")
|
||||
|
||||
|
||||
class TestEditInformation:
|
||||
def test_run(self, ifc, document):
|
||||
def test_edit_reference(self, ifc, document):
|
||||
document.export_document_attributes().should_be_called().will_return("attributes")
|
||||
ifc.run("document.edit_reference", reference="reference", attributes="attributes").should_be_called()
|
||||
document.is_document_information("document").should_be_called().will_return(False)
|
||||
ifc.run("document.edit_reference", reference="document", attributes="attributes").should_be_called()
|
||||
document.disable_editing_document().should_be_called()
|
||||
document.import_references().should_be_called()
|
||||
subject.edit_reference(ifc, document, reference="reference")
|
||||
document.clear_document_tree().should_be_called()
|
||||
document.get_active_breadcrumb().should_be_called().will_return("parent")
|
||||
document.import_subdocuments("parent").should_be_called()
|
||||
document.import_references("parent").should_be_called()
|
||||
subject.edit_document(ifc, document, document="document")
|
||||
|
||||
|
||||
class TestRemoveDocument:
|
||||
def test_remove_information(self, ifc, document):
|
||||
document.clear_document_tree().should_be_called()
|
||||
document.is_document_information("document").should_be_called().will_return(True)
|
||||
ifc.run("document.remove_document", document="document").should_be_called()
|
||||
document.import_information().should_be_called()
|
||||
document.disable_editing_document().should_be_called()
|
||||
ifc.run("document.remove_information", information="document").should_be_called()
|
||||
document.get_active_breadcrumb().should_be_called().will_return(None)
|
||||
document.import_project_documents().should_be_called()
|
||||
subject.remove_document(ifc, document, document="document")
|
||||
|
||||
def test_remove_reference(self, ifc, document):
|
||||
document.clear_document_tree().should_be_called()
|
||||
document.is_document_information("document").should_be_called().will_return(False)
|
||||
ifc.run("document.remove_document", document="document").should_be_called()
|
||||
document.import_references().should_be_called()
|
||||
document.disable_editing_document().should_be_called()
|
||||
ifc.run("document.remove_reference", reference="document").should_be_called()
|
||||
document.get_active_breadcrumb().should_be_called().will_return("parent")
|
||||
document.import_subdocuments("parent").should_be_called()
|
||||
document.import_references("parent").should_be_called()
|
||||
subject.remove_document(ifc, document, document="document")
|
||||
|
||||
|
||||
class TestEnableAssigningDocument:
|
||||
def test_run(self, document):
|
||||
document.import_references().should_be_called()
|
||||
document.enable_reference_editing_ui().should_be_called()
|
||||
document.disable_editing_document().should_be_called()
|
||||
document.enable_document_assignment_ui("obj").should_be_called()
|
||||
subject.enable_assigning_document(document, obj="obj")
|
||||
|
||||
|
||||
class TestDisableAssigningDocument:
|
||||
def test_run(self, document):
|
||||
document.disable_document_assignment_ui("obj").should_be_called()
|
||||
subject.disable_assigning_document(document, obj="obj")
|
||||
|
||||
|
||||
class TestAssignDocument:
|
||||
def test_run(self, ifc):
|
||||
ifc.run("document.assign_document", product="product", document="document").should_be_called()
|
||||
|
||||
@@ -141,7 +141,8 @@ class TestDisableEditingDrawings:
|
||||
|
||||
class TestAddDrawing:
|
||||
def test_run(self, ifc, collector, drawing):
|
||||
drawing.ensure_unique_drawing_name("UNTITLED").should_be_called().will_return("name")
|
||||
drawing.generate_drawing_name("target_view", "location_hint").should_be_called().will_return("drawing_name")
|
||||
drawing.ensure_unique_drawing_name("drawing_name").should_be_called().will_return("name")
|
||||
drawing.generate_drawing_matrix("target_view", "location_hint").should_be_called().will_return("matrix")
|
||||
drawing.create_camera("name", "matrix").should_be_called().will_return("obj")
|
||||
drawing.get_body_context().should_be_called().will_return("context")
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blenderbim.core.material as subject
|
||||
from test.core.bootstrap import ifc, material
|
||||
from test.core.bootstrap import ifc, material, style
|
||||
|
||||
|
||||
class TestUnlinkMaterial:
|
||||
@@ -26,9 +26,131 @@ class TestUnlinkMaterial:
|
||||
subject.unlink_material(ifc, obj="obj")
|
||||
|
||||
|
||||
class TestAddDefaultMaterial:
|
||||
def test_run(self, ifc, material):
|
||||
class TestAddMaterial:
|
||||
def test_add_a_default_material(self, ifc, material, style):
|
||||
material.add_default_material_object().should_be_called().will_return("obj")
|
||||
ifc.run("material.add_material", name="Default").should_be_called().will_return("material")
|
||||
material.get_name("obj").should_be_called().will_return("name")
|
||||
ifc.run("material.add_material", name="name").should_be_called().will_return("material")
|
||||
ifc.link("material", "obj").should_be_called()
|
||||
assert subject.add_default_material(ifc, material) == "obj"
|
||||
style.get_style("obj").should_be_called().will_return(None)
|
||||
material.is_editing_materials().should_be_called().will_return(False)
|
||||
assert subject.add_material(ifc, material, style) == "material"
|
||||
|
||||
def test_add_a_material_to_a_blender_material_object(self, ifc, material, style):
|
||||
material.get_name("obj").should_be_called().will_return("name")
|
||||
ifc.run("material.add_material", name="name").should_be_called().will_return("material")
|
||||
ifc.link("material", "obj").should_be_called()
|
||||
style.get_style("obj").should_be_called().will_return(None)
|
||||
material.is_editing_materials().should_be_called().will_return(False)
|
||||
assert subject.add_material(ifc, material, style, obj="obj") == "material"
|
||||
|
||||
def test_reloading_imported_materials_if_you_are_editing_scene_materials(self, ifc, material, style):
|
||||
material.get_name("obj").should_be_called().will_return("name")
|
||||
ifc.run("material.add_material", name="name").should_be_called().will_return("material")
|
||||
ifc.link("material", "obj").should_be_called()
|
||||
style.get_style("obj").should_be_called().will_return(None)
|
||||
material.is_editing_materials().should_be_called().will_return(True)
|
||||
material.get_active_material_type().should_be_called().will_return("material_type")
|
||||
material.import_material_definitions("material_type").should_be_called()
|
||||
assert subject.add_material(ifc, material, style, obj="obj") == "material"
|
||||
|
||||
def test_add_a_style_to_the_material_if_the_object_also_has_an_attached_style(self, ifc, material, style):
|
||||
material.get_name("obj").should_be_called().will_return("name")
|
||||
ifc.run("material.add_material", name="name").should_be_called().will_return("material")
|
||||
ifc.link("material", "obj").should_be_called()
|
||||
style.get_style("obj").should_be_called().will_return("style")
|
||||
style.get_context("obj").should_be_called().will_return("context")
|
||||
ifc.run("style.assign_material_style", material="material", style="style", context="context").should_be_called()
|
||||
material.is_editing_materials().should_be_called().will_return(False)
|
||||
assert subject.add_material(ifc, material, style, obj="obj") == "material"
|
||||
|
||||
def test_not_adding_a_style_if_there_is_no_style_context_available(self, ifc, material, style):
|
||||
material.get_name("obj").should_be_called().will_return("name")
|
||||
ifc.run("material.add_material", name="name").should_be_called().will_return("material")
|
||||
ifc.link("material", "obj").should_be_called()
|
||||
style.get_style("obj").should_be_called().will_return("style")
|
||||
style.get_context("obj").should_be_called().will_return(None)
|
||||
material.is_editing_materials().should_be_called().will_return(False)
|
||||
assert subject.add_material(ifc, material, style, obj="obj") == "material"
|
||||
|
||||
|
||||
class TestAddMaterialSet:
|
||||
def test_adding_a_material_set(self, ifc, material):
|
||||
ifc.run("material.add_material_set", name="Unnamed", set_type="set_type").should_be_called().will_return(
|
||||
"material"
|
||||
)
|
||||
material.is_editing_materials().should_be_called().will_return(False)
|
||||
assert subject.add_material_set(ifc, material, set_type="set_type") == "material"
|
||||
|
||||
def test_adding_a_material_set_and_reloading_imported_materials(self, ifc, material):
|
||||
ifc.run("material.add_material_set", name="Unnamed", set_type="set_type").should_be_called().will_return(
|
||||
"material"
|
||||
)
|
||||
material.is_editing_materials().should_be_called().will_return(True)
|
||||
material.get_active_material_type().should_be_called().will_return("material_type")
|
||||
material.import_material_definitions("material_type").should_be_called()
|
||||
assert subject.add_material_set(ifc, material, set_type="set_type") == "material"
|
||||
|
||||
|
||||
class TestRemoveMaterial:
|
||||
def test_removing_a_material(self, ifc, material, style):
|
||||
ifc.get_object("material").should_be_called().will_return(None)
|
||||
ifc.unlink(element="material").should_be_called()
|
||||
ifc.run("material.remove_material", material="material").should_be_called()
|
||||
material.is_editing_materials().should_be_called().will_return(False)
|
||||
subject.remove_material(ifc, material, style, material="material")
|
||||
|
||||
def test_removing_a_material_and_reloading_imported_materials(self, ifc, material, style):
|
||||
ifc.get_object("material").should_be_called().will_return(None)
|
||||
ifc.unlink(element="material").should_be_called()
|
||||
ifc.run("material.remove_material", material="material").should_be_called()
|
||||
material.is_editing_materials().should_be_called().will_return(True)
|
||||
material.get_active_material_type().should_be_called().will_return("material_type")
|
||||
material.import_material_definitions("material_type").should_be_called()
|
||||
subject.remove_material(ifc, material, style, material="material")
|
||||
|
||||
def test_removing_a_material_object_if_it_has_no_style(self, ifc, material, style):
|
||||
ifc.get_object("material").should_be_called().will_return("obj")
|
||||
ifc.unlink(element="material").should_be_called()
|
||||
ifc.run("material.remove_material", material="material").should_be_called()
|
||||
style.get_style("obj").should_be_called().will_return(None)
|
||||
material.delete_object("obj").should_be_called()
|
||||
material.is_editing_materials().should_be_called().will_return(False)
|
||||
subject.remove_material(ifc, material, style, material="material")
|
||||
|
||||
def test_preserving_a_material_object_if_it_is_still_used_as_a_style(self, ifc, material, style):
|
||||
ifc.get_object("material").should_be_called().will_return("obj")
|
||||
ifc.unlink(element="material").should_be_called()
|
||||
ifc.run("material.remove_material", material="material").should_be_called()
|
||||
style.get_style("obj").should_be_called().will_return("style")
|
||||
material.is_editing_materials().should_be_called().will_return(False)
|
||||
subject.remove_material(ifc, material, style, material="material")
|
||||
|
||||
|
||||
class TestRemoveMaterialSet:
|
||||
def test_run(self, ifc, material):
|
||||
ifc.run("material.remove_material_set", material="material").should_be_called()
|
||||
material.is_editing_materials().should_be_called().will_return(True)
|
||||
material.get_active_material_type().should_be_called().will_return("material_type")
|
||||
material.import_material_definitions("material_type").should_be_called()
|
||||
subject.remove_material_set(ifc, material, material="material")
|
||||
|
||||
|
||||
class TestLoadMaterials:
|
||||
def test_run(self, material):
|
||||
material.import_material_definitions("material_type").should_be_called()
|
||||
material.enable_editing_materials().should_be_called()
|
||||
subject.load_materials(material, "material_type")
|
||||
|
||||
|
||||
class TestDisableEditingMaterials:
|
||||
def test_run(self, material):
|
||||
material.disable_editing_materials().should_be_called()
|
||||
subject.disable_editing_materials(material)
|
||||
|
||||
|
||||
class TestSelectByMaterial:
|
||||
def test_run(self, material):
|
||||
material.get_elements_by_material("material").should_be_called().will_return("elements")
|
||||
material.select_elements("elements").should_be_called()
|
||||
subject.select_by_material(material, material="material")
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blenderbim.core.style as subject
|
||||
from test.core.bootstrap import ifc, style
|
||||
from test.core.bootstrap import ifc, material, style
|
||||
|
||||
|
||||
class TestAddStyle:
|
||||
@@ -53,11 +53,32 @@ class TestAddStyle:
|
||||
|
||||
|
||||
class TestRemoveStyle:
|
||||
def test_run(self, ifc, style):
|
||||
style.get_style("obj").should_be_called().will_return("style")
|
||||
def test_removing_a_style(self, ifc, material, style):
|
||||
ifc.get_object("style").should_be_called().will_return("obj")
|
||||
ifc.unlink(obj="obj", element="style").should_be_called()
|
||||
ifc.run("style.remove_style", style="style").should_be_called()
|
||||
subject.remove_style(ifc, style, obj="obj")
|
||||
ifc.get_entity("obj").should_be_called().will_return("material")
|
||||
style.is_editing_styles().should_be_called().will_return(False)
|
||||
subject.remove_style(ifc, material, style, style="style")
|
||||
|
||||
def test_removing_a_style_and_reloading_imported_styles(self, ifc, material, style):
|
||||
ifc.get_object("style").should_be_called().will_return("obj")
|
||||
ifc.unlink(obj="obj", element="style").should_be_called()
|
||||
ifc.run("style.remove_style", style="style").should_be_called()
|
||||
ifc.get_entity("obj").should_be_called().will_return("material")
|
||||
style.is_editing_styles().should_be_called().will_return(True)
|
||||
style.get_active_style_type().should_be_called().will_return("style_type")
|
||||
style.import_presentation_styles("style_type").should_be_called()
|
||||
subject.remove_style(ifc, material, style, style="style")
|
||||
|
||||
def test_removing_an_object_if_it_is_not_still_used_for_a_material(self, ifc, material, style):
|
||||
ifc.get_object("style").should_be_called().will_return("obj")
|
||||
ifc.unlink(obj="obj", element="style").should_be_called()
|
||||
ifc.run("style.remove_style", style="style").should_be_called()
|
||||
ifc.get_entity("obj").should_be_called().will_return(None)
|
||||
material.delete_object("obj").should_be_called()
|
||||
style.is_editing_styles().should_be_called().will_return(False)
|
||||
subject.remove_style(ifc, material, style, style="style")
|
||||
|
||||
|
||||
class TestUpdateStyleColours:
|
||||
@@ -174,3 +195,16 @@ class TestEditStyle:
|
||||
ifc.run("style.edit_presentation_style", style="style", attributes="attributes").should_be_called()
|
||||
style.disable_editing("obj").should_be_called()
|
||||
subject.edit_style(ifc, style, obj="obj")
|
||||
|
||||
|
||||
class TestLoadStyles:
|
||||
def test_run(self, style):
|
||||
style.import_presentation_styles("style_type").should_be_called()
|
||||
style.enable_editing_styles().should_be_called()
|
||||
subject.load_styles(style, style_type="style_type")
|
||||
|
||||
|
||||
class TestDisableEditingStyles:
|
||||
def test_run(self, style):
|
||||
style.disable_editing_styles().should_be_called()
|
||||
subject.disable_editing_styles(style)
|
||||
|
||||
@@ -39,7 +39,9 @@ class TestAssignSceneUnits:
|
||||
"unit.add_si_unit", unit_type="VOLUMEUNIT", name="name", prefix="prefix"
|
||||
).should_be_called().will_return("volumeunit")
|
||||
|
||||
ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit"]).should_be_called()
|
||||
ifc.run("unit.add_conversion_based_unit", name="degree").should_be_called().will_return("planeangleunit")
|
||||
|
||||
ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit"]).should_be_called()
|
||||
subject.assign_scene_units(ifc, unit)
|
||||
|
||||
def test_creating_and_assigning_imperial_units(self, ifc, unit):
|
||||
@@ -53,7 +55,9 @@ class TestAssignSceneUnits:
|
||||
unit.get_scene_unit_name("volume").should_be_called().will_return("volumename")
|
||||
ifc.run("unit.add_conversion_based_unit", name="volumename").should_be_called().will_return("volumeunit")
|
||||
|
||||
ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit"]).should_be_called()
|
||||
ifc.run("unit.add_conversion_based_unit", name="degree").should_be_called().will_return("planeangleunit")
|
||||
|
||||
ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit"]).should_be_called()
|
||||
subject.assign_scene_units(ifc, unit)
|
||||
|
||||
|
||||
@@ -115,6 +119,13 @@ class TestAddContextDependentUnit:
|
||||
assert subject.add_context_dependent_unit(ifc, unit, unit_type="unit_type", name="name") == "unit"
|
||||
|
||||
|
||||
class TestAddConversionBasedUnit:
|
||||
def test_run(self, ifc, unit):
|
||||
ifc.run("unit.add_conversion_based_unit", name="name").should_be_called().will_return("unit")
|
||||
unit.import_units().should_be_called()
|
||||
assert subject.add_conversion_based_unit(ifc, unit, name="name") == "unit"
|
||||
|
||||
|
||||
class TestEnableEditingUnit:
|
||||
def test_run(self, unit):
|
||||
unit.set_active_unit("unit").should_be_called()
|
||||
|
||||
@@ -29,12 +29,30 @@ class TestImplementsTool(NewFile):
|
||||
assert isinstance(subject(), blenderbim.core.tool.Document)
|
||||
|
||||
|
||||
class TestDisableDocumentAssignmentUI(NewFile):
|
||||
class TestAddBreadcrumb(NewFile):
|
||||
def test_run(self):
|
||||
obj = bpy.data.objects.new("Object", None)
|
||||
obj.BIMObjectDocumentProperties.is_adding = "foo"
|
||||
subject.disable_document_assignment_ui(obj)
|
||||
assert obj.BIMObjectDocumentProperties.is_adding == ""
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc().set(ifc)
|
||||
document = ifc.createIfcDocumentInformation()
|
||||
subject.add_breadcrumb(document)
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
assert props.breadcrumbs[0].name == str(document.id())
|
||||
|
||||
|
||||
class TestClearBreadcrumbs(NewFile):
|
||||
def test_run(self):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
props.breadcrumbs.add()
|
||||
subject.clear_breadcrumbs()
|
||||
assert len(props.breadcrumbs) == 0
|
||||
|
||||
|
||||
class TestClearDocumentTree(NewFile):
|
||||
def test_run(self):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
new = props.documents.add()
|
||||
subject.clear_document_tree()
|
||||
assert len(props.documents) == 0
|
||||
|
||||
|
||||
class TestDisableEditingDocument(NewFile):
|
||||
@@ -46,31 +64,16 @@ class TestDisableEditingDocument(NewFile):
|
||||
|
||||
class TestDisableEditingUI(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMDocumentProperties.is_editing = "is_editing"
|
||||
bpy.context.scene.BIMDocumentProperties.is_editing = True
|
||||
subject.disable_editing_ui()
|
||||
assert bpy.context.scene.BIMDocumentProperties.is_editing == ""
|
||||
assert bpy.context.scene.BIMDocumentProperties.is_editing == False
|
||||
|
||||
|
||||
class TestEnableDocumentAssignmentUI(NewFile):
|
||||
class TestEnableEditingUI(NewFile):
|
||||
def test_run(self):
|
||||
obj = bpy.data.objects.new("Object", None)
|
||||
obj.BIMObjectDocumentProperties.is_adding = ""
|
||||
subject.enable_document_assignment_ui(obj)
|
||||
assert obj.BIMObjectDocumentProperties.is_adding == "IfcDocumentReference"
|
||||
|
||||
|
||||
class TestEnableInformationEditingUI(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMDocumentProperties.is_editing = ""
|
||||
subject.enable_information_editing_ui()
|
||||
assert bpy.context.scene.BIMDocumentProperties.is_editing == "information"
|
||||
|
||||
|
||||
class TestEnableReferenceEditingUI(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMDocumentProperties.is_editing = ""
|
||||
subject.enable_reference_editing_ui()
|
||||
assert bpy.context.scene.BIMDocumentProperties.is_editing == "reference"
|
||||
bpy.context.scene.BIMDocumentProperties.is_editing = False
|
||||
subject.enable_editing_ui()
|
||||
assert bpy.context.scene.BIMDocumentProperties.is_editing == True
|
||||
|
||||
|
||||
class TestExportDocumentAttributes(NewFile):
|
||||
@@ -95,6 +98,15 @@ class TestExportDocumentAttributes(NewFile):
|
||||
}
|
||||
|
||||
|
||||
class TestGetActiveBreadcrumb(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc().set(ifc)
|
||||
document = ifc.createIfcDocumentInformation()
|
||||
subject.add_breadcrumb(document)
|
||||
assert subject.get_active_breadcrumb() == document
|
||||
|
||||
|
||||
class TestImportDocumentAttributes(NewFile):
|
||||
def test_importing_information(self):
|
||||
ifc = ifcopenshell.file()
|
||||
@@ -149,30 +161,51 @@ class TestImportDocumentAttributes(NewFile):
|
||||
assert props.document_attributes.get("Description").string_value == "Description"
|
||||
|
||||
|
||||
class TestImportInformation(NewFile):
|
||||
class TestImportProjectDocuments(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc().set(ifc)
|
||||
document = ifc.createIfcDocumentInformation()
|
||||
subject.import_information()
|
||||
ifc.createIfcProject()
|
||||
document = ifcopenshell.api.run("document.add_information", ifc)
|
||||
subject.import_project_documents()
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
assert len(props.documents) == 1
|
||||
assert props.documents[0].ifc_definition_id == document.id()
|
||||
assert props.documents[0].name == "Unnamed"
|
||||
assert props.documents[0].identification == "*"
|
||||
assert props.documents[0].identification == "X"
|
||||
assert props.documents[0].is_information is True
|
||||
|
||||
|
||||
class TestImportReference(NewFile):
|
||||
class TestImportReferences(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc().set(ifc)
|
||||
document = ifc.createIfcDocumentReference()
|
||||
subject.import_references()
|
||||
ifc.createIfcProject()
|
||||
document = ifcopenshell.api.run("document.add_information", ifc)
|
||||
reference = ifcopenshell.api.run("document.add_reference", ifc, information=document)
|
||||
subject.import_references(document)
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
assert len(props.documents) == 1
|
||||
assert props.documents[0].ifc_definition_id == document.id()
|
||||
assert props.documents[0].ifc_definition_id == reference.id()
|
||||
assert props.documents[0].name == "Unnamed"
|
||||
assert props.documents[0].identification == "*"
|
||||
assert props.documents[0].is_information is False
|
||||
|
||||
|
||||
class TestImportSubdocuments(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc().set(ifc)
|
||||
ifc.createIfcProject()
|
||||
document = ifcopenshell.api.run("document.add_information", ifc)
|
||||
subdocument = ifcopenshell.api.run("document.add_information", ifc, parent=document)
|
||||
subject.import_subdocuments(document)
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
assert len(props.documents) == 1
|
||||
assert props.documents[0].ifc_definition_id == subdocument.id()
|
||||
assert props.documents[0].name == "Unnamed"
|
||||
assert props.documents[0].identification == "X"
|
||||
assert props.documents[0].is_information is True
|
||||
|
||||
|
||||
class TestIsDocumentInformation(NewFile):
|
||||
@@ -184,6 +217,15 @@ class TestIsDocumentInformation(NewFile):
|
||||
assert subject.is_document_information(reference) is False
|
||||
|
||||
|
||||
class TestRemoveLatestBreadcrumb(NewFile):
|
||||
def test_run(self):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
props.breadcrumbs.add()
|
||||
props.breadcrumbs.add()
|
||||
subject.remove_latest_breadcrumb()
|
||||
assert len(props.breadcrumbs) == 1
|
||||
|
||||
|
||||
class TestSetActiveDocument(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
|
||||
@@ -399,10 +399,13 @@ class TestImportDrawings(NewFile):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
drawing = ifc.createIfcAnnotation(Name="FOOBAR", ObjectType="DRAWING")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=drawing, name="EPset_Drawing")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"TargetView": "PLAN_VIEW"})
|
||||
subject.import_drawings()
|
||||
props = bpy.context.scene.DocProperties
|
||||
assert props.drawings[0].ifc_definition_id == drawing.id()
|
||||
assert props.drawings[0].name == "FOOBAR"
|
||||
assert props.drawings[0].target_view == "PLAN_VIEW"
|
||||
|
||||
|
||||
class TestImportSheets(NewFile):
|
||||
|
||||
@@ -34,3 +34,122 @@ class TestAddDefaultMaterialObject(NewFile):
|
||||
material = subject.add_default_material_object()
|
||||
assert isinstance(material, bpy.types.Material)
|
||||
assert material.name == "Default"
|
||||
|
||||
|
||||
class TestDeleteObject(NewFile):
|
||||
def test_run(self):
|
||||
material = subject.add_default_material_object()
|
||||
assert bpy.data.materials.get("Default")
|
||||
subject.delete_object(material)
|
||||
assert not bpy.data.materials.get("Default")
|
||||
|
||||
|
||||
class TestDisableEditingMaterials(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = True
|
||||
subject.disable_editing_materials()
|
||||
assert bpy.context.scene.BIMMaterialProperties.is_editing is False
|
||||
|
||||
|
||||
class TestEnableEditingMaterials(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = False
|
||||
subject.enable_editing_materials()
|
||||
assert bpy.context.scene.BIMMaterialProperties.is_editing is True
|
||||
|
||||
|
||||
class TestGetActiveMaterialType(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
bpy.context.scene.BIMMaterialProperties.material_type = "IfcMaterial"
|
||||
assert subject.get_active_material_type() == "IfcMaterial"
|
||||
bpy.context.scene.BIMMaterialProperties.material_type = "IfcMaterialLayerSet"
|
||||
assert subject.get_active_material_type() == "IfcMaterialLayerSet"
|
||||
|
||||
|
||||
class TestGetElementsByMaterial(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
material = ifcopenshell.api.run("material.add_material", ifc)
|
||||
ifcopenshell.api.run("material.assign_material", ifc, product=element, material=material)
|
||||
assert subject.get_elements_by_material(material) == {element}
|
||||
|
||||
|
||||
class TestGetName(NewFile):
|
||||
def test_run(self):
|
||||
assert subject.get_name(bpy.data.materials.new("Material")) == "Material"
|
||||
|
||||
|
||||
class TestImportMaterialDefinitions(NewFile):
|
||||
def test_import_materials(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
material = ifc.createIfcMaterial(Name="Name")
|
||||
subject.import_material_definitions("IfcMaterial")
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
assert props.materials[0].ifc_definition_id == material.id()
|
||||
assert props.materials[0].name == "Name"
|
||||
assert props.materials[0].total_elements == 0
|
||||
|
||||
def test_import_material_layer_sets(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
material = ifc.createIfcMaterialLayerSet(LayerSetName="Name")
|
||||
subject.import_material_definitions("IfcMaterialLayerSet")
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
assert props.materials[0].ifc_definition_id == material.id()
|
||||
assert props.materials[0].name == "Name"
|
||||
assert props.materials[0].total_elements == 0
|
||||
|
||||
def test_import_material_profile_sets(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
material = ifc.createIfcMaterialProfileSet(Name="Name")
|
||||
subject.import_material_definitions("IfcMaterialProfileSet")
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
assert props.materials[0].ifc_definition_id == material.id()
|
||||
assert props.materials[0].name == "Name"
|
||||
assert props.materials[0].total_elements == 0
|
||||
|
||||
def test_import_material_constituent_sets(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
material = ifc.createIfcMaterialConstituentSet(Name="Name")
|
||||
subject.import_material_definitions("IfcMaterialConstituentSet")
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
assert props.materials[0].ifc_definition_id == material.id()
|
||||
assert props.materials[0].name == "Name"
|
||||
assert props.materials[0].total_elements == 0
|
||||
|
||||
def test_import_material_lists(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
material = ifc.createIfcMaterialList()
|
||||
subject.import_material_definitions("IfcMaterialList")
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
assert props.materials[0].ifc_definition_id == material.id()
|
||||
assert props.materials[0].name == "Unnamed"
|
||||
assert props.materials[0].total_elements == 0
|
||||
|
||||
|
||||
class TestIsEditingMaterials(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = False
|
||||
subject.is_editing_materials() is False
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = True
|
||||
subject.is_editing_materials() is True
|
||||
|
||||
|
||||
class TestSelectElements(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc().set(ifc)
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcPump")
|
||||
obj = bpy.data.objects.new("Object", None)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
tool.Ifc.link(element, obj)
|
||||
subject.select_elements([element])
|
||||
assert obj in bpy.context.selected_objects
|
||||
|
||||
@@ -50,6 +50,13 @@ class TestDisableEditing(NewFile):
|
||||
assert obj.BIMStyleProperties.is_editing is False
|
||||
|
||||
|
||||
class TestDisableEditingStyles(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMStylesProperties.is_editing = True
|
||||
subject.disable_editing_styles()
|
||||
assert bpy.context.scene.BIMStylesProperties.is_editing is False
|
||||
|
||||
|
||||
class TestEnableEditing(NewFile):
|
||||
def test_run(self):
|
||||
obj = bpy.data.materials.new("Material")
|
||||
@@ -57,6 +64,13 @@ class TestEnableEditing(NewFile):
|
||||
assert obj.BIMStyleProperties.is_editing is True
|
||||
|
||||
|
||||
class TestEnableEditingStyles(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMStylesProperties.is_editing = False
|
||||
subject.enable_editing_styles()
|
||||
assert bpy.context.scene.BIMStylesProperties.is_editing is True
|
||||
|
||||
|
||||
class TestExportSurfaceAttributes(NewFile):
|
||||
def test_run(self):
|
||||
TestImportSurfaceAttributes().test_run()
|
||||
@@ -64,6 +78,16 @@ class TestExportSurfaceAttributes(NewFile):
|
||||
assert subject.export_surface_attributes(obj) == {"Name": "Name", "Side": "BOTH"}
|
||||
|
||||
|
||||
class TestGetActiveStyleType(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
bpy.context.scene.BIMStylesProperties.style_type = "IfcSurfaceStyle"
|
||||
assert subject.get_active_style_type() == "IfcSurfaceStyle"
|
||||
bpy.context.scene.BIMStylesProperties.style_type = "IfcCurveStyle"
|
||||
assert subject.get_active_style_type() == "IfcCurveStyle"
|
||||
|
||||
|
||||
class TestGetContext(NewFile):
|
||||
def test_run(self):
|
||||
bpy.ops.bim.create_project()
|
||||
@@ -116,6 +140,7 @@ class TestGetSurfaceRenderingAttributes(NewFile):
|
||||
"Green": 0.5,
|
||||
"Blue": 0.5,
|
||||
},
|
||||
"SpecularColour": 0.0,
|
||||
"SpecularHighlight": {"IfcSpecularRoughness": 0.2},
|
||||
"ReflectanceMethod": "NOTDEFINED",
|
||||
}
|
||||
@@ -351,3 +376,53 @@ class TestImportSurfaceAttributes(NewFile):
|
||||
assert len(obj.BIMStyleProperties.attributes) == 2
|
||||
assert obj.BIMStyleProperties.attributes.get("Name").string_value == "Name"
|
||||
assert obj.BIMStyleProperties.attributes.get("Side").enum_value == "BOTH"
|
||||
|
||||
|
||||
class TestImportPresentationStyles(NewFile):
|
||||
def test_import_curve_styles(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
style = ifc.createIfcCurveStyle(Name="Name")
|
||||
subject.import_presentation_styles("IfcCurveStyle")
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
assert props.styles[0].ifc_definition_id == style.id()
|
||||
assert props.styles[0].name == "Name"
|
||||
assert props.styles[0].total_elements == 0
|
||||
|
||||
def test_import_fillarea_styles(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
style = ifc.createIfcFillAreaStyle(Name="Name")
|
||||
subject.import_presentation_styles("IfcFillAreaStyle")
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
assert props.styles[0].ifc_definition_id == style.id()
|
||||
assert props.styles[0].name == "Name"
|
||||
assert props.styles[0].total_elements == 0
|
||||
|
||||
def test_import_surface_styles(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
style = ifc.createIfcSurfaceStyle(Name="Name")
|
||||
subject.import_presentation_styles("IfcSurfaceStyle")
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
assert props.styles[0].ifc_definition_id == style.id()
|
||||
assert props.styles[0].name == "Name"
|
||||
assert props.styles[0].total_elements == 0
|
||||
|
||||
def test_import_text_styles(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
style = ifc.createIfcTextStyle(Name="Name")
|
||||
subject.import_presentation_styles("IfcTextStyle")
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
assert props.styles[0].ifc_definition_id == style.id()
|
||||
assert props.styles[0].name == "Name"
|
||||
assert props.styles[0].total_elements == 0
|
||||
|
||||
|
||||
class TestIsEditingStyles(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMStylesProperties.is_editing = False
|
||||
subject.is_editing_styles() is False
|
||||
bpy.context.scene.BIMStylesProperties.is_editing = True
|
||||
subject.is_editing_styles() is True
|
||||
|
||||
@@ -299,7 +299,7 @@ private:
|
||||
|
||||
public:
|
||||
MAKE_TYPE_NAME(Kernel)()
|
||||
: IfcGeom::Kernel(0)
|
||||
: IfcGeom::Kernel()
|
||||
, deflection_tolerance(0.001)
|
||||
, max_faces_to_orient(-1.0)
|
||||
, ifc_length_unit(1.0)
|
||||
@@ -321,7 +321,7 @@ public:
|
||||
{}
|
||||
|
||||
MAKE_TYPE_NAME(Kernel)(const MAKE_TYPE_NAME(Kernel)& other)
|
||||
: IfcGeom::Kernel(0)
|
||||
: IfcGeom::Kernel()
|
||||
, deflection_tolerance(other.deflection_tolerance)
|
||||
, max_faces_to_orient(other.max_faces_to_orient)
|
||||
, ifc_length_unit(other.ifc_length_unit)
|
||||
|
||||
@@ -563,8 +563,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleHollowProfileDef* l,
|
||||
const double y = l->YDim() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double d = l->WallThickness() * getValue(GV_LENGTH_UNIT);
|
||||
|
||||
const bool fr1 = !l->OuterFilletRadius();
|
||||
const bool fr2 = !l->InnerFilletRadius();
|
||||
const bool fr1 = !!l->OuterFilletRadius();
|
||||
const bool fr2 = !!l->InnerFilletRadius();
|
||||
|
||||
const double r1 = fr1 ? (*l->OuterFilletRadius()) * getValue(GV_LENGTH_UNIT) : 0.;
|
||||
const double r2 = fr2 ? (*l->InnerFilletRadius()) * getValue(GV_LENGTH_UNIT) : 0.;
|
||||
|
||||
@@ -4240,6 +4240,7 @@ bool IfcGeom::Kernel::wire_intersections(const TopoDS_Wire& wire, TopTools_ListO
|
||||
: (std::min)(min_edge_length(wire) / 2., getValue(GV_PRECISION) * 10.);
|
||||
}
|
||||
|
||||
// @todo: should this start from 0 in case of n > 64?
|
||||
for (int i = 2; i < n; ++i) {
|
||||
|
||||
std::vector<int> js;
|
||||
@@ -5224,8 +5225,11 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo
|
||||
PERF("boolean operation: result min face-face dist check");
|
||||
|
||||
if ((v = min_face_face_distance(r, 1.e-4)) < 1.e-4) {
|
||||
reason = 2;
|
||||
success = false;
|
||||
// #2095 Check if this distance wasn't already realized in the input first operand.
|
||||
if (v < min_face_face_distance(a, 1.e-4)) {
|
||||
reason = 2;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,12 @@
|
||||
#include <TopTools_ListIteratorOfListOfShape.hxx>
|
||||
|
||||
#include <BRepAdaptor_CompCurve.hxx>
|
||||
|
||||
#include <Standard_Version.hxx>
|
||||
#if OCC_VERSION_HEX < 0x70600
|
||||
#include <BRepAdaptor_HCompCurve.hxx>
|
||||
#endif
|
||||
|
||||
#include <Approx_Curve3d.hxx>
|
||||
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
@@ -483,8 +488,12 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrimmedCurve* l, TopoDS_Wire&
|
||||
Logger::Warning("Approximating BasisCurve due to possible discontinuities", l);
|
||||
TopoDS_Wire w;
|
||||
if (!convert_wire(basis_curve, w)) return false;
|
||||
#if OCC_VERSION_HEX < 0x70600
|
||||
BRepAdaptor_CompCurve cc(w, true);
|
||||
Handle(Adaptor3d_HCurve) hcc = Handle(Adaptor3d_HCurve)(new BRepAdaptor_HCompCurve(cc));
|
||||
#else
|
||||
auto hcc = new BRepAdaptor_CompCurve(w, true);
|
||||
#endif
|
||||
// @todo, arbitrary numbers here, note they cannot be too high as contiguous memory is allocated based on them.
|
||||
Approx_Curve3d approx(hcc, getValue(GV_PRECISION), GeomAbs_C0, 10, 10);
|
||||
curve = approx.Curve();
|
||||
@@ -606,7 +615,10 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrimmedCurve* l, TopoDS_Wire&
|
||||
}
|
||||
}
|
||||
|
||||
if (isConic && ALMOST_THE_SAME(fmod(flts[1]-flts[0],M_PI*2.), 0., 100 * getValue(GV_PRECISION) / (2 * M_PI * radius))) {
|
||||
// @todo is 100. not too much? Check with the original issue.
|
||||
const double precision_markup = getValue(IfcGeom::Kernel::GV_PRECISION_FACTOR) == 1. ? 1. : 100.;
|
||||
|
||||
if (isConic && ALMOST_THE_SAME(fmod(flts[1]-flts[0],M_PI*2.), 0., precision_markup * getValue(GV_PRECISION) / (2 * M_PI * radius))) {
|
||||
e = BRepBuilderAPI_MakeEdge(curve).Edge();
|
||||
} else {
|
||||
BRepBuilderAPI_MakeEdge me (curve,flts[0],flts[1]);
|
||||
@@ -681,6 +693,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyline* l, TopoDS_Wire& resu
|
||||
polygon.Append(pnt);
|
||||
}
|
||||
|
||||
// @todo the strict tolerance should also govern these arbitrary precision increases
|
||||
const double eps = getValue(GV_PRECISION) * 10;
|
||||
const bool closed_by_proximity = polygon.Length() >= 3 && polygon.First().Distance(polygon.Last()) < eps;
|
||||
if (closed_by_proximity) {
|
||||
|
||||
@@ -42,6 +42,9 @@ namespace IfcGeom {
|
||||
private:
|
||||
Kernel* implementation_;
|
||||
|
||||
protected:
|
||||
Kernel() {};
|
||||
|
||||
public:
|
||||
// Tolerances and settings for various geometrical operations:
|
||||
enum GeomValue {
|
||||
@@ -80,7 +83,7 @@ namespace IfcGeom {
|
||||
GV_BOOLEAN_ATTEMPT_2D
|
||||
};
|
||||
|
||||
Kernel(IfcParse::IfcFile* file_ = 0);
|
||||
Kernel(IfcParse::IfcFile* file_);
|
||||
|
||||
virtual ~Kernel() {}
|
||||
|
||||
|
||||
@@ -22,12 +22,36 @@ import ifcopenshell
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {}
|
||||
self.settings = {"parent": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
id_attribute = "DocumentId" if self.file.schema == "IFC2X3" else "Identification"
|
||||
return self.file.create_entity(
|
||||
"IfcDocumentInformation", **{id_attribute: ifcopenshell.guid.new(), "Name": "Unnamed"}
|
||||
information = self.file.create_entity(
|
||||
"IfcDocumentInformation", **{id_attribute: "X", "Name": "Unnamed"}
|
||||
)
|
||||
parent = self.settings["parent"]
|
||||
if not parent and self.file.by_type("IfcProject"):
|
||||
parent = self.file.by_type("IfcProject")[0]
|
||||
if parent.is_a("IfcProject") or parent.is_a("IfcContext"):
|
||||
self.file.create_entity(
|
||||
"IfcRelAssociatesDocument",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
RelatingDocument=information,
|
||||
RelatedObjects=[parent],
|
||||
)
|
||||
elif parent.is_a("IfcDocumentInformation"):
|
||||
if parent.IsPointer:
|
||||
rel = parent.IsPointer[0]
|
||||
documents = set(rel.RelatedDocuments)
|
||||
documents.add(information)
|
||||
rel.RelatedDocuments = list(documents)
|
||||
else:
|
||||
self.file.create_entity(
|
||||
"IfcDocumentInformationRelationship",
|
||||
RelatingDocument=parent,
|
||||
RelatedDocuments=[information]
|
||||
)
|
||||
return information
|
||||
|
||||
@@ -16,16 +16,15 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {}
|
||||
self.settings = {"information": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
id_attribute = "ItemReference" if self.file.schema == "IFC2X3" else "Identification"
|
||||
return self.file.create_entity("IfcDocumentReference", **{id_attribute: ifcopenshell.guid.new()})
|
||||
if self.file.schema == "IFC2X3":
|
||||
return self.file.create_entity("IfcDocumentReference")
|
||||
return self.file.create_entity("IfcDocumentReference", ReferencedDocument=self.settings["information"])
|
||||
|
||||
@@ -56,7 +56,7 @@ class Usecase:
|
||||
"IfcRelAssociatesDocument",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
# TODO: owner history
|
||||
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
"RelatingDocument": self.settings["document"],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import ifcopenshell.api
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"information": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for reference in self.settings["information"].HasDocumentReferences or []:
|
||||
ifcopenshell.api.run("document.remove_reference", self.file, reference=reference)
|
||||
for rel in self.settings["information"].IsPointer or []:
|
||||
for information in rel.RelatedDocuments:
|
||||
ifcopenshell.api.run("document.remove_information", self.file, information=information)
|
||||
self.file.remove(rel)
|
||||
for rel in self.settings["information"].DocumentInfoForObjects or []:
|
||||
self.file.remove(rel)
|
||||
self.file.remove(self.settings["information"])
|
||||
+5
-6
@@ -1,5 +1,5 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
@@ -20,12 +20,11 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"document": None}
|
||||
self.settings = {"reference": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
self.file.remove(self.settings["document"])
|
||||
for rel in self.file.by_type("IfcRelAssociatesDocument"):
|
||||
if not rel.RelatingDocument:
|
||||
self.file.remove(rel)
|
||||
for rel in self.settings["reference"].DocumentRefForObjects or []:
|
||||
self.file.remove(rel)
|
||||
self.file.remove(self.settings["reference"])
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user