merge master v6.0

This commit is contained in:
admin
2021-01-11 11:39:36 +08:00
parent 699742880e
commit 555a2ce79e
40 changed files with 1189 additions and 25605 deletions
@@ -1,12 +1,70 @@
import os
from behave.model import Scenario
from logfile import create_logfile
from logfile import append_logfile
from zoom_smart_view import append_zoom_smartview
from zoom_smart_view import create_zoom_smartview
this_path = os.path.dirname(os.path.realpath(__file__))
def before_all(context):
# get from userdata
userdata = context.config.userdata
context.ifcbasename = userdata["ifcbasename"]
context.localedir = userdata.get("localedir")
# do not break after a failed scenario
# https://community.osarch.org/discussion/comment/3328/#Comment_3328
continue_after_failed = userdata.getbool("runner.continue_after_failed_step", True)
continue_after_failed = userdata.getbool(
"runner.continue_after_failed_step", True
)
Scenario.continue_after_failed_step = continue_after_failed
# keep out path
context.outpath = os.path.join(this_path, "..")
# since bimtesterfc directory in tmp is removed on every run
# neither log file nor sm file does need to be explicit removed first
# set up log file
context.thelogfile = os.path.join(
context.outpath,
context.ifcbasename + ".log"
)
create_logfile(
context.thelogfile,
context.ifcbasename,
)
# set up smart view file
context.smview_file = os.path.join(
context.outpath,
context.ifcbasename + ".bcsv"
)
create_zoom_smartview(
context.smview_file,
context.ifcbasename,
)
def after_step(context, step):
if step.status == "failed":
# append log file
append_logfile(context, step)
# extend smart view
if hasattr(context, "falseguids"):
# print(context.falseguids)
append_zoom_smartview(
context.smview_file,
step.name,
context.falseguids
)
@@ -1,36 +1,49 @@
from behave import step
from attributes_eleclasses_methods import all_element_attribs_have_a_value
from attributes_eleclasses_methods import name_has_a_value
from attributes_eleclasses_methods import description_has_a_value
from attributes_eleclasses_methods import no_element_class_ele
import attributes_eleclasses_methods as aem
from utils import assert_elements
from utils import IfcFile
from utils import switch_locale
the_lang = "en"
@step("there are no {ifc_class} elements because {reason}")
def step_impl(context, ifc_class, reason):
switch_locale(context.localedir, "en")
no_element_class_ele(context, ifc_class, reason)
switch_locale(context.localedir, the_lang)
aem.no_eleclass_because_reason(
context,
ifc_class,
reason
)
@step('all {ifc_class} elements class attributes have a value')
def step_impl(context, ifc_class):
switch_locale(context.localedir, "en")
all_element_attribs_have_a_value(context, ifc_class)
switch_locale(context.localedir, the_lang)
aem.eleclass_have_class_attributes_with_a_value(
context,
ifc_class
)
@step('all {ifc_class} elements have a name given')
def step_impl(context, ifc_class):
switch_locale(context.localedir, "en")
name_has_a_value(context, ifc_class)
switch_locale(context.localedir, the_lang)
aem.eleclass_has_name_with_a_value(
context,
ifc_class
)
@step('all {ifc_class} elements have a description given')
def step_impl(context, ifc_class):
switch_locale(context.localedir, "en")
description_has_a_value(context, ifc_class)
switch_locale(context.localedir, the_lang)
aem.eleclass_has_description_with_a_value(
context,
ifc_class
)
@step('all {ifc_class} elements have a name matching the pattern "{pattern}"')
@@ -4,7 +4,9 @@ from utils import assert_elements
from utils import IfcFile
def no_element_class_ele(context, ifc_class, reason):
def no_eleclass_because_reason(
context, ifc_class, reason
):
context.falseelems = []
context.falseguids = []
@@ -41,7 +43,9 @@ def no_element_class_ele(context, ifc_class, reason):
assert False, _("Error in falsecount, something went wrong.")
def all_element_attribs_have_a_value(context, ifc_class):
def eleclass_have_class_attributes_with_a_value(
context, ifc_class
):
from ifcopenshell.ifcopenshell_wrapper import schema_by_name
# schema = schema_by_name("IFC2X3")
@@ -84,7 +88,7 @@ def all_element_attribs_have_a_value(context, ifc_class):
)
def name_has_a_value(context, ifc_class):
def eleclass_has_name_with_a_value(context, ifc_class):
context.falseelems = []
context.falseguids = []
@@ -109,7 +113,9 @@ def name_has_a_value(context, ifc_class):
)
def description_has_a_value(context, ifc_class):
def eleclass_has_description_with_a_value(
context, ifc_class
):
context.falseelems = []
context.falseguids = []
@@ -1,15 +1,23 @@
from behave import step
from attributes_psets_methods import all_eleclass_elements_have_prop_in_pset
import attributes_psets_methods as apm
from utils import assert_elements
from utils import IfcFile
from utils import switch_locale
the_lang = "en"
@step("all {ifc_class} elements have an {aproperty} property in the {pset} pset")
def step_impl(context, ifc_class, aproperty, pset):
switch_locale(context.localedir, "en")
all_eleclass_elements_have_prop_in_pset(context, ifc_class, aproperty, pset)
switch_locale(context.localedir, the_lang)
apm.eleclass_has_property_in_pset(
context,
ifc_class,
aproperty,
pset
)
# ------------------------------------------------------------------------
@@ -4,7 +4,9 @@ from utils import assert_elements
from utils import IfcFile
def all_eleclass_elements_have_prop_in_pset(context, ifc_class, aproperty, pset):
def eleclass_has_property_in_pset(
context, ifc_class, aproperty, pset
):
context.falseelems = []
context.falseguids = []
@@ -1,11 +1,14 @@
from behave import step
from geometric_detail_methods import class_geometric_representation
import geometric_detail_methods as gdm
from utils import assert_elements
from utils import IfcFile
from utils import switch_locale
the_lang = "en"
@step("All elements must be under {number} polygons")
def step_impl(context, number):
number = int(number)
@@ -36,5 +39,9 @@ def step_impl(context, number):
@step("all {ifc_class} elements have an {representation_class} representation")
def step_impl(context, ifc_class, representation_class):
switch_locale(context.localedir, "en")
class_geometric_representation(context, ifc_class, representation_class)
switch_locale(context.localedir, the_lang)
gdm.eleclass_has_geometric_representation_of_specific_class(
context,
ifc_class,
representation_class
)
@@ -1,10 +1,17 @@
from behave import step
from geometric_detail_methods import class_geometric_representation
import geometric_detail_methods as gdm
from utils import switch_locale
the_lang = "de"
@step("Alle {ifc_class} Bauteile müssen eine geometrische Repräsentation der Klasse {representation_class} verwenden")
def step_impl(context, ifc_class, representation_class):
switch_locale(context.localedir, "de")
class_geometric_representation(context, ifc_class, representation_class)
switch_locale(context.localedir, the_lang)
gdm.eleclass_has_geometric_representation_of_specific_class(
context,
ifc_class,
representation_class
)
@@ -3,7 +3,11 @@ from utils import assert_elements
from utils import IfcFile
def class_geometric_representation(context, ifc_class, representation_class):
def eleclass_has_geometric_representation_of_specific_class(
context,
ifc_class,
representation_class
):
def is_item_a_representation(item, representation):
if "/" in representation:
@@ -6,6 +6,10 @@ from ifcdata_methods import assert_schema
from utils import IfcFile
from utils import switch_locale
the_lang = "en"
@step('The IFC schema "{schema}" must be provided')
def step_impl(context, schema):
try:
@@ -15,6 +19,7 @@ def step_impl(context, schema):
except:
assert False, f"The schema {schema} could not be loaded"
@step('The IFC file "{file}" must be provided')
def step_impl(context, file):
try:
@@ -22,6 +27,7 @@ def step_impl(context, file):
except:
assert False, f"The file {file} could not be loaded"
@given('The IFC file has been provided through an argument')
def step_impl(context):
try:
@@ -29,6 +35,7 @@ def step_impl(context):
except:
assert False, f"The IFC {context.config.userdata.get('ifcfile')} file could not be loaded"
@given('A file path has been provided through an argument')
def step_impl(context):
try:
@@ -36,9 +43,10 @@ def step_impl(context):
except:
assert False, f"The path {context.config.userdata.get('path')} could not be loaded"
@step("IFC data must use the {schema} schema")
def step_impl(context, schema):
switch_locale(context.localedir, "en")
switch_locale(context.localedir, the_lang)
assert_schema(context, schema)
@@ -3,7 +3,11 @@ from behave import step
from ifcdata_methods import assert_schema
from utils import switch_locale
the_lang = "de"
@step("Die IFC Daten müssen das {schema} Schema benutzen")
def step_impl(context, schema):
switch_locale(context.localedir, "de")
switch_locale(context.localedir, the_lang)
assert_schema(context, schema)
@@ -3,7 +3,11 @@ from behave import step
from ifcdata_methods import assert_schema
from utils import switch_locale
the_lang = "fr"
@step("Les données IFC doivent utiliser le schéma {schema}")
def step_impl(context, schema):
switch_locale(context.localedir, "fr")
switch_locale(context.localedir, the_lang)
assert_schema(context, schema)
@@ -1,5 +1,5 @@
import ifcopenshell.util.geolocation
import numpy as np
import ifcopenshell.util.placement
from behave import step
from utils import assert_number
@@ -7,31 +7,6 @@ from utils import assert_type
from utils import IfcFile
def a2p(o, z, x):
y = np.cross(z, x)
r = np.eye(4)
r[:-1, :-1] = x, y, z
r[-1, :-1] = o
return r.T
def get_axis2placement(plc):
z = np.array(plc.Axis.DirectionRatios if plc.Axis else (0, 0, 1))
x = np.array(plc.RefDirection.DirectionRatios if plc.RefDirection else (1, 0, 0))
o = plc.Location.Coordinates
return a2p(o, z, x)
def get_local_placement(plc):
if plc is None:
return np.eye(4)
if plc.PlacementRelTo is None:
parent = np.eye(4)
else:
parent = get_local_placement(plc.PlacementRelTo)
return np.dot(get_axis2placement(plc.RelativePlacement), parent)
def get_decimal_points(value):
try:
return len(value.split(".")[1])
@@ -83,7 +58,7 @@ def step_impl(context, guid, easting, northing, elevation):
element = IfcFile.by_guid(guid)
if not element.ObjectPlacement:
assert False, "The element does not have an object placement: {}".format(element)
m = get_local_placement(element.ObjectPlacement)
m = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
e, n, h = ifcopenshell.util.geolocation.xyz2enh(
m[0][3],
m[1][3],
@@ -110,7 +85,7 @@ def step_impl(context, guid, x, y, z):
element = IfcFile.by_guid(guid)
if not element.ObjectPlacement:
assert False, "The element does not have an object placement: {}".format(element)
m = get_local_placement(element.ObjectPlacement)
m = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
element_x = round(m[0][3], get_decimal_points(x))
element_y = round(m[1][3], get_decimal_points(y))
element_z = round(m[2][3], get_decimal_points(z))
+2 -2
View File
@@ -14,7 +14,7 @@ def generate_report(
html_template_file_path=""
):
# print("# Generating HTML reports now.")
print("# Generating HTML reports now.")
# get locale path
localedir = os.path.join(
@@ -150,7 +150,7 @@ def generate_report(
def get_html_template_strings():
print(_("OpenBIM auditing is a feature of"))
return {
"tr_lang": _("en"),
"tr_success": _("Success"),
+38 -36
View File
@@ -144,13 +144,13 @@ def run_intmp_tests(args={}):
)
return False
# get the features_path, the feature files where the tests are in
# get the features_path, the dir where the feature files to test are in
if "features" in args and args["features"] != "":
features_path = os.path.join(args["features"], "features")
if not os.path.isdir(features_path):
the_features_path = os.path.join(args["features"], "features")
if not os.path.isdir(the_features_path):
print(
"The features directory does not exist: {}"
.format(features_path)
.format(the_features_path)
)
return False
else:
@@ -191,23 +191,47 @@ def run_intmp_tests(args={}):
os.mkdir(run_path)
report_path = os.path.join(run_path, "report")
copy_features_path = os.path.join(run_path, "features")
copy_steps_path = os.path.join(copy_features_path, "steps")
# copy features path from bimtester source code
srccode_features_path = os.path.join(
bimtester_path,
"features",
)
# print(srccode_features_path)
if os.path.exists(srccode_features_path):
shutil.copytree(srccode_features_path, copy_features_path)
else:
print(
"Bimtester source code features directory {} not found."
.format(srccode_features_path)
)
return False
# copy features files
# print(features_path)
# print(the_features_path)
# print(copy_features_path)
if os.path.exists(features_path):
shutil.copytree(features_path, copy_features_path)
# parameter dirs_exist_ok=True, from py 3.8
# if os.path.exists(the_features_path):
# shutil.copytree(
# the_features_path,
# copy_features_path,
# dirs_exist_ok=True
# )
# replace ifcpath in feature files
# IMHO better than copy the ifc file which could be 500 MB
feature_files = os.listdir(copy_features_path)
# copy feature files and replace ifcpath in feature files
# replaceing is IMHO better than copy the ifc file which could be 500 MB
feature_files = os.listdir(the_features_path)
# print(feature_files)
for feature_file in feature_files:
feature_file = os.path.join(copy_features_path, feature_file)
cp_feature_file = os.path.join(copy_features_path, feature_file)
# print(feature_file)
# copy file
shutil.copyfile(
os.path.join(the_features_path, feature_file),
cp_feature_file
)
# search the line
ff = open(feature_file, "r")
ff = open(cp_feature_file, "r")
lines = ff.readlines()
ff.close()
theline = ""
@@ -228,32 +252,10 @@ def run_intmp_tests(args={}):
# replace the line
if newifcline != "":
# https://stackoverflow.com/a/290494
for line in fileinput.input(feature_file, inplace=True):
for line in fileinput.input(cp_feature_file, inplace=True):
# the print replaces the line in the file
print(line.replace(theline, newifcline), end="")
# copy step files and environment file
steps_path = os.path.join(
bimtester_path,
"features",
"steps"
)
# print(steps_path)
# print(copy_steps_path)
if os.path.exists(steps_path):
shutil.copytree(steps_path, copy_steps_path)
environment_file = os.path.join(
bimtester_path,
"features",
"environment.py"
)
if os.path.isfile(environment_file):
shutil.copyfile(
environment_file,
os.path.join(copy_features_path, "environment.py")
)
# get advanced args
# print to console from inside step files, add "--no-capture" flag
# https://github.com/behave/behave/issues/346
+55 -51
View File
@@ -5,15 +5,25 @@ bpy = sys.modules.get("bpy")
if bpy is not None:
import bpy
import blenderbim.bim.module.covetool as module_covetool
import blenderbim.bim.module.model as module_model
import blenderbim.bim.module.root as module_root
import blenderbim.bim.module.aggregate as module_aggregate
import blenderbim.bim.module.attribute as module_attribute
import blenderbim.bim.module.bcf as module_bcf
import blenderbim.bim.module.context as module_context
import blenderbim.bim.module.covetool as module_covetool
import blenderbim.bim.module.debug as module_debug
import blenderbim.bim.module.geometry as module_geometry
import blenderbim.bim.module.model as module_model
import blenderbim.bim.module.owner as module_owner
import blenderbim.bim.module.project as module_project
import blenderbim.bim.module.pset as module_pset
import blenderbim.bim.module.spatial as module_spatial
import blenderbim.bim.module.style as module_style
import blenderbim.bim.module.type as module_type
import blenderbim.bim.module.unit as module_unit
from . import ui, prop, operator
classes = [
operator.ReassignClass,
operator.AssignClass,
operator.UnassignClass,
operator.SelectClass,
operator.SelectType,
operator.OpenUri,
@@ -27,7 +37,6 @@ if bpy is not None:
operator.ValidateIfcFile,
operator.ExportIFC,
operator.ImportIFC,
operator.ProfileImportIFC,
operator.ColourByClass,
operator.ColourByAttribute,
operator.ColourByPset,
@@ -45,8 +54,6 @@ if bpy is not None:
operator.SelectSweptSolidInnerCurves,
operator.AssignSweptSolidExtrusion,
operator.SelectSweptSolidExtrusion,
operator.AddPset,
operator.RemovePset,
operator.AddQto,
operator.RemoveQto,
operator.AddMaterialPset,
@@ -65,18 +72,6 @@ if bpy is not None:
operator.AssignConstraint,
operator.UnassignConstraint,
operator.RemoveObjectConstraint,
operator.AddPerson,
operator.RemovePerson,
operator.AddPersonRole,
operator.RemovePersonRole,
operator.AddPersonAddress,
operator.RemovePersonAddress,
operator.AddOrganisation,
operator.RemoveOrganisation,
operator.AddOrganisationRole,
operator.RemoveOrganisationRole,
operator.AddOrganisationAddress,
operator.RemoveOrganisationAddress,
operator.AddDocumentInformation,
operator.RemoveDocumentInformation,
operator.AssignDocumentInformation,
@@ -86,11 +81,8 @@ if bpy is not None:
operator.UnassignDocumentReference,
operator.RemoveObjectDocumentReference,
operator.GenerateGlobalId,
operator.AddAttribute,
operator.RemoveAttribute,
operator.AddMaterialAttribute,
operator.RemoveMaterialAttribute,
operator.QuickProjectSetup,
operator.SelectGlobalId,
operator.SelectAttribute,
operator.SelectPset,
@@ -107,8 +99,6 @@ if bpy is not None:
operator.FetchLibraryInformation,
operator.FetchExternalMaterial,
operator.FetchObjectPassport,
operator.AddSubcontext,
operator.RemoveSubcontext,
operator.CutSection,
operator.AddSheet,
operator.OpenSheet,
@@ -133,8 +123,6 @@ if bpy is not None:
operator.SmartClashGroup,
operator.SelectSmartGroup,
operator.LoadSmartGroupsForActiveClashSet,
operator.SwitchContext,
operator.RemoveContext,
operator.OpenUpstream,
operator.BIM_OT_ChangeClassificationLevel,
operator.AddPropertySetTemplate,
@@ -151,7 +139,6 @@ if bpy is not None:
operator.ImportIfcCsv,
operator.EyedropIfcCsv,
operator.ReloadIfcFile,
operator.SelectSimilarType,
operator.AddIfcFile,
operator.RemoveIfcFile,
operator.SelectDocIfcFile,
@@ -161,7 +148,6 @@ if bpy is not None:
operator.AddVariable,
operator.RemoveVariable,
operator.PropagateTextData,
operator.PushRepresentation,
operator.ConvertLocalToGlobal,
operator.ConvertGlobalToLocal,
operator.GuessQuantity,
@@ -203,15 +189,7 @@ if bpy is not None:
operator.RemoveDrawingStyleAttribute,
operator.CopyPropertyToSelection,
operator.CopyAttributeToSelection,
operator.CreateShapeFromStepId,
operator.SelectHighPolygonMeshes,
operator.InspectFromStepId,
operator.InspectFromObject,
operator.RewindInspector,
operator.RefreshDrawingList,
operator.GetRepresentationIfcParameters,
operator.BakeParametricGeometry,
operator.UpdateIfcRepresentation,
operator.SetBlenderClashSetA,
operator.SetBlenderClashSetB,
operator.ExecuteBlenderClash,
@@ -245,11 +223,8 @@ if bpy is not None:
prop.Schedule,
prop.DrawingStyle,
prop.Sheet,
prop.Subcontext,
prop.Representation,
prop.PresentationLayer,
prop.BIMProperties,
prop.BIMDebugProperties,
prop.DocProperties,
prop.BIMLibrary,
prop.MapConversion,
@@ -278,10 +253,6 @@ if bpy is not None:
ui.BIM_PT_search,
ui.BIM_PT_ifccsv,
ui.BIM_PT_ifcclash,
ui.BIM_PT_owner,
ui.BIM_PT_people,
ui.BIM_PT_organisations,
ui.BIM_PT_context,
ui.BIM_PT_qa,
ui.BIM_PT_library,
ui.BIM_PT_gis,
@@ -290,15 +261,10 @@ if bpy is not None:
ui.BIM_PT_cobie,
ui.BIM_PT_patch,
ui.BIM_PT_mvd,
ui.BIM_PT_debug,
ui.BIM_PT_material,
ui.BIM_PT_mesh,
ui.BIM_PT_presentation_layer_data,
ui.BIM_PT_object,
ui.BIM_PT_object_material,
ui.BIM_PT_object_psets,
ui.BIM_PT_object_qto,
ui.BIM_PT_representations,
ui.BIM_PT_classification_references,
ui.BIM_PT_documents,
ui.BIM_PT_constraint_relations,
@@ -322,9 +288,22 @@ if bpy is not None:
ui.BIM_ADDON_preferences,
]
classes.extend(module_root.classes)
classes.extend(module_aggregate.classes)
classes.extend(module_attribute.classes)
classes.extend(module_bcf.classes)
classes.extend(module_context.classes)
classes.extend(module_covetool.classes)
classes.extend(module_debug.classes)
classes.extend(module_geometry.classes)
classes.extend(module_model.classes)
classes.extend(module_owner.classes)
classes.extend(module_project.classes)
classes.extend(module_pset.classes)
classes.extend(module_spatial.classes)
classes.extend(module_style.classes)
classes.extend(module_type.classes)
classes.extend(module_unit.classes)
def menu_func_export(self, context):
self.layout.operator(operator.ExportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcjson)")
@@ -344,7 +323,6 @@ if bpy is not None:
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
bpy.types.Scene.BIMDebugProperties = bpy.props.PointerProperty(type=prop.BIMDebugProperties)
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
bpy.types.Scene.BIMLibrary = bpy.props.PointerProperty(type=prop.BIMLibrary)
bpy.types.Scene.MapConversion = bpy.props.PointerProperty(type=prop.MapConversion)
@@ -356,10 +334,24 @@ if bpy is not None:
bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
module_root.register()
module_aggregate.register()
module_attribute.register()
module_bcf.register()
module_context.register()
module_covetool.register()
module_debug.register()
module_geometry.register()
module_model.register()
module_owner.register()
module_project.register()
module_pset.register()
module_spatial.register()
module_style.register()
module_type.register()
module_unit.register()
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
bpy.app.handlers.load_post.append(prop.toggleDecorationsOnLoad)
def unregister():
for cls in reversed(classes):
@@ -368,7 +360,6 @@ if bpy is not None:
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
del bpy.types.Scene.BIMProperties
del bpy.types.Scene.BIMDebugProperties
del bpy.types.Scene.DocProperties
del bpy.types.Scene.MapConversion
del bpy.types.Scene.TargetCRS
@@ -379,7 +370,20 @@ if bpy is not None:
del bpy.types.Camera.BIMCameraProperties
del bpy.types.TextCurve.BIMTextProperties
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
module_unit.unregister()
module_type.unregister()
module_style.unregister()
module_spatial.unregister()
module_pset.unregister()
module_project.unregister()
module_owner.unregister()
module_model.unregister()
module_geometry.unregister()
module_debug.unregister()
module_covetool.unregister()
module_context.unregister()
module_bcf.unregister()
module_attribute.register()
module_aggregate.register()
module_root.unregister()
bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler)
+128 -126
View File
@@ -21,7 +21,6 @@ class BaseDecorator():
DEF_GLSL = """
#define PI 3.141592653589793
#define COLOR vec4({color[0]}, {color[1]}, {color[2]}, {color[3]})
#define MAX_POINTS 64
#define CIRCLE_SEGS 24
"""
@@ -99,63 +98,28 @@ class BaseDecorator():
"""
FRAG_GLSL = """
uniform vec4 color;
out vec4 fragColor;
void main() {
fragColor = COLOR;
fragColor = color;
}
"""
# class var for single handler
installed = None
@classmethod
def install(cls, *args, **kwargs):
if cls.installed:
cls.uninstall()
handler = cls(*args, **kwargs)
cls.installed = SpaceView3D.draw_handler_add(handler, (), 'WINDOW', 'POST_PIXEL')
@classmethod
def uninstall(cls):
try:
SpaceView3D.draw_handler_remove(cls.installed, 'WINDOW')
except ValueError:
pass
cls.installed = None
def __init__(self, props, context):
self.context = context
self.props = props
self.shader = self.create_shader()
self.font_id = 0
self.font_size = 16
self.dpi = context.preferences.system.dpi
def create_shader(self):
defines = self.DEF_GLSL.format(color=self.props.decorations_colour)
def __init__(self):
# NB: libcode param doesn't work
return GPUShader(vertexcode=self.VERT_GLSL,
fragcode=self.FRAG_GLSL,
geocode=self.LIB_GLSL + self.GEOM_GLSL,
defines=defines)
self.shader = GPUShader(vertexcode=self.VERT_GLSL,
fragcode=self.FRAG_GLSL,
geocode=self.LIB_GLSL + self.GEOM_GLSL,
defines=self.DEF_GLSL)
def __call__(self):
if self.props.active_drawing_index is None or len(self.props.drawings) == 0:
return
for obj in self.get_objects():
self.decorate(obj)
def get_objects(self):
def get_objects(self, collection):
"""find relevant objects
using class.basename
returns: iterable of blender objects
"""
drawing = self.props.drawings[self.props.active_drawing_index]
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
return filter(lambda o: self.basename in o.name, collection.objects)
return filter(lambda o: self.basename in o.name, collection.all_objects)
def get_path_geom(self, obj, topo=True):
"""Parses path geometry into line segments
@@ -223,13 +187,14 @@ class BaseDecorator():
return vertices, indices
def decorate(self, object):
def decorate(self, context, object):
"""perform actuall drawing stuff"""
raise NotImplementedError()
def draw_lines(self, obj, vertices, indices, topology=None):
region = self.context.region
region3d = self.context.region_data
def draw_lines(self, context, obj, vertices, indices, topology=None):
region = context.region
region3d = context.region_data
color = context.scene.DocProperties.decorations_colour
fmt = GPUVertFormat()
fmt.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
@@ -245,13 +210,20 @@ class BaseDecorator():
batch = GPUBatch(type='LINES', buf=vbo, elem=ibo)
bgl.glEnable(bgl.GL_LINE_SMOOTH)
bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST)
bgl.glEnable(bgl.GL_BLEND)
bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
self.shader.bind()
self.shader.uniform_float
self.shader.uniform_float("viewMatrix", region3d.perspective_matrix)
self.shader.uniform_float("winsize", (region.width, region.height))
self.shader.uniform_float("color", color)
batch.draw(self.shader)
def draw_label(self, text, pos, dir, gap=4, center=True, vcenter=False):
def draw_label(self, context, text, pos, dir, gap=4, center=True, vcenter=False):
"""Draw text label
Args:
@@ -259,15 +231,21 @@ class BaseDecorator():
aligned and centered at segment middle
"""
font_id = 0
font_size = 16
dpi = context.preferences.system.dpi
color = context.scene.DocProperties.decorations_colour
ang = -Vector((1, 0)).angle_signed(dir)
cos = math.cos(ang)
sin = math.sin(ang)
blf.size(self.font_id, self.font_size, self.dpi)
blf.size(font_id, font_size, dpi)
w, h = 0, 0
if center or vcenter:
w, h = blf.dimensions(self.font_id, text)
w, h = blf.dimensions(font_id, text)
if center:
# horizontal centering
@@ -281,18 +259,18 @@ class BaseDecorator():
# side-shifting
pos += Vector((-sin, cos)) * gap
blf.enable(self.font_id, blf.ROTATION)
blf.position(self.font_id, pos.x, pos.y, 0)
blf.enable(font_id, blf.ROTATION)
blf.position(font_id, pos.x, pos.y, 0)
blf.rotation(self.font_id, ang)
blf.color(self.font_id, *self.props.decorations_colour)
blf.draw(self.font_id, text)
blf.disable(self.font_id, blf.ROTATION)
blf.rotation(font_id, ang)
blf.color(font_id, *color)
blf.draw(font_id, text)
blf.disable(font_id, blf.ROTATION)
def format_value(self, value):
unit_system = bpy.context.scene.unit_settings.system
def format_value(self, context, value):
unit_system = context.scene.unit_settings.system
if unit_system == 'IMPERIAL':
precision = bpy.context.scene.BIMProperties.imperial_precision
precision = context.scene.BIMProperties.imperial_precision
if precision == "NONE":
precision = 256
elif precision == "1":
@@ -313,7 +291,6 @@ class DimensionDecorator(BaseDecorator):
- puts metric text next to each segment
"""
basename = "IfcAnnotation/Dimension"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define ARROW_ANGLE PI / 12.0
@@ -377,14 +354,14 @@ class DimensionDecorator(BaseDecorator):
}
"""
def decorate(self, obj):
def decorate(self, context, obj):
verts, idxs, _ = self.get_path_geom(obj, topo=False)
self.draw_lines(obj, verts, idxs)
self.draw_labels(obj, verts, idxs)
self.draw_lines(context, obj, verts, idxs)
self.draw_labels(context, obj, verts, idxs)
def draw_labels(self, obj, vertices, indices):
region = self.context.region
region3d = self.context.region_data
def draw_labels(self, context, obj, vertices, indices):
region = context.region
region3d = context.region_data
for i0, i1 in indices:
v0 = Vector(vertices[i0])
v1 = Vector(vertices[i1])
@@ -394,8 +371,8 @@ class DimensionDecorator(BaseDecorator):
if dir.length < 1:
continue
length = (v1 - v0).length
text = self.format_value(length)
self.draw_label(text, p0 + (dir) * .5, dir)
text = self.format_value(context, length)
self.draw_label(context, text, p0 + (dir) * .5, dir)
class EqualityDecorator(DimensionDecorator):
@@ -405,11 +382,10 @@ class EqualityDecorator(DimensionDecorator):
- puts 'EQ' label
"""
basename = "IfcAnnotation/Equal"
installed = None
def draw_labels(self, obj, vertices, indices):
region = self.context.region
region3d = self.context.region_data
def draw_labels(self, context, obj, vertices, indices):
region = context.region
region3d = context.region_data
for i0, i1 in indices:
v0 = Vector(vertices[i0])
v1 = Vector(vertices[i1])
@@ -418,7 +394,7 @@ class EqualityDecorator(DimensionDecorator):
dir = p1 - p0
if dir.length < 1:
continue
self.draw_label("EQ", p0 + (dir) * .5, dir)
self.draw_label(context, "EQ", p0 + (dir) * .5, dir)
class LeaderDecorator(BaseDecorator):
@@ -427,7 +403,6 @@ class LeaderDecorator(BaseDecorator):
- middle points w/out decorations
"""
basename = "IfcAnnotation/Leader"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define ARROW_ANGLE PI / 12.0
@@ -484,9 +459,9 @@ class LeaderDecorator(BaseDecorator):
}
"""
def decorate(self, obj):
def decorate(self, context, obj):
verts, idxs, topo = self.get_path_geom(obj)
self.draw_lines(obj, verts, idxs, topo)
self.draw_lines(context, obj, verts, idxs, topo)
class StairDecorator(BaseDecorator):
@@ -496,7 +471,6 @@ class StairDecorator(BaseDecorator):
- middle points w/out decorations
"""
basename = "IfcAnnotation/Stair"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CIRCLE_SIZE 8.0
@@ -570,14 +544,13 @@ class StairDecorator(BaseDecorator):
}
"""
def decorate(self, obj):
def decorate(self, context, obj):
verts, idxs, topo = self.get_path_geom(obj)
self.draw_lines(obj, verts, idxs, topo)
self.draw_lines(context, obj, verts, idxs, topo)
class HiddenDecorator(BaseDecorator):
basename = "IfcAnnotation/Hidden"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define DASH_SIZE 16.0
@@ -619,6 +592,7 @@ class HiddenDecorator(BaseDecorator):
"""
FRAG_GLSL = """
uniform vec4 color;
in vec2 gl_FragCoord;
in float dist;
out vec4 fragColor;
@@ -626,21 +600,20 @@ class HiddenDecorator(BaseDecorator):
void main() {
uint bit = uint(fract(dist / DASH_SIZE) * 32);
if ((DASH_PATTERN & (1U<<bit)) == 0U) discard;
fragColor = COLOR;
fragColor = color;
}
"""
def decorate(self, obj):
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)
self.draw_lines(obj, verts, idxs)
self.draw_lines(context, obj, verts, idxs)
class MiscDecorator(HiddenDecorator):
basename = "IfcAnnotation/Misc"
installed = None
FRAG_GLSL = BaseDecorator.FRAG_GLSL
@@ -661,16 +634,15 @@ class LevelDecorator(BaseDecorator):
continue
yield [obj.matrix_world @ p.co for p in spline_points]
def decorate(self, obj):
def decorate(self, context, obj):
verts, idxs, topo = self.get_path_geom(obj)
self.draw_lines(obj, verts, idxs, topo)
self.draw_lines(context, obj, verts, idxs, topo)
splines = self.get_splines(obj)
self.draw_labels(obj, splines)
self.draw_labels(context, obj, splines)
class PlanDecorator(LevelDecorator):
basename = "IfcAnnotation/Plan Level"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CIRCLE_SIZE 8.0
@@ -736,9 +708,9 @@ class PlanDecorator(LevelDecorator):
}
"""
def draw_labels(self, obj, splines):
region = self.context.region
region3d = self.context.region_data
def draw_labels(self, context, obj, splines):
region = context.region
region3d = context.region_data
for verts in splines:
v0 = verts[0]
v1 = verts[1]
@@ -747,13 +719,12 @@ class PlanDecorator(LevelDecorator):
dir = p1 - p0
if dir.length < 1:
continue
text = "RL " + self.format_value(verts[-1].z)
self.draw_label(text, p0, dir, gap=8, center=False)
text = "RL " + self.format_value(context, verts[-1].z)
self.draw_label(context, text, p0, dir, gap=8, center=False)
class SectionDecorator(LevelDecorator):
basename = "IfcAnnotation/Section Level"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CALLOUT_GAP 8.0
@@ -816,9 +787,9 @@ class SectionDecorator(LevelDecorator):
}
"""
def draw_labels(self, obj, splines):
region = self.context.region
region3d = self.context.region_data
def draw_labels(self, context, obj, splines):
region = context.region
region3d = context.region_data
for verts in splines:
v0 = verts[0]
v1 = verts[1]
@@ -827,8 +798,8 @@ class SectionDecorator(LevelDecorator):
dir = p1 - p0
if dir.length < 1:
continue
text = "RL " + self.format_value(verts[-1].z)
self.draw_label(text, p0 + dir.normalized() * 16, -dir, gap=16, center=False)
text = "RL " + self.format_value(context, verts[-1].z)
self.draw_label(context, text, p0 + dir.normalized() * 16, -dir, gap=16, center=False)
class BreakDecorator(BaseDecorator):
@@ -838,7 +809,6 @@ class BreakDecorator(BaseDecorator):
Uses first two vertices in verts list.
"""
basename = "IfcAnnotation/Break"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define BREAK_LENGTH 32.0
@@ -893,12 +863,12 @@ class BreakDecorator(BaseDecorator):
}
"""
def decorate(self, obj):
def decorate(self, context, obj):
if obj.data.is_editmode:
verts = self.get_editmesh_geom(obj)
else:
verts = self.get_mesh_geom(obj)
self.draw_lines(obj, verts, [(0, 1)])
self.draw_lines(context, obj, verts, [(0, 1)])
def get_mesh_geom(self, obj):
# first vertices only
@@ -914,7 +884,6 @@ class BreakDecorator(BaseDecorator):
class GridDecorator(BaseDecorator):
basename = "IfcGridAxis/"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CIRCLE_SIZE 16.0
@@ -983,6 +952,7 @@ class GridDecorator(BaseDecorator):
"""
FRAG_GLSL = """
uniform vec4 color;
in vec2 gl_FragCoord;
in float dist;
out vec4 fragColor;
@@ -990,7 +960,7 @@ class GridDecorator(BaseDecorator):
void main() {
uint bit = uint(fract(dist / DASH_SIZE) * 32);
if ((DASH_PATTERN & (1U<<bit)) == 0U) discard;
fragColor = COLOR;
fragColor = color;
}
"""
@@ -1005,36 +975,68 @@ class GridDecorator(BaseDecorator):
vertices = [obj.matrix_world @ v.co for v in mesh.edges[0].verts]
return vertices
def decorate(self, obj):
def decorate(self, context, obj):
if obj.data.is_editmode:
verts = self.get_editmesh_geom(obj)
else:
verts = self.get_mesh_geom(obj)
self.draw_lines(obj, verts, [(0, 1)])
self.draw_labels(obj, verts)
self.draw_lines(context, obj, verts, [(0, 1)])
self.draw_labels(context, obj, verts)
def draw_labels(self, obj, vertices):
region = self.context.region
region3d = self.context.region_data
def draw_labels(self, context, obj, vertices):
region = context.region
region3d = context.region_data
v0 = Vector(vertices[0])
v1 = Vector(vertices[1])
p0 = location_3d_to_region_2d(region, region3d, v0)
p1 = location_3d_to_region_2d(region, region3d, v1)
dir = Vector((1, 0))
text = obj.BIMObjectProperties.attributes['AxisTag'].string_value
self.draw_label(text, p0, dir, vcenter=True, gap=0)
self.draw_label(text, p1, dir, vcenter=True, gap=0)
self.draw_label(context, text, p0, dir, vcenter=True, gap=0)
self.draw_label(context, text, p1, dir, vcenter=True, gap=0)
all_decorators = [
DimensionDecorator,
EqualityDecorator,
GridDecorator,
HiddenDecorator,
LeaderDecorator,
MiscDecorator,
PlanDecorator,
SectionDecorator,
StairDecorator,
BreakDecorator
]
class DecorationsHandler():
decorators_classes = [
DimensionDecorator,
EqualityDecorator,
GridDecorator,
HiddenDecorator,
LeaderDecorator,
MiscDecorator,
PlanDecorator,
SectionDecorator,
StairDecorator,
BreakDecorator
]
installed = None
@classmethod
def install(cls, context):
if cls.installed:
cls.uninstall()
handler = cls()
cls.installed = SpaceView3D.draw_handler_add(handler, (context,), 'WINDOW', 'POST_PIXEL')
@classmethod
def uninstall(cls):
try:
SpaceView3D.draw_handler_remove(cls.installed, 'WINDOW')
except ValueError:
pass
cls.installed = None
def __init__(self):
self.decorators = [cls() for cls in self.decorators_classes]
def __call__(self, context):
props = context.scene.DocProperties
if props.active_drawing_index is None or len(props.drawings) == 0:
return
drawing = props.drawings[props.active_drawing_index]
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
for decorator in self.decorators:
for obj in decorator.get_objects(collection):
decorator.decorate(context, obj)
+70 -100
View File
@@ -8,7 +8,6 @@ import os
import zipfile
import tempfile
import ifcopenshell
import ifcopenshell.util.pset
import ifcopenshell.util.schema
from pathlib import Path
from mathutils import Vector, Matrix
@@ -60,7 +59,6 @@ class IfcParser:
self.rel_contained_in_spatial_structure = {}
self.rel_nests = {}
self.rel_space_boundaries = {}
self.rel_defines_by_type = {}
self.rel_defines_by_qto = {}
self.rel_defines_by_pset = {}
self.rel_associates_document_object = {}
@@ -372,9 +370,6 @@ class IfcParser:
product.update(metadata_override)
type_product = obj.BIMObjectProperties.relating_type
if type_product and self.is_a_type(self.get_ifc_class(type_product.name)):
reference = self.get_type_product_reference(type_product.name)
self.rel_defines_by_type.setdefault(reference, []).append(self.product_index)
if product["has_boundary_condition"]:
product["boundary_condition_class"] = obj.BIMObjectProperties.boundary_condition.name
@@ -472,39 +467,28 @@ class IfcParser:
continue
results[item_key] = {"ifc": None, "raw": raw, "material": material, "attributes": {"Name": item.name}}
def add_automatic_qtos(self, ifc_class, obj):
def add_automatic_qtos(self, ifc_class: str, obj):
if not obj.data:
return
qto_names = self.get_applicable_qtos(ifc_class)
for name in qto_names:
if name not in ifcopenshell.util.pset.qtos:
continue
applicable_qtos = schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
for applicable_qto in applicable_qtos:
has_automatic_value = False
props = ifcopenshell.util.pset.qtos[name]["HasPropertyTemplates"].keys()
guessed_values = {}
for prop_name in props:
value = self.qto_calculator.guess_quantity(prop_name, props, obj)
prop_names = [p.Name for p in applicable_qto.HasPropertyTemplates]
for prop_name in prop_names:
value = self.qto_calculator.guess_quantity(prop_name, prop_names, obj)
if value:
guessed_values[prop_name] = value
has_automatic_value = True
if has_automatic_value:
qto = obj.BIMObjectProperties.qtos.add()
qto.name = name
for prop_name in props:
qto.name = applicable_qto.Name
for prop_name in prop_names:
prop = qto.properties.add()
prop.name = prop_name
if prop_name in guessed_values:
prop.string_value = str(guessed_values[prop_name])
def get_applicable_qtos(self, ifc_class):
results = []
empty = ifcopenshell.file(schema=self.ifc_export_settings.schema)
element = empty.create_entity(ifc_class)
for ifc_class, qto_names in schema.ifc.applicable_qtos.items():
if element.is_a(ifc_class):
results.extend(qto_names)
return results
def get_product_relating_structure(self, product, obj):
relating_structure = obj.BIMObjectProperties.relating_structure
if relating_structure:
@@ -1057,29 +1041,32 @@ class IfcParser:
for context in self.ifc_export_settings.context_tree:
for subcontext in context["subcontexts"]:
for target_view in subcontext["target_views"]:
rep_context = self.get_obj_representation_context(obj, context["name"], subcontext["name"], target_view)
if rep_context:
self.append_representation_in_context(obj, rep_context, name)
representation = self.get_shape_representation(obj, context["name"], subcontext["name"], target_view)
if representation:
self.append_representation_in_context(obj, representation, name)
def get_obj_representation_context(self, obj, context, subcontext, target_view):
for c in obj.BIMObjectProperties.representation_contexts:
if c.context == context and c.name == subcontext and c.target_view == target_view:
return c
if obj.BIMObjectProperties.representation_contexts:
def get_shape_representation(self, obj, context, subcontext, target_view):
for representation in obj.BIMObjectProperties.representations:
c = self.stored_file.by_id(representation.ifc_definition_id)
if c.ContextType == context and c.ContextIdentifier == subcontext and c.TargetView == target_view:
return representation
if obj.BIMObjectProperties.representations:
return
if context == "Model" and subcontext == "Body" and target_view == "MODEL_VIEW":
representation_context = obj.BIMObjectProperties.representation_contexts.add()
representation_context.context = "Model"
representation_context.name = "Body"
representation_context.target_view = "MODEL_VIEW"
return representation_context
# TODO: reimplement - see bug #1222
#if context == "Model" and subcontext == "Body" and target_view == "MODEL_VIEW":
# representation_context = obj.BIMObjectProperties.representation_contexts.add()
# representation_context.context = "Model"
# representation_context.name = "Body"
# representation_context.target_view = "MODEL_VIEW"
# return representation_context
def append_representation_in_context(self, obj, rep_context, name):
context = rep_context.context
subcontext = rep_context.name
target_view = rep_context.target_view
def append_representation_in_context(self, obj, shape_representation, name):
context_of_items = self.stored_file.by_id(shape_representation.ifc_definition_id).ContextOfItems
context = context_of_items.ContextType
subcontext = context_of_items.ContextIdentifier
target_view = context_of_items.TargetView
if self.ifc_export_settings.should_roundtrip_native and rep_context.ifc_definition_id:
if self.ifc_export_settings.should_roundtrip_native and shape_representation.ifc_definition_id:
self.representations[
"{}/{}/{}/{}".format(context, subcontext, target_view, name)
] = self.get_representation(obj.data, obj, context, subcontext, target_view)
@@ -1088,7 +1075,6 @@ class IfcParser:
context_prefix = "/".join([context, subcontext, target_view])
mesh_name = "/".join([context_prefix, name])
mesh = self.search_for_mesh_or_curve_data(mesh_name)
# TODO: if the search result is empty, we should check for ifc_definition_id
if mesh:
self.representations[mesh_name] = self.get_representation(mesh, obj, context, subcontext, target_view)
if "Model/Box/MODEL_VIEW" in self.generated_subcontexts and context_prefix == "Model/Body/MODEL_VIEW":
@@ -1113,7 +1099,7 @@ class IfcParser:
return data
def get_representation(self, mesh, obj, context, subcontext, target_view):
rep_context = self.get_obj_representation_context(obj, context, subcontext, target_view)
representation = self.get_shape_representation(obj, context, subcontext, target_view)
return {
"ifc": None,
"raw": mesh,
@@ -1121,9 +1107,9 @@ class IfcParser:
"context": context,
"subcontext": subcontext,
"target_view": target_view,
"has_ifc_definition": rep_context and rep_context.ifc_definition_id,
"has_ifc_definition": representation and representation.ifc_definition_id,
"ifc_definition": mesh.BIMMeshProperties.ifc_definition if hasattr(mesh, "BIMMeshProperties") else None,
"ifc_definition_id": rep_context.ifc_definition_id if rep_context else 0
"ifc_definition_id": representation.ifc_definition_id if representation else 0
if hasattr(mesh, "BIMMeshProperties")
else None,
"is_parametric": mesh.BIMMeshProperties.is_parametric if hasattr(mesh, "BIMMeshProperties") else False,
@@ -1345,8 +1331,9 @@ class IfcExporter:
self.roundtrip_id_new_to_old = {}
def export(self, selected_objects):
self.file = ifc.IfcStore.get_file()
if self.file and self.ifc_export_settings.should_export_from_memory:
self.stored_file = ifc.IfcStore.get_file() # See bug #1222
if self.stored_file and self.ifc_export_settings.should_export_from_memory:
self.file = self.stored_file
return self.write_ifc_file()
self.schema_version = self.ifc_export_settings.schema
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.schema_version)
@@ -1383,7 +1370,6 @@ class IfcExporter:
self.relate_objects_to_objects()
self.relate_elements_to_spatial_structures()
self.relate_nested_elements_to_hosted_elements()
self.relate_objects_to_types()
self.relate_objects_to_qtos()
self.relate_objects_to_psets()
self.relate_objects_to_opening_elements()
@@ -1423,15 +1409,16 @@ class IfcExporter:
self.file.wrapped_data.header.file_name.originating_system = "{} {}".format(
self.get_application_name(), self.get_application_version()
)
if self.owner_history:
if self.schema_version == "IFC2X3":
self.file.wrapped_data.header.file_name.authorization = self.owner_history.OwningUser.ThePerson.Id
else:
self.file.wrapped_data.header.file_name.authorization = (
self.owner_history.OwningUser.ThePerson.Identification
)
else:
self.file.wrapped_data.header.file_name.authorization = "Nobody"
# TODO: reimplement. See #1222.
#if self.owner_history:
# if self.schema_version == "IFC2X3":
# self.file.wrapped_data.header.file_name.authorization = self.owner_history.OwningUser.ThePerson.Id
# else:
# self.file.wrapped_data.header.file_name.authorization = (
# self.owner_history.OwningUser.ThePerson.Identification
# )
#else:
# self.file.wrapped_data.header.file_name.authorization = "Nobody"
def get_application_name(self):
return "BlenderBIM"
@@ -1699,13 +1686,15 @@ class IfcExporter:
pset["ifc"] = self.file.create_entity("IfcMaterialProperties", **pset["attributes"])
def create_qto_properties(self, qto):
if qto["attributes"]["Name"] in ifcopenshell.util.pset.qtos:
return self.create_templated_qto_properties(qto)
qto_template = schema.ifc.psetqto.get_by_name(qto["attributes"]["Name"])
if qto_template:
return self.create_templated_qto_properties(qto, qto_template)
return self.create_custom_qto_properties(qto)
def create_pset_properties(self, pset):
if pset["attributes"]["Name"] in ifcopenshell.util.pset.psets:
return self.create_templated_pset_properties(pset)
pset_template = schema.ifc.psetqto.get_by_name(pset["attributes"]["Name"])
if pset_template:
return self.create_templated_pset_properties(pset, pset_template)
return self.create_custom_pset_properties(pset)
def create_custom_pset_properties(self, pset):
@@ -1735,15 +1724,15 @@ class IfcExporter:
)
return properties
def create_templated_pset_properties(self, pset):
def create_templated_pset_properties(self, pset, pset_template):
properties = []
templates = ifcopenshell.util.pset.psets[pset["attributes"]["Name"]]["HasPropertyTemplates"]
for name, data in templates.items():
for prop in pset_template.HasPropertyTemplates:
name = prop.Name
if name not in pset["raw"]:
continue
if data.TemplateType == "P_SINGLEVALUE" or data.TemplateType == "P_ENUMERATEDVALUE":
if data.PrimaryMeasureType:
value_type = data.PrimaryMeasureType
if prop.TemplateType == "P_SINGLEVALUE" or prop.TemplateType == "P_ENUMERATEDVALUE":
if prop.PrimaryMeasureType:
value_type = prop.PrimaryMeasureType
else:
# The IFC spec is missing some, so we provide a fallback
value_type = "IfcLabel"
@@ -1753,7 +1742,8 @@ class IfcExporter:
properties.append(
self.file.create_entity("IfcPropertySingleValue", **{"Name": name, "NominalValue": nominal_value})
)
invalid_pset_keys = [k for k in pset["raw"].keys() if k not in templates.keys()]
templates_names = [prop.Name for prop in qto_template.HasPropertyTemplates]
invalid_pset_keys = [k for k in pset["raw"].keys() if k not in templates_names]
if invalid_pset_keys:
self.ifc_export_settings.logger.error(
"One or more properties were invalid in the pset {}: {}".format(
@@ -1762,20 +1752,21 @@ class IfcExporter:
)
return properties
def create_templated_qto_properties(self, qto):
def create_templated_qto_properties(self, qto, qto_template):
properties = []
templates = ifcopenshell.util.pset.qtos[qto["attributes"]["Name"]]["HasPropertyTemplates"]
for name, data in templates.items():
for prop in qto_template.HasPropertyTemplates:
name = prop.Name
if name not in qto["raw"]:
continue
if data.TemplateType[0:2] == "Q_":
value_basename = data.TemplateType[2:].title()
if prop.TemplateType[0:2] == "Q_":
value_basename = prop.TemplateType[2:].title()
value_name = f"{value_basename}Value"
class_name = f"IfcQuantity{value_basename}"
properties.append(
self.file.create_entity(class_name, **{"Name": name, value_name: float(qto["raw"][name])})
)
invalid_qto_keys = [k for k in qto["raw"].keys() if k not in templates.keys()]
templates_names = [prop.Name for prop in qto_template.HasPropertyTemplates]
invalid_qto_keys = [k for k in qto["raw"].keys() if k not in templates_names]
if invalid_qto_keys:
self.ifc_export_settings.logger.error(
"One or more properties were invalid in the qto {}/{}: {}".format(
@@ -2039,8 +2030,8 @@ class IfcExporter:
# At the moment, we assume that styled items only apply to the body context.
if representation.RepresentationIdentifier != "Body":
continue
rep_context = self.ifc_parser.get_obj_representation_context(product["raw"], "Model", "Body", "MODEL_VIEW")
if self.ifc_export_settings.should_roundtrip_native and rep_context and rep_context.ifc_definition_id:
rep = self.ifc_parser.get_shape_representation(product["raw"], "Model", "Body", "MODEL_VIEW")
if self.ifc_export_settings.should_roundtrip_native and rep and rep.ifc_definition_id:
# For native roundtripping, each slot could be a one to many relationship to items
for item in self.get_geometric_representation_items(representation):
original_id = self.roundtrip_id_new_to_old[item.id()]
@@ -3181,17 +3172,6 @@ class IfcExporter:
[o["ifc"] for o in related_objects],
)
def relate_objects_to_types(self):
for relating_type, related_objects in self.ifc_parser.rel_defines_by_type.items():
self.file.createIfcRelDefinesByType(
ifcopenshell.guid.new(),
self.owner_history,
None,
None,
[self.ifc_parser.products[o]["ifc"] for o in related_objects],
self.ifc_parser.type_products[relating_type]["ifc"],
)
def relate_objects_to_qtos(self):
for relating_property_key, related_objects in self.ifc_parser.rel_defines_by_qto.items():
self.file.createIfcRelDefinesByProperties(
@@ -3468,6 +3448,7 @@ class IfcExporter:
return co * self.ifc_parser.unit_scale
def write_ifc_file(self):
self.set_header()
extension = self.ifc_export_settings.output_file.split(".")[-1]
if extension == "ifczip":
with tempfile.TemporaryDirectory() as unzipped_path:
@@ -3551,15 +3532,4 @@ class IfcExportSettings:
settings.should_roundtrip_native = scene_bim.import_export_should_roundtrip_native
settings.should_export_from_memory = scene_bim.export_should_export_from_memory
settings.context_tree = []
for ifc_context in ["model", "plan"]:
if getattr(scene_bim, "has_{}_context".format(ifc_context)):
subcontexts = {}
for subcontext in getattr(scene_bim, "{}_subcontexts".format(ifc_context)):
subcontexts.setdefault(subcontext.name, []).append(subcontext.target_view)
settings.context_tree.append(
{
"name": ifc_context.title(),
"subcontexts": [{"name": key, "target_views": value} for key, value in subcontexts.items()],
}
)
return settings
@@ -1,4 +1,6 @@
import math
from mathutils import geometry
from mathutils import Vector
import bpy
@@ -285,3 +287,84 @@ def format_distance(value, isArea=False, hide_units=True):
tx_dist = fmt % value
return tx_dist
def parse_diagram_scale(camera):
"""Returns numeric value of scale"""
if camera.BIMCameraProperties.diagram_scale == "CUSTOM":
_, fraction = camera.BIMCameraProperties.custom_diagram_scale.split("|")
else:
_, fraction = camera.BIMCameraProperties.diagram_scale.split("|")
numerator, denominator = fraction.split("/")
return float(numerator) / float(denominator)
def ortho_view_frame(camera, margin=0.015):
"""Calculates 2d bounding box of camera view area.
Similar to `bpy.types.Camera.view_frame`
:arg camera: camera of drawing
:type camera: bpy.types.Camera + BIMCameraProperties
:arg margin: margins, in scene units
:type margin: float
:return: (xmin, xmax, ymin, ymax) in local camera coordinates
"""
aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x
size = camera.ortho_scale
hwidth = size * .5
hheight = size * .5 * aspect
scale = parse_diagram_scale(camera)
xmarg = margin * scale
ymarg = margin * scale * aspect
return (-hwidth + xmarg, hwidth - xmarg, -hheight + ymarg, hheight - ymarg)
def clip_segment(bounds, segm):
"""Clipping line segment to bounds
:arg bounds: (xmin, xmax, ymin, ymax)
:arg segm: 2 vertices of the segment
:return: 2 new vertices of segment or None if segment outside the bounding box
"""
# LiangBarsky algorithm
xmin, xmax, ymin, ymax = bounds
p1, p2 = segm
def clip_side(p, q):
if abs(p) < 1e-10: # ~= 0, parallel to the side
if q < 0:
return None # outside
else:
return 0, 1 # inside
t = q / p # the intersection point
if p < 0: # entering
return t, 1
else: # leaving
return 0, t
dlt = p2 - p1
tt = (
clip_side(-dlt.x, p1.x - xmin), # left
clip_side(+dlt.x, xmax - p1.x), # right
clip_side(-dlt.y, p1.y - ymin), # bottom
clip_side(+dlt.y, ymax - p1.y), # top
)
if None in tt:
return None
t1 = max(0, max(t[0] for t in tt))
t2 = min(1, min(t[1] for t in tt))
if t1 >= t2:
return None
p1c = p1 + dlt * t1
p2c = p1 + dlt * t2
return p1c, p2c
@@ -5,6 +5,7 @@ import ifcopenshell
class IfcStore:
path = ""
file = None
schema = None
pset_template_path = ""
pset_template_file = None
@@ -15,3 +16,11 @@ class IfcStore:
if IfcStore.path:
IfcStore.file = ifcopenshell.open(IfcStore.path)
return IfcStore.file
@staticmethod
def get_schema():
if IfcStore.file is None:
return
elif IfcStore.schema is None:
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema)
return IfcStore.schema
+35 -109
View File
@@ -3,7 +3,7 @@ import ifcopenshell.geom
import ifcopenshell.util.geolocation
import ifcopenshell.util.selector
import ifcopenshell.util.element
import ifcopenshell.util.pset
import ifcopenshell.util.unit
import bpy
import bmesh
import os
@@ -20,8 +20,8 @@ import tempfile
from pathlib import Path
from itertools import cycle
from datetime import datetime
from . import helper
from . import ifc
from . import schema
class FileCopy(threading.Thread):
@@ -256,6 +256,7 @@ class MaterialCreator:
def create_new_single(self, material):
self.materials[material.Name] = obj = bpy.data.materials.new(material.Name)
obj.BIMMaterialProperties.ifc_definition_id = int(material.id())
self.ifc_importer.add_element_attributes(material, obj.BIMMaterialProperties)
for pset in getattr(material, "HasProperties", ()):
self.ifc_importer.add_pset(pset, obj.BIMMaterialProperties)
@@ -286,6 +287,7 @@ class MaterialCreator:
for style in styles:
if not style.is_a("IfcSurfaceStyle"):
continue
material.BIMMaterialProperties.ifc_style_id = int(style.id())
external_style = None
for surface_style in style.Styles:
if surface_style.is_a("IfcSurfaceStyleShading"):
@@ -373,7 +375,6 @@ class IfcImporter:
self.native_data = {}
self.groups = {}
self.aggregates = {}
self.aggregate_collection = None
self.aggregate_collections = {}
self.material_creator = MaterialCreator(ifc_import_settings, self)
@@ -409,8 +410,6 @@ class IfcImporter:
self.profile_code("Patching ifc")
self.set_units()
self.profile_code("Set units")
self.create_geometric_representation_contexts()
self.profile_code("Create contexts")
self.create_project()
self.profile_code("Create project")
self.create_classifications()
@@ -773,6 +772,7 @@ class IfcImporter:
obj = self.existing_elements[element.GlobalId]
else:
obj = bpy.data.objects.new(f"{element.is_a()}/{element.Name}", None)
obj.BIMObjectProperties.ifc_definition_id = element.id()
self.add_element_attributes(element, obj.BIMObjectProperties)
group_collection.objects.link(obj)
self.groups[element.GlobalId] = {"ifc": element, "blender": obj}
@@ -809,6 +809,7 @@ class IfcImporter:
shape = ifcopenshell.geom.create_shape(self.settings_2d, axis.AxisCurve)
mesh = self.create_mesh(axis, shape)
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
obj.BIMObjectProperties.ifc_definition_id = element.id()
obj.matrix_world = matrix_world
self.add_element_attributes(axis, obj.BIMObjectProperties)
grid.objects.link(obj)
@@ -843,12 +844,10 @@ class IfcImporter:
except:
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
obj = bpy.data.objects.new(self.get_name(element), mesh)
obj.BIMObjectProperties.ifc_definition_id = element.id()
self.material_creator.create(element, obj, mesh)
self.add_element_attributes(element, obj.BIMObjectProperties)
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_type_product_psets(element, obj)
self.add_product_representations(element, obj)
self.type_collection.objects.link(obj)
self.type_products[element.GlobalId] = obj
@@ -950,11 +949,16 @@ class IfcImporter:
mesh = self.create_native_mesh(element, shape)
if mesh is None:
mesh = self.create_mesh(element, shape)
if "-" in shape.geometry.id:
mesh.BIMMeshProperties.ifc_definition_id = int(shape.geometry.id.split("-")[0])
else:
mesh.BIMMeshProperties.ifc_definition_id = int(shape.geometry.id)
self.meshes[mesh_name] = mesh
else:
mesh = None
obj = bpy.data.objects.new(self.get_name(element), mesh)
obj.BIMObjectProperties.ifc_definition_id = element.id()
if shape:
m = shape.transformation.matrix.data
@@ -968,13 +972,10 @@ class IfcImporter:
obj.matrix_world = self.get_element_matrix(element)
self.add_element_representation_items(element, obj)
self.add_element_attributes(element, obj.BIMObjectProperties)
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_defines_by_type_relation(element, obj)
self.add_opening_relation(element, obj)
self.add_product_definitions(element, obj)
self.add_product_representations(element, obj)
self.added_data[element.GlobalId] = obj
if element.is_a("IfcOpeningElement"):
@@ -1309,7 +1310,6 @@ class IfcImporter:
# Occurs when reloading a project
pass
project_collection = bpy.context.view_layer.layer_collection.children[self.project["blender"].name]
project_collection.children[self.aggregate_collection.name].hide_viewport = True
project_collection.children[self.opening_collection.name].hide_viewport = True
project_collection.children[self.type_collection.name].hide_viewport = True
@@ -1355,47 +1355,21 @@ class IfcImporter:
bpy.ops.mesh.normals_make_consistent(context_override)
bpy.ops.object.editmode_toggle(context_override)
def add_product_representations(self, element, obj):
if element.is_a("IfcProduct"):
if not element.Representation:
return
for r in element.Representation.Representations:
new = obj.BIMObjectProperties.representations.add()
new.name = r.RepresentationIdentifier
new.type = r.RepresentationType
new.ifc_definition_id = r.id()
elif element.is_a("IfcTypeProduct"):
if not element.RepresentationMaps:
return
for r in element.RepresentationMaps:
new = obj.BIMObjectProperties.representations.add()
new.name = r.MappedRepresentation.RepresentationIdentifier
new.type = r.MappedRepresentation.RepresentationType
new.ifc_definition_id = r.MappedRepresentation.id()
def add_product_definitions(self, element, obj):
if not hasattr(element, "IsDefinedBy") or not element.IsDefinedBy:
return
for definition in element.IsDefinedBy:
if not definition.is_a("IfcRelDefinesByProperties"):
continue
if definition.RelatingPropertyDefinition.is_a("IfcPropertySet"):
self.add_pset(definition.RelatingPropertyDefinition, obj.BIMObjectProperties)
elif definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"):
if definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"):
self.add_qto(definition.RelatingPropertyDefinition, obj)
def add_type_product_psets(self, element, obj):
if not hasattr(element, "HasPropertySets") or not element.HasPropertySets:
return
for definition in element.HasPropertySets:
if definition.is_a("IfcPropertySet"):
self.add_pset(definition, obj.BIMObjectProperties)
def add_pset(self, pset, props):
new_pset = props.psets.add()
new_pset.name = pset.Name
if new_pset.name in ifcopenshell.util.pset.psets:
for prop_name in ifcopenshell.util.pset.psets[new_pset.name]["HasPropertyTemplates"].keys():
pset_template = schema.ifc.psetqto.get_by_name(new_pset.name)
if pset_template:
for prop_name in (p.Name for p in pset_template.HasPropertyTemplates):
prop = new_pset.properties.add()
prop.name = prop_name
try:
@@ -1421,8 +1395,9 @@ class IfcImporter:
def add_qto(self, qto, obj):
new_qto = obj.BIMObjectProperties.qtos.add()
new_qto.name = str(qto.Name)
if new_qto.name in ifcopenshell.util.pset.qtos:
for prop_name in ifcopenshell.util.pset.qtos[new_qto.name]["HasPropertyTemplates"].keys():
qto_template = schema.ifc.psetqto.get_by_name(new_qto.name)
if qto_template:
for prop_name in (p.Name for p in qto_template.HasPropertyTemplates):
prop = new_qto.properties.add()
prop.name = prop_name
for prop in qto.Quantities:
@@ -1438,11 +1413,6 @@ class IfcImporter:
new_prop.name = prop.Name
new_prop.string_value = str(value)
def add_defines_by_type_relation(self, element, obj):
related_type = ifcopenshell.util.element.get_type(element)
if related_type:
obj.BIMObjectProperties.relating_type = self.type_products[related_type.GlobalId]
def add_opening_relation(self, element, obj):
if not element.is_a("IfcOpeningElement"):
return
@@ -1499,7 +1469,7 @@ class IfcImporter:
self.unit_scale *= unit.ConversionFactor.ValueComponent.wrappedValue
unit = unit.ConversionFactor.UnitComponent
if unit.is_a("IfcSIUnit"):
self.unit_scale *= helper.SIUnitHelper.get_prefix_multiplier(unit.Prefix)
self.unit_scale *= ifcopenshell.util.unit.get_prefix_multiplier(unit.Prefix)
def set_units(self):
units = self.file.by_type("IfcUnitAssignment")[0]
@@ -1530,28 +1500,6 @@ class IfcImporter:
unit.Prefix + "/" if hasattr(unit, "Prefix") and unit.Prefix else "", name
)
def create_geometric_representation_contexts(self):
bpy.context.scene.BIMProperties.has_model_context = False
for context in self.file.by_type("IfcGeometricRepresentationContext"):
if context.is_a("IfcGeometricRepresentationSubContext"):
if not context.ContextIdentifier:
# Revit creates invalid contexts, so we just ignore them
continue
if context.ContextType == "Model":
subcontexts = bpy.context.scene.BIMProperties.model_subcontexts
elif context.ContextType == "Plan":
subcontexts = bpy.context.scene.BIMProperties.plan_subcontexts
if subcontexts.get(context.ContextIdentifier):
continue
subcontext = subcontexts.add()
subcontext.name = context.ContextIdentifier
subcontext.target_view = context.TargetView
subcontext.ifc_definition_id = context.id()
elif context.ContextType == "Model":
bpy.context.scene.BIMProperties.has_model_context = True
elif context.ContextType == "Plan":
bpy.context.scene.BIMProperties.has_plan_context = True
def create_project(self):
self.project = {"ifc": self.file.by_type("IfcProject")[0]}
if self.project["ifc"].GlobalId in self.existing_elements:
@@ -1743,30 +1691,23 @@ class IfcImporter:
]
else:
rel_aggregates = [a for a in self.file.by_type("IfcRelAggregates") if a.RelatingObject.is_a("IfcElement")]
for collection in self.project["blender"].children:
if collection.name == "Aggregates":
self.aggregate_collection = collection
break
if not self.aggregate_collection:
self.aggregate_collection = bpy.data.collections.new("Aggregates")
self.project["blender"].children.link(self.aggregate_collection)
for rel_aggregate in rel_aggregates:
self.create_aggregate(rel_aggregate)
def create_aggregate(self, rel_aggregate):
collection = bpy.data.collections.new(f"IfcRelAggregates/{rel_aggregate.id()}")
self.aggregate_collection.children.link(collection)
element = rel_aggregate.RelatingObject
obj = bpy.data.objects.new("{}/{}".format(element.is_a(), element.Name), None)
obj.instance_type = "COLLECTION"
obj.instance_collection = collection
obj.BIMObjectProperties.ifc_definition_id = element.id()
self.place_object_in_spatial_tree(element, obj)
self.add_element_attributes(element, obj.BIMObjectProperties)
collection = bpy.data.collections.new(obj.name)
obj.users_collection[0].children.link(collection)
obj.users_collection[0].objects.unlink(obj)
collection.objects.link(obj)
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_defines_by_type_relation(element, obj)
self.add_product_definitions(element, obj)
self.aggregates[element.GlobalId] = obj
self.aggregate_collections[rel_aggregate.id()] = collection
@@ -1823,12 +1764,11 @@ class IfcImporter:
return
obj = bpy.data.objects.new(self.get_name(element), mesh)
obj.BIMObjectProperties.ifc_definition_id = element.id()
self.material_creator.create(element, obj, mesh)
obj.matrix_world = self.get_element_matrix(element, mesh_name)
self.add_element_attributes(element, obj.BIMObjectProperties)
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_defines_by_type_relation(element, obj)
self.add_product_definitions(element, obj)
self.added_data[element.GlobalId] = obj
@@ -1869,8 +1809,6 @@ class IfcImporter:
):
container = element.ContainedInStructure[0].RelatingStructure
if container.is_a("IfcSpace"):
if self.ifc_import_settings.should_import_spaces and container.GlobalId in self.added_data:
obj.BIMObjectProperties.relating_structure = self.added_data[container.GlobalId]
return self.place_object_in_spatial_tree(container, obj)
elif element.is_a("IfcGrid"):
grid_collection = bpy.data.collections.get(obj.name)
@@ -2052,23 +1990,6 @@ class IfcImporter:
):
return representation.Items[0].MappingTarget
def get_geometry_type(self, element):
tree = []
if hasattr(element, "Representation"):
tree = self.file.traverse(element.Representation)
elif hasattr(element, "RepresentationMaps"):
for representation_map in element.RepresentationMaps:
tree.extend(self.file.traverse(representation_map))
representations = [
e
for e in tree
if e.is_a("IfcRepresentation")
and e.RepresentationIdentifier == "Body"
and e.RepresentationType != "MappedRepresentation"
]
for representation in representations:
return representation.Items[0].is_a()
def create_mesh(self, element, shape, is_curve=False):
try:
if hasattr(shape, "geometry"):
@@ -2079,7 +2000,13 @@ class IfcImporter:
if is_curve:
return self.create_curve(geometry)
mesh = bpy.data.meshes.new(geometry.id)
representation_id = geometry.id
if "-" in representation_id:
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
else:
representation_id = int(re.sub(r"\D", "", representation_id))
mesh = bpy.data.meshes.new("{}/{}".format(
self.file.by_id(representation_id).ContextOfItems.id(), geometry.id))
if geometry.faces:
num_vertices = len(geometry.verts) // 3
@@ -2120,7 +2047,6 @@ class IfcImporter:
ios_materials.append(mat.name)
mesh["ios_materials"] = ios_materials
mesh["ios_material_ids"] = geometry.material_ids
mesh.BIMMeshProperties.geometry_type = str(self.get_geometry_type(element))
return mesh
except:
self.ifc_import_settings.logger.error("Could not create mesh for %s", element)
File diff suppressed because it is too large Load Diff
+118 -119
View File
@@ -1,7 +1,6 @@
import json
import os
import ifcopenshell
import ifcopenshell.util.pset
from pathlib import Path
from . import export_ifc
from . import schema
@@ -9,6 +8,7 @@ from . import ifc
from . import annotation
from . import decoration
import bpy
from blenderbim.bim.ifc import IfcStore
from bpy.types import PropertyGroup
from bpy.app.handlers import persistent
from bpy.props import (
@@ -40,35 +40,19 @@ psettemplatefiles_enum = []
propertysettemplates_enum = []
classification_enum = []
attributes_enum = []
psetnames_enum = []
psetnames = {}
qtonames_enum = []
materialattributes_enum = []
materialtypes_enum = []
contexts_enum = []
subcontexts_enum = []
target_views_enum = []
persons_enum = []
organisations_enum = []
sheets_enum = []
vector_styles_enum = []
@persistent
def setDefaultProperties(scene):
if (
bpy.context.scene.BIMProperties.has_model_context
and len(bpy.context.scene.BIMProperties.model_subcontexts) == 0
):
subcontext = bpy.context.scene.BIMProperties.model_subcontexts.add()
subcontext.name = "Body"
subcontext.target_view = "MODEL_VIEW"
subcontext = bpy.context.scene.BIMProperties.model_subcontexts.add()
subcontext.name = "Box"
subcontext.target_view = "MODEL_VIEW"
if bpy.context.scene.BIMProperties.has_plan_context and len(bpy.context.scene.BIMProperties.plan_subcontexts) == 0:
subcontext = bpy.context.scene.BIMProperties.plan_subcontexts.add()
subcontext.name = "Annotation"
subcontext.target_view = "PLAN_VIEW"
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Technical"
@@ -140,14 +124,13 @@ def setDefaultProperties(scene):
def getIfcPredefinedTypes(self, context):
global types_enum
if len(types_enum) < 1:
for name, data in schema.ifc.elements.items():
if name != self.ifc_class.strip():
continue
for attribute in data["attributes"]:
if attribute["name"] != "PredefinedType":
continue
types_enum.extend([(e, e, "") for e in attribute["enum_values"]])
file = IfcStore.get_file()
if len(types_enum) < 1 and file:
declaration = IfcStore.get_schema().declaration_by_name(self.ifc_class)
for attribute in declaration.attributes():
if attribute.name() == "PredefinedType":
types_enum.extend([(e, e, "") for e in attribute.type_of_attribute().declared_type().enumeration_items()])
break
return types_enum
@@ -263,6 +246,7 @@ def refreshActiveDrawingIndex(self, context):
def getIfcProducts(self, context):
global products_enum
file = IfcStore.get_file()
if len(products_enum) < 1:
products_enum.extend(
[
@@ -272,23 +256,38 @@ def getIfcProducts(self, context):
"IfcElementType",
"IfcSpatialElement",
"IfcGroup",
"IfcStructural",
"IfcPositioningElement",
"IfcStructuralItem",
"IfcContext",
"IfcAnnotation",
]
]
)
if file.schema == "IFC2X3":
products_enum[2] = ("IfcSpatialStructureElement", "IfcSpatialStructureElement", "")
return products_enum
def getIfcClasses(self, context):
global classes_enum
if len(classes_enum) < 1:
classes_enum.extend([(e, e, "") for e in getattr(schema.ifc, self.ifc_product)])
file = IfcStore.get_file()
if len(classes_enum) < 1 and file:
declaration = IfcStore.get_schema().declaration_by_name(self.ifc_product)
def get_classes(declaration):
results = []
if not declaration.is_abstract():
results.append(declaration.name())
for subtype in declaration.subtypes():
results.extend(get_classes(subtype))
return results
classes = get_classes(declaration)
classes_enum.extend([(c, c, "") for c in sorted(classes)])
return classes_enum
def getAttributeEnumValues(self, context):
return [(e, e, "") for e in json.loads(self.enum_items)]
def getProfileDef(self, context):
global profiledef_enum
if len(profiledef_enum) < 1:
@@ -297,17 +296,27 @@ def getProfileDef(self, context):
def getPersons(self, context):
global persons_enum
persons_enum.clear()
persons_enum.extend([(p.name, p.name, "") for p in bpy.context.scene.BIMProperties.people])
return persons_enum
from blenderbim.bim.module.owner.data import Data
if not Data.is_loaded:
Data.load()
results = []
for ifc_id, person in Data.people.items():
if "Id" in person:
identifier = person["Id"] or ""
else:
identifier = person["Identifier"] or ""
results.append((str(ifc_id), identifier, ""))
return results
def getOrganisations(self, context):
global organisations_enum
organisations_enum.clear()
organisations_enum.extend([(o.name, o.name, "") for o in bpy.context.scene.BIMProperties.organisations])
return organisations_enum
from blenderbim.bim.module.owner.data import Data
if not Data.is_loaded:
Data.load()
results = []
for ifc_id, organisation in Data.organisations.items():
results.append((str(ifc_id), organisation["Name"], ""))
return results
def getIfcPatchRecipes(self, context):
@@ -359,11 +368,18 @@ def refreshTitleblocks(self, context):
def toggleDecorations(self, context):
toggle = self.should_draw_decorations
if toggle:
for dec in decoration.all_decorators:
dec.install(self, context)
decoration.DecorationsHandler.install(context)
else:
for dec in decoration.all_decorators:
dec.uninstall()
decoration.DecorationsHandler.uninstall()
@persistent
def toggleDecorationsOnLoad(*args):
toggle = bpy.context.scene.DocProperties.should_draw_decorations
if toggle:
decoration.DecorationsHandler.install(bpy.context)
else:
decoration.DecorationsHandler.uninstall()
def getScenarios(self, context):
@@ -430,22 +446,20 @@ def refreshReferences(self, context):
def getPsetNames(self, context):
global psetnames_enum
psetnames_enum.clear()
if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements:
pset_names = ifcopenshell.util.pset.get_applicable_psetqtos(
bpy.context.scene.BIMProperties.export_schema, context.active_object.name.split("/")[0], is_pset=True
)
psetnames_enum.extend([(p, p, "") for p in pset_names])
return psetnames_enum
global psetnames
if "/" in context.active_object.name:
ifc_class = context.active_object.name.split("/")[0]
if ifc_class not in psetnames:
psets = schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
return psetnames[ifc_class]
return []
def getMaterialPsetNames(self, context):
global materialpsetnames_enum
materialpsetnames_enum.clear()
pset_names = ifcopenshell.util.pset.get_applicable_psetqtos(
bpy.context.scene.BIMProperties.export_schema, "IfcMaterial", is_pset=True
)
pset_names = schema.ifc.psetqto.get_applicable_names("IfcMaterial", pset_only=True)
materialpsetnames_enum.extend([(p, p, "") for p in pset_names])
return materialpsetnames_enum
@@ -454,27 +468,11 @@ def getQtoNames(self, context):
global qtonames_enum
qtonames_enum.clear()
if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements:
qto_names = ifcopenshell.util.pset.get_applicable_psetqtos(
bpy.context.scene.BIMProperties.export_schema, context.active_object.name.split("/")[0], is_qto=True
)
qto_names = schema.ifc.psetqto.get_applicable_names(context.active_object.name.split("/")[0], qto_only=True)
qtonames_enum.extend([(q, q, "") for q in qto_names])
return qtonames_enum
def getApplicableAttributes(self, context):
global attributes_enum
attributes_enum.clear()
if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements:
attributes_enum.extend(
[
(a["name"], a["name"], "")
for a in schema.ifc.elements[context.active_object.name.split("/")[0]]["attributes"]
if self.attributes.find(a["name"]) == -1
]
)
return attributes_enum
def getApplicableMaterialAttributes(self, context):
global materialattributes_enum
materialattributes_enum.clear()
@@ -508,6 +506,19 @@ def getMaterialTypes(self, context):
return materialtypes_enum
def getContexts(self, context):
from blenderbim.bim.module.context.data import Data
if not Data.is_loaded:
Data.load()
results = []
for ifc_id, context in Data.contexts.items():
results.append((str(ifc_id), context["ContextType"], ""))
for ifc_id2, subcontext in context["HasSubContexts"].items():
results.append((str(ifc_id2), "{}/{}/{}".format(
subcontext["ContextType"], subcontext["ContextIdentifier"], subcontext["TargetView"]), ""))
return results
def getSubcontexts(self, context):
global subcontexts_enum
subcontexts_enum.clear()
@@ -556,19 +567,9 @@ class Attribute(PropertyGroup):
bool_value: BoolProperty(name="Value")
int_value: IntProperty(name="Value")
float_value: FloatProperty(name="Value")
class Subcontext(PropertyGroup):
name: StringProperty(name="Name")
context: StringProperty(name="Context")
target_view: StringProperty(name="Target View")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class Representation(PropertyGroup):
name: StringProperty(name="Name")
type: StringProperty(name="Type")
ifc_definition_id: IntProperty(name="IFC Definition ID")
is_null: BoolProperty(name="Is Null")
enum_items: StringProperty(name="Value")
enum_value: EnumProperty(items=getAttributeEnumValues, name="Value")
class MaterialLayer(PropertyGroup):
@@ -685,8 +686,7 @@ class DocProperties(PropertyGroup):
drawing_styles: CollectionProperty(name="Drawing Styles", type=DrawingStyle)
should_draw_decorations: BoolProperty(name="Should Draw Decorations", update=toggleDecorations)
decorations_colour: FloatVectorProperty(name="Decorations Colour", subtype="COLOR", default=(1, 0, 0, 1),
min=0.0, max=1.0, size=4,
update=toggleDecorations)
min=0.0, max=1.0, size=4)
class BIMCameraProperties(PropertyGroup):
@@ -1097,6 +1097,7 @@ class Address(PropertyGroup):
name: StringProperty(name="Name", default="IfcPostalAddress") # Stores IfcPostalAddress or IfcTelecomAddress
purpose: EnumProperty(
items=[
("None", "None", ""),
("OFFICE", "OFFICE", "An office address."),
("SITE", "SITE", "A site address."),
("HOME", "HOME", "A home address."),
@@ -1117,7 +1118,7 @@ class Address(PropertyGroup):
country: StringProperty(name="Country")
telephone_numbers: StringProperty(name="Telephone Numbers")
fascimile_numbers: StringProperty(name="Fascimile Numbers")
facsimile_numbers: StringProperty(name="Facsimile Numbers")
pager_number: StringProperty(name="Pager Number")
electronic_mail_addresses: StringProperty(name="Emails")
www_home_page_url: StringProperty(name="Websites")
@@ -1158,12 +1159,9 @@ class Role(PropertyGroup):
class Organisation(PropertyGroup):
identification: StringProperty(name="Identification")
name: StringProperty(name="Name")
description: StringProperty(name="Description")
roles: CollectionProperty(name="Roles", type=Role)
active_role_index: bpy.props.IntProperty()
addresses: CollectionProperty(name="Addresses", type=Address)
active_address_index: bpy.props.IntProperty()
class Person(PropertyGroup):
@@ -1173,10 +1171,6 @@ class Person(PropertyGroup):
middle_names: StringProperty(name="Middle Names")
prefix_titles: StringProperty(name="Prefixes")
suffix_titles: StringProperty(name="Suffixes")
roles: CollectionProperty(name="Roles", type=Role)
active_role_index: bpy.props.IntProperty()
addresses: CollectionProperty(name="Addresses", type=Address)
active_address_index: bpy.props.IntProperty()
class Classification(PropertyGroup):
@@ -1269,7 +1263,7 @@ class BIMProperties(PropertyGroup):
)
export_should_force_faceted_brep: BoolProperty(name="Export with Faceted Breps", default=False)
export_should_force_triangulation: BoolProperty(name="Export with Triangulation", default=False)
export_should_export_from_memory: BoolProperty(name="Export from Memory", default=False)
export_should_export_from_memory: BoolProperty(name="Export from Memory", default=True)
import_should_ignore_site_coordinates: BoolProperty(name="Import Ignoring Site Coordinates", default=False)
import_should_ignore_building_coordinates: BoolProperty(name="Import Ignoring Building Coordinates", default=False)
import_should_reset_absolute_coordinates: BoolProperty(name="Import Resetting Absolute Coordinates", default=False)
@@ -1281,7 +1275,7 @@ class BIMProperties(PropertyGroup):
import_should_auto_set_workarounds: BoolProperty(name="Automatically Set Vendor Workarounds", default=True)
import_should_use_legacy: BoolProperty(name="Import with Legacy Importer", default=False)
import_should_import_native: BoolProperty(name="Import Native Representations", default=False)
import_export_should_roundtrip_native: BoolProperty(name="Roundtrip Native Representations", default=False)
import_export_should_roundtrip_native: BoolProperty(name="Roundtrip Native Representations", default=True)
import_should_use_cpu_multiprocessing: BoolProperty(name="Import with CPU Multiprocessing", default=True)
import_should_import_with_profiling: BoolProperty(name="Import with Profiling", default=True)
import_should_import_aggregates: BoolProperty(name="Import Aggregates", default=True)
@@ -1296,12 +1290,18 @@ class BIMProperties(PropertyGroup):
import_should_offset_model: BoolProperty(name="Import and Offset Model", default=False)
import_model_offset_coordinates: StringProperty(name="Model Offset Coordinates", default="0,0,0")
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
person: EnumProperty(items=getPersons, name="Person")
organisation: EnumProperty(items=getOrganisations, name="Organisation")
people: CollectionProperty(name="People", type=Person)
organisations: CollectionProperty(name="Organisations", type=Organisation)
active_person_index: IntProperty(name="Active Person Index")
active_organisation_index: IntProperty(name="Active Organisation Index")
person: PointerProperty(type=Person)
active_person_id: IntProperty(name="Active Person Id")
organisation: PointerProperty(type=Organisation)
active_organisation_id: IntProperty(name="Active Organisation Id")
role: PointerProperty(type=Role)
active_role_id: IntProperty(name="Active Role Id")
address: PointerProperty(type=Address)
active_address_id: IntProperty(name="Active Address Id")
user_person: EnumProperty(items=getPersons, name="Person")
user_organisation: EnumProperty(items=getOrganisations, name="Organisation")
has_georeferencing: BoolProperty(name="Has Georeferencing", default=False)
has_library: BoolProperty(name="Has Project Library", default=False)
search_regex: BoolProperty(name="Search With Regex", default=False)
@@ -1328,10 +1328,7 @@ class BIMProperties(PropertyGroup):
classification: EnumProperty(items=getClassifications, name="Classification", update=refreshReferences)
active_classification_name: StringProperty(name="Active Classification Name")
classifications: CollectionProperty(name="Classifications", type=Classification)
has_model_context: BoolProperty(name="Has Model Context", default=True)
has_plan_context: BoolProperty(name="Has Plan Context", default=True)
model_subcontexts: CollectionProperty(name="Model Subcontexts", type=Subcontext)
plan_subcontexts: CollectionProperty(name="Plan Subcontexts", type=Subcontext)
contexts: EnumProperty(items=getContexts, name="Contexts")
available_contexts: EnumProperty(items=[("Model", "Model", ""), ("Plan", "Plan", "")], name="Available Contexts")
available_subcontexts: EnumProperty(items=getSubcontexts, name="Available Subcontexts")
available_target_views: EnumProperty(items=getTargetViews, name="Available Target Views")
@@ -1487,14 +1484,23 @@ class BoundaryCondition(PropertyGroup):
class BIMObjectProperties(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID")
is_reassigning_class: BoolProperty(name="Is Reassigning Class")
global_ids: CollectionProperty(name="GlobalIds", type=GlobalId)
attributes: CollectionProperty(name="Attributes", type=Attribute)
is_editing_attributes: BoolProperty(name="Is Editing Attributes")
relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object)
is_editing_aggregate: BoolProperty(name="Is Editing Aggregate")
is_editing_container: BoolProperty(name="Is Editing Container")
relating_type: PointerProperty(name="Type", type=bpy.types.Object)
is_editing_type: BoolProperty(name="Is Editing Type")
relating_type: PointerProperty(name="Type Product", type=bpy.types.Object)
relating_structure: PointerProperty(name="Spatial Container", type=bpy.types.Object)
active_pset_id: IntProperty(name="Active Pset ID")
active_pset_name: StringProperty(name="Pset Name")
properties: CollectionProperty(name="Properties", type=Attribute)
psets: CollectionProperty(name="Psets", type=PsetQto)
qtos: CollectionProperty(name="Qtos", type=PsetQto)
applicable_attributes: EnumProperty(items=getApplicableAttributes, name="Attribute Names")
document_references: CollectionProperty(name="Document References", type=DocumentReference)
active_document_reference_index: IntProperty(name="Active Document Reference Index")
constraints: CollectionProperty(name="Constraints", type=Constraint)
@@ -1508,20 +1514,10 @@ class BIMObjectProperties(PropertyGroup):
has_boundary_condition: BoolProperty(name="Has Boundary Condition")
boundary_condition: PointerProperty(name="Boundary Condition", type=BoundaryCondition)
structural_member_connection: PointerProperty(name="Structural Member Connection", type=bpy.types.Object)
representations: CollectionProperty(name="Representations", type=Representation)
# Address applies to IfcSite's SiteAddress and IfcBuilding's BuildingAddress
address: PointerProperty(name="Address", type=Address)
class BIMDebugProperties(PropertyGroup):
step_id: IntProperty(name="STEP ID")
number_of_polygons: IntProperty(name="Number of Polygons")
active_step_id: IntProperty(name="STEP ID")
step_id_breadcrumb: CollectionProperty(name="STEP ID Breadcrumb", type=StrProperty)
attributes: CollectionProperty(name="Attributes", type=Attribute)
inverse_attributes: CollectionProperty(name="Inverse Attributes", type=Attribute)
class BIMMaterialProperties(PropertyGroup):
is_external: BoolProperty(name="Has External Definition")
location: StringProperty(name="Location")
@@ -1531,6 +1527,9 @@ class BIMMaterialProperties(PropertyGroup):
psets: CollectionProperty(name="Psets", type=PsetQto)
attributes: CollectionProperty(name="Attributes", type=Attribute)
applicable_attributes: EnumProperty(items=getApplicableMaterialAttributes, name="Attribute Names")
ifc_definition_id: IntProperty(name="IFC Definition ID")
# In Blender, a material object can map to an IFC material, IFC surface style, or both
ifc_style_id: IntProperty(name="IFC Style ID")
class SweptSolid(PropertyGroup):
@@ -1551,13 +1550,13 @@ class ItemSlotMap(PropertyGroup):
class BIMMeshProperties(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID")
is_native: BoolProperty(name="Is Native", default=False)
is_swept_solid: BoolProperty(name="Is Swept Solid")
swept_solids: CollectionProperty(name="Swept Solids", type=SweptSolid)
is_parametric: BoolProperty(name="Is Parametric", default=False)
geometry_type: StringProperty(name="Geometry Type")
ifc_definition: StringProperty(name="IFC Definition")
ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter)
active_representation_item_index: IntProperty(name="Active Representation Item Index")
presentation_layer_index: IntProperty(name="Presentation Layer Index", default=-1)
ifc_item_ids: CollectionProperty(name="IFC Definition ID", type=ItemSlotMap)
ifc_item_ids: CollectionProperty(name="IFC Item IDs", type=ItemSlotMap)
@@ -10,11 +10,10 @@ cwd = os.path.dirname(os.path.realpath(__file__))
class IfcSchema:
def __init__(self):
self.schema_dir = os.path.join(cwd, "schema") # TODO: make configurable
self.data_dir = os.path.join(cwd, "data") # TODO: make configurable
self.schema_dir = Path(cwd).joinpath("schema") # TODO: make configurable
self.data_dir = Path(cwd).joinpath("data") # TODO: make configurable
# TODO: Make it less troublesome
self.products = [
"IfcContext",
"IfcElement",
"IfcSpatialElement",
"IfcGroup",
@@ -29,10 +28,11 @@ class IfcSchema:
self.elements = {}
self.property_files = []
property_paths = Path(os.path.join(self.data_dir, "pset")).glob("*.ifc")
property_paths = self.data_dir.joinpath("pset").glob("*.ifc")
# TODO: add IFC2X3 PsetQto template support
self.psetqto = ifcopenshell.util.pset.PsetQto("IFC4")
for path in property_paths:
ifcopenshell.util.pset.load_property_set_template(path)
ifcopenshell.util.pset.load_property_set_template(os.path.join(self.schema_dir, "Pset_IFC4_ADD2.ifc"))
self.psetqto.templates.append(ifcopenshell.open(path))
self.classification_files = {}
self.classifications = {}
@@ -1,114 +0,0 @@
{
"IfcProject": {
"is_abstract": false,
"parent": "IfcContext",
"attributes": [
{
"name": "GlobalId",
"type": "IfcGloballyUniqueId",
"is_enum": false,
"enum_values": []
},
{
"name": "Name",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "Description",
"type": "IfcText",
"is_enum": false,
"enum_values": []
},
{
"name": "ObjectType",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "LongName",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "Phase",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
}
],
"complex_attributes": [
{
"name": "OwnerHistory",
"type": "IfcOwnerHistory",
"is_select": false,
"select_types": []
},
{
"name": "UnitsInContext",
"type": "IfcUnitAssignment",
"is_select": false,
"select_types": []
}
]
},
"IfcProjectLibrary": {
"is_abstract": false,
"parent": "IfcContext",
"attributes": [
{
"name": "GlobalId",
"type": "IfcGloballyUniqueId",
"is_enum": false,
"enum_values": []
},
{
"name": "Name",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "Description",
"type": "IfcText",
"is_enum": false,
"enum_values": []
},
{
"name": "ObjectType",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "LongName",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "Phase",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
}
],
"complex_attributes": [
{
"name": "OwnerHistory",
"type": "IfcOwnerHistory",
"is_select": false,
"select_types": []
},
{
"name": "UnitsInContext",
"type": "IfcUnitAssignment",
"is_select": false,
"select_types": []
}
]
}
}
File diff suppressed because it is too large Load Diff
+1 -561
View File
@@ -5,106 +5,6 @@ from bpy.types import Panel
from bpy.props import StringProperty
class BIM_PT_object(Panel):
bl_label = "IFC Object"
bl_idname = "BIM_PT_object"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
return context.active_object is not None and hasattr(context.active_object, "BIMObjectProperties")
def draw(self, context):
if context.active_object is None:
return
layout = self.layout
props = context.active_object.BIMObjectProperties
bim_properties = context.scene.BIMProperties
if props.is_reassigning_class or "/" not in context.active_object.name:
row = layout.row()
row.prop(bim_properties, "ifc_product")
row = layout.row()
row.prop(bim_properties, "ifc_class")
if bim_properties.ifc_predefined_type:
row = layout.row()
row.prop(bim_properties, "ifc_predefined_type")
if bim_properties.ifc_predefined_type == "USERDEFINED":
row = layout.row()
row.prop(bim_properties, "ifc_userdefined_type")
row = layout.row(align=True)
if "Ifc" not in context.active_object.name:
op = row.operator("bim.assign_class")
else:
op = row.operator("bim.assign_class")
op.object_name = context.active_object.name
op = row.operator("bim.unassign_class", icon="X", text="")
op.object_name = context.active_object.name
else:
row = layout.row()
row.operator("bim.reassign_class", text="Reassign IFC Class")
if "Ifc" not in context.active_object.name:
return
layout.label(text="Attributes:")
row = layout.row(align=True)
row.prop(props, "applicable_attributes", text="")
row.operator("bim.add_attribute")
for index, attribute in enumerate(props.attributes):
row = layout.row(align=True)
row.prop(attribute, "name", text="")
row.prop(attribute, "string_value", text="")
if attribute.name == "GlobalId":
row.operator("bim.generate_global_id", icon="FILE_REFRESH", text="")
op = row.operator("bim.copy_attribute_to_selection", icon="COPYDOWN", text="")
op.attribute_name = attribute.name
op.attribute_value = attribute.string_value
row.operator("bim.remove_attribute", icon="X", text="").attribute_index = index
row = layout.row()
row.prop(props, "attributes")
if "IfcSite/" in context.active_object.name or "IfcBuilding/" in context.active_object.name:
self.draw_addresses_ui()
row = layout.row(align=True)
row.prop(props, "relating_type")
row.operator("bim.select_similar_type", icon="RESTRICT_SELECT_OFF", text="")
row = layout.row()
row.prop(props, "relating_structure")
def draw_addresses_ui(self):
layout = self.layout
layout.label(text="Address:")
address = bpy.context.active_object.BIMObjectProperties.address
row = layout.row()
row.prop(address, "purpose")
if address.purpose == "USERDEFINED":
row = layout.row()
row.prop(address, "user_defined_purpose")
row = layout.row()
row.prop(address, "description")
row = layout.row()
row.prop(address, "internal_location")
row = layout.row()
row.prop(address, "address_lines")
row = layout.row()
row.prop(address, "postal_box")
row = layout.row()
row.prop(address, "town")
row = layout.row()
row.prop(address, "region")
row = layout.row()
row.prop(address, "postal_code")
row = layout.row()
row.prop(address, "country")
class BIM_PT_object_material(Panel):
bl_label = "IFC Object Material"
bl_idname = "BIM_PT_object_material"
@@ -243,73 +143,6 @@ class BIM_PT_object_material(Panel):
row.prop(attribute, "string_value", text="")
class BIM_PT_object_psets(Panel):
bl_label = "IFC Object Property Sets"
bl_idname = "BIM_PT_object_psets"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
return context.active_object is not None and hasattr(context.active_object, "BIMObjectProperties")
def draw(self, context):
if context.active_object is None:
return
layout = self.layout
props = context.active_object.BIMObjectProperties
row = layout.row(align=True)
row.prop(props, "pset_name", text="")
row.operator("bim.add_pset")
self.draw_psets_ui(props)
if props.relating_type and props.relating_type.BIMObjectProperties.psets:
layout.label(text="Inherited Psets:")
self.draw_psets_ui(props.relating_type.BIMObjectProperties, enabled=False)
def draw_psets_ui(self, props, enabled=True):
for index, pset in enumerate(props.psets):
box = self.layout.box()
row = box.row(align=True)
row.prop(
pset,
"is_expanded",
icon="TRIA_DOWN" if pset.is_expanded else "TRIA_RIGHT",
icon_only=True,
emboss=False,
)
row2 = row.row(align=True)
row2.enabled = enabled
row2.prop(pset, "name", text="", icon="COPY_ID", emboss=pset.is_editable)
if enabled:
row.prop(pset, "is_editable", icon="CHECKMARK" if pset.is_editable else "GREASEPENCIL", icon_only=True)
row.operator("bim.remove_pset", icon="X", text="").pset_index = index
if not pset.is_expanded:
continue
if pset.is_editable:
for prop in pset.properties:
row = box.row(align=True)
row.enabled = enabled
row.prop(prop, "name", text="")
row.prop(prop, "string_value", text="")
if not enabled:
continue
op = row.operator("bim.copy_property_to_selection", icon="COPYDOWN", text="")
op.pset_name = pset.name
op.prop_name = prop.name
op.prop_value = prop.string_value
else:
col = box.column(align=True)
for prop in pset.properties:
if not prop.string_value:
continue
col.enabled = enabled
col.prop(prop, "string_value", text=prop.name)
class BIM_PT_object_qto(Panel):
bl_label = "IFC Object Quantity Sets"
bl_idname = "BIM_PT_object_qto"
@@ -626,48 +459,6 @@ class BIM_PT_constraint_relations(Panel):
layout.label(text="Constraint is invalid")
class BIM_PT_representations(Panel):
bl_label = "IFC Representations"
bl_idname = "BIM_PT_representations"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
def draw(self, context):
layout = self.layout
props = context.active_object.BIMObjectProperties
if not props.representations:
layout.label(text="No representations found")
row = layout.row(align=True)
row.prop(bpy.context.scene.BIMProperties, "available_contexts", text="")
row.prop(bpy.context.scene.BIMProperties, "available_subcontexts", text="")
row.prop(bpy.context.scene.BIMProperties, "available_target_views", text="")
# TODO: reimplement with incremental editing
# op = row.operator("bim.switch_context", icon="ADD", text="")
# op.has_target_context = False
self.file = ifc.IfcStore.get_file()
for index, representation in enumerate(props.representations):
row = layout.row(align=True)
row.prop(representation, "name", text="")
row.prop(representation, "type", text="")
if not representation.ifc_definition_id:
continue
subcontext = self.file.by_id(representation.ifc_definition_id).ContextOfItems
if subcontext.is_a() == "IfcGeometricRepresentationContext":
continue
# TODO: reimplement with incremental editing
# op = row.operator("bim.switch_context", icon="OUTLINER_DATA_MESH", text="")
# op.has_target_context = True
# op.context_name = subcontext.ContextType
# op.subcontext_name = subcontext.ContextIdentifier
# op.target_view_name = subcontext.TargetView
# row.operator("bim.remove_context", icon="X", text="").index = index
class BIM_PT_classification_references(Panel):
bl_label = "IFC Classification References"
bl_idname = "BIM_PT_classification_references"
@@ -794,70 +585,6 @@ class BIM_PT_classifications(Panel):
row.prop(props, "classifications")
class BIM_PT_mesh(Panel):
bl_label = "IFC Representations"
bl_idname = "BIM_PT_mesh"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "data"
@classmethod
def poll(cls, context):
return (
context.active_object is not None
and context.active_object.type == "MESH"
and hasattr(context.active_object.data, "BIMMeshProperties")
)
def draw(self, context):
if not context.active_object.data:
return
layout = self.layout
props = context.active_object.data.BIMMeshProperties
row = layout.row(align=True)
row.operator("bim.push_representation")
row = layout.row()
row.prop(props, "geometry_type")
layout.label(text="IFC Parameters:")
row = layout.row()
row.operator("bim.get_representation_ifc_parameters")
row = layout.row()
row.operator("bim.bake_parametric_geometry")
for index, ifc_parameter in enumerate(props.ifc_parameters):
row = layout.row(align=True)
row.prop(ifc_parameter, "name", text="")
row.prop(ifc_parameter, "value", text="")
row.operator("bim.update_ifc_representation", icon="FILE_REFRESH", text="").index = index
row = layout.row()
row.prop(props, "is_parametric")
row = layout.row()
row.prop(props, "is_native")
row = layout.row()
row.prop(props, "is_swept_solid")
row = layout.row()
row.operator("bim.add_swept_solid")
for index, swept_solid in enumerate(props.swept_solids):
row = layout.row(align=True)
row.prop(swept_solid, "name", text="")
row.operator("bim.remove_swept_solid", icon="X", text="").index = index
row = layout.row()
sub = row.row(align=True)
sub.operator("bim.assign_swept_solid_outer_curve").index = index
sub.operator("bim.select_swept_solid_outer_curve", icon="RESTRICT_SELECT_OFF", text="").index = index
sub = row.row(align=True)
sub.operator("bim.add_swept_solid_inner_curve").index = index
sub.operator("bim.select_swept_solid_inner_curves", icon="RESTRICT_SELECT_OFF", text="").index = index
row = layout.row(align=True)
row.operator("bim.assign_swept_solid_extrusion").index = index
row.operator("bim.select_swept_solid_extrusion", icon="RESTRICT_SELECT_OFF", text="").index = index
row = layout.row()
row.prop(props, "swept_solids")
class BIM_PT_presentation_layer_data(Panel):
bl_label = "IFC Presentation Layers"
bl_idname = "BIM_PT_presentation"
@@ -1303,213 +1030,6 @@ class BIM_PT_text(Panel):
row.prop(variable, "prop_key")
class BIM_PT_owner(Panel):
bl_label = "IFC Owner History"
bl_idname = "BIM_PT_owner"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
scene = context.scene
props = scene.BIMProperties
if not props.person:
layout.label(text="No people found.")
else:
row = layout.row()
row.prop(props, "person")
if not props.organisation:
layout.label(text="No organisations found.")
else:
row = layout.row()
row.prop(props, "organisation")
class BIM_PT_people(Panel):
bl_label = "IFC People"
bl_idname = "BIM_PT_people"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
props = context.scene.BIMProperties
row = layout.row()
row.operator("bim.add_person")
if props.people:
layout.template_list("BIM_UL_generic", "", props, "people", props, "active_person_index")
if props.active_person_index < len(props.people):
person = props.people[props.active_person_index]
row = layout.row()
row.prop(person, "name")
row.operator("bim.remove_person", icon="X", text="").index = props.active_person_index
row = layout.row()
row.prop(person, "family_name")
row = layout.row()
row.prop(person, "given_name")
row = layout.row()
row.prop(person, "middle_names")
row = layout.row()
row.prop(person, "prefix_titles")
row = layout.row()
row.prop(person, "suffix_titles")
layout.label(text="Roles:")
draw_roles_ui(layout, person, "person")
layout.label(text="Addresses:")
draw_addresses_ui(layout, person, "person")
class BIM_PT_organisations(Panel):
bl_label = "IFC Organisations"
bl_idname = "BIM_PT_organisations"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
props = context.scene.BIMProperties
row = layout.row()
row.operator("bim.add_organisation")
if props.organisations:
layout.template_list("BIM_UL_generic", "", props, "organisations", props, "active_organisation_index")
if props.active_organisation_index < len(props.organisations):
organisation = props.organisations[props.active_organisation_index]
row = layout.row()
row.prop(organisation, "name")
row.operator("bim.remove_organisation", icon="X", text="").index = props.active_organisation_index
row = layout.row()
row.prop(organisation, "description")
layout.label(text="Roles:")
draw_roles_ui(layout, organisation, "organisation")
layout.label(text="Addresses:")
draw_addresses_ui(layout, organisation, "organisation")
def draw_roles_ui(layout, parent, parent_type):
row = layout.row()
row.operator(f"bim.add_{parent_type}_role")
if parent.roles:
layout.template_list("BIM_UL_generic", "", parent, "roles", parent, "active_role_index")
if parent.active_role_index < len(parent.roles):
role = parent.roles[parent.active_role_index]
row = layout.row()
row.prop(role, "name")
row.operator(f"bim.remove_{parent_type}_role", icon="X", text="").index = parent.active_role_index
if role.name == "USERDEFINED":
row = layout.row()
row.prop(role, "user_defined_role")
row = layout.row()
row.prop(role, "description")
def draw_addresses_ui(layout, parent, parent_type):
row = layout.row()
row.operator(f"bim.add_{parent_type}_address")
if parent.addresses:
layout.template_list("BIM_UL_generic", "", parent, "addresses", parent, "active_address_index")
if parent.active_address_index < len(parent.addresses):
address = parent.addresses[parent.active_address_index]
row = layout.row()
row.prop(address, "name")
row.operator(f"bim.remove_{parent_type}_address", icon="X", text="").index = parent.active_address_index
row = layout.row()
row.prop(address, "purpose")
if address.purpose == "USERDEFINED":
row = layout.row()
row.prop(address, "user_defined_purpose")
row = layout.row()
row.prop(address, "description")
if "IfcPostalAddress" in address.name:
row = layout.row()
row.prop(address, "internal_location")
row = layout.row()
row.prop(address, "address_lines")
row = layout.row()
row.prop(address, "postal_box")
row = layout.row()
row.prop(address, "town")
row = layout.row()
row.prop(address, "region")
row = layout.row()
row.prop(address, "postal_code")
row = layout.row()
row.prop(address, "country")
elif "IfcTelecomAddress" in address.name:
row = layout.row()
row.prop(address, "telephone_numbers")
row = layout.row()
row.prop(address, "fascimile_numbers")
row = layout.row()
row.prop(address, "pager_number")
row = layout.row()
row.prop(address, "electronic_mail_addresses")
row = layout.row()
row.prop(address, "www_home_page_url")
row = layout.row()
row.prop(address, "messaging_ids")
class BIM_PT_context(Panel):
bl_label = "IFC Geometric Representation Contexts"
bl_idname = "BIM_PT_context"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
scene = context.scene
props = scene.BIMProperties
for context in ["model", "plan"]:
row = layout.row(align=True)
row.prop(props, f"has_{context}_context")
if not getattr(props, f"has_{context}_context"):
continue
layout.label(text="Geometric Representation Subcontexts:")
row = layout.row(align=True)
row.prop(props, "available_subcontexts", text="")
row.prop(props, "available_target_views", text="")
row.operator("bim.add_subcontext", icon="ADD", text="").context = context
for subcontext_index, subcontext in enumerate(getattr(props, "{}_subcontexts".format(context))):
row = layout.row(align=True)
row.prop(subcontext, "name", text="")
row.prop(subcontext, "target_view", text="")
row.operator("bim.remove_subcontext", icon="X", text="").indexes = "{}-{}".format(
context, subcontext_index
)
class BIM_PT_bim(Panel):
bl_label = "Building Information Modeling"
bl_idname = "BIM_PT_bim"
@@ -1526,9 +1046,6 @@ class BIM_PT_bim(Panel):
layout.label(text="System Setup:")
row = layout.row()
row.operator("bim.quick_project_setup")
row = layout.row(align=True)
row.prop(bim_properties, "schema_dir")
row.operator("bim.select_schema_dir", icon="FILE_FOLDER", text="")
@@ -1548,22 +1065,6 @@ class BIM_PT_bim(Panel):
layout.label(text="IFC Categorisation:")
row = layout.row()
row.prop(bim_properties, "ifc_product")
row = layout.row()
row.prop(bim_properties, "ifc_class")
if bim_properties.ifc_predefined_type:
row = layout.row()
row.prop(bim_properties, "ifc_predefined_type")
if bim_properties.ifc_predefined_type == "USERDEFINED":
row = layout.row()
row.prop(bim_properties, "ifc_userdefined_type")
row = layout.row(align=True)
op = row.operator("bim.assign_class")
op.object_name = ""
op = row.operator("bim.unassign_class", icon="X", text="")
op.object_name = ""
row = layout.row(align=True)
row.operator("bim.select_class")
row.operator("bim.select_type")
@@ -1850,6 +1351,7 @@ class BIM_PT_patch(Panel):
class BIM_PT_mvd(Panel):
bl_label = "Model View Definitions (MVD)"
bl_idname = "BIM_PT_mvd"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
@@ -2360,68 +1862,6 @@ class BIM_PT_misc_utilities(Panel):
row.operator("bim.snap_spaces_together")
class BIM_PT_debug(Panel):
bl_label = "IFC Debug"
bl_idname = "BIM_PT_debug"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
scene = context.scene
props = scene.BIMDebugProperties
row = layout.row()
row.operator("bim.profile_import_ifc")
row = layout.row()
row.prop(props, "step_id", text="")
row = layout.row()
row.operator("bim.create_shape_from_step_id")
row = layout.row()
row.prop(props, "number_of_polygons", text="")
row = layout.row()
row.operator("bim.select_high_polygon_meshes")
layout.label(text="Inspector:")
row = layout.row(align=True)
if len(props.step_id_breadcrumb) >= 2:
row.operator("bim.rewind_inspector", icon="FRAME_PREV", text="")
row.prop(props, "active_step_id", text="")
row = layout.row(align=True)
row.operator("bim.inspect_from_step_id").step_id = bpy.context.scene.BIMDebugProperties.active_step_id
row.operator("bim.inspect_from_object")
if props.attributes:
layout.label(text="Direct attributes:")
for index, attribute in enumerate(props.attributes):
row = layout.row(align=True)
row.prop(attribute, "name", text="")
row.prop(attribute, "string_value", text="")
if attribute.int_value:
row.operator(
"bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text=""
).step_id = attribute.int_value
if props.inverse_attributes:
layout.label(text="Inverse attributes:")
for index, attribute in enumerate(props.inverse_attributes):
row = layout.row(align=True)
row.prop(attribute, "name", text="")
row.prop(attribute, "string_value", text="")
if attribute.int_value:
row.operator(
"bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text=""
).step_id = attribute.int_value
def ifc_units(self, context):
scene = context.scene
props = context.scene.BIMProperties
+5
View File
@@ -376,6 +376,8 @@ int main(int argc, char** argv) {
"Creates SVG elevation drawings automatically based on model extents")
("svg-xmlns",
"Stores name and guid in a separate namespace as opposed to data-name, data-guid")
("svg-poly",
"Uses the polygonal algorithm for hidden line rendering")
("door-arcs", "Draw door openings arcs for IfcDoor elements")
("section-height", po::value<double>(&section_height),
"Specifies the cut section height for SVG 2D geometry.")
@@ -467,6 +469,7 @@ int main(int argc, char** argv) {
const bool use_element_types = vmap.count("use-element-types") != 0;
const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 0;
const bool use_z_up = vmap.count("use-z-up") != 0;
const bool no_normals = vmap.count("no-normals") != 0;
const bool center_model = vmap.count("center-model") != 0;
const bool center_model_geometry = vmap.count("center-model-geometry") != 0;
@@ -741,6 +744,7 @@ int main(int argc, char** argv) {
settings.set(SerializerSettings::USE_ELEMENT_NAMES, use_element_names);
settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids);
settings.set(SerializerSettings::USE_Z_UP, use_z_up);
settings.set(SerializerSettings::USE_ELEMENT_STEPIDS, use_element_stepids);
settings.set(SerializerSettings::USE_MATERIAL_NAMES, use_material_names);
settings.set(SerializerSettings::USE_ELEMENT_TYPES, use_element_types);
@@ -962,6 +966,7 @@ int main(int argc, char** argv) {
static_cast<SvgSerializer*>(serializer.get())->setAutoElevation(true);
}
static_cast<SvgSerializer*>(serializer.get())->setUseNamespace(vmap.count("svg-xmlns") > 0);
static_cast<SvgSerializer*>(serializer.get())->setUseHlrPoly(vmap.count("svg-poly") > 0);
if (relative_center_x && relative_center_y) {
static_cast<SvgSerializer*>(serializer.get())->setDrawingCenter(*relative_center_x, *relative_center_y);
}
+2 -19
View File
@@ -4,6 +4,7 @@
import ifcopenshell
import ifcopenshell.util.selector
import ifcopenshell.util.element
import ifcopenshell.util.schema
import csv
import lark
import argparse
@@ -13,7 +14,7 @@ class IfcAttributeExtractor:
@staticmethod
def set_element_key(ifc_file, element, key, value):
if key == "type" and element.is_a() != value:
return IfcAttributeExtractor.change_ifc_class(ifc_file, element, value)
return ifcopenshell.util.schema.reassign_class(ifc_file, element, value)
if hasattr(element, key):
setattr(element, key, value)
return element
@@ -32,24 +33,6 @@ class IfcAttributeExtractor:
return element
return element
@staticmethod
def change_ifc_class(ifc_file, element, new_class):
try:
new_element = ifc_file.create_entity(new_class)
except:
print(f"Class of {element} could not be changed to {new_class}")
return element
new_attributes = [new_element.attribute_name(i) for i, attribute in enumerate(new_element)]
for i, attribute in enumerate(element):
try:
new_element[new_attributes.index(element.attribute_name(i))] = attribute
except:
continue
for inverse in ifc_file.get_inverse(element):
ifcopenshell.util.element.replace_attribute(inverse, element, new_element)
ifc_file.remove(element)
return new_element
@staticmethod
def get_element_qto(element, name):
for relationship in element.IsDefinedBy:
@@ -63,8 +63,8 @@ def open(fn):
raise IOError("Unable to open file for reading")
def create_entity(type, *args, **kwargs):
e = entity_instance(type)
def create_entity(type, schema='IFC4', *args, **kwargs):
e = entity_instance((schema, type))
attrs = list(enumerate(args)) + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
for idx, arg in attrs:
e[idx] = arg
@@ -271,3 +271,10 @@ class entity_instance(object):
return return_type(_())
__dict__ = property(get_info)
def get_info_2(self, include_identifier=True, recursive=False, return_type=dict, ignore=()):
assert include_identifier
assert recursive
assert return_type is dict
assert len(ignore) == 0
return ifcopenshell_wrapper.get_info_cpp(self.wrapped_data)
@@ -93,3 +93,32 @@ def replace_attribute(element, old, new):
if item == old:
new_attribute[j] = new
element[i] = new_attribute
def is_representation_of_context(representation, context, subcontext=None, target_view=None):
if target_view is not None:
return (
representation.ContextOfItems.is_a("IfcGeometricRepresentationSubContext")
and representation.ContextOfItems.TargetView == target_view
and representation.ContextOfItems.ContextIdentifier == subcontext
and representation.ContextOfItems.ContextType == context
)
elif subcontext is not None:
return (
representation.ContextOfItems.is_a("IfcGeometricRepresentationSubContext")
and representation.ContextOfItems.ContextIdentifier == subcontext
and representation.ContextOfItems.ContextType == context
)
elif representation.ContextOfItems.ContextType == context:
return True
def get_representation(element, context, subcontext=None, target_view=None):
if element.is_a("IfcProduct") and element.Representation:
for r in element.Representation.Representations:
if is_representation_of_context(r, context, subcontext, target_view):
return r
elif element.is_a("IfcTypeProduct") and element.RepresentationMaps:
for r in element.RepresentationMaps:
if is_representation_of_context(r.MappedRepresentation, context, subcontext, target_view):
return r.MappedRepresentation
@@ -1,39 +1,77 @@
import pathlib
import re
from functools import lru_cache
from typing import List, Generator, Optional
import ifcopenshell
property_set_template_files = []
psets = {}
qtos = {}
applicable_psets = {}
applicable_qtos = {}
from ifcopenshell.entity_instance import entity_instance
def load_property_set_template(path):
property_set_template_files.append(ifcopenshell.open(path))
for prop in property_set_template_files[-1].by_type("IfcPropertySetTemplate"):
if prop.Name[0:4] == "Qto_":
qtos[prop.Name] = {"HasPropertyTemplates": {p.Name: p for p in prop.HasPropertyTemplates}}
entity = prop.ApplicableEntity if prop.ApplicableEntity else "IfcRoot"
applicable_qtos.setdefault(entity, []).append(prop.Name)
else:
psets[prop.Name] = {"HasPropertyTemplates": {p.Name: p for p in prop.HasPropertyTemplates}}
entity = prop.ApplicableEntity if prop.ApplicableEntity else "IfcRoot"
applicable_psets.setdefault(entity, []).append(prop.Name)
class PsetQto:
templates_path = {
"IFC4": "Pset_IFC4_ADD2.ifc",
}
def __init__(self, schema: str, templates=None) -> None:
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema)
if not templates:
folder_path = pathlib.Path(__file__).parent.absolute()
path = folder_path.joinpath("schema", self.templates_path[schema])
templates = [ifcopenshell.open(path)]
self.templates = templates
def get_applicable_psetqtos(schema_version, ifc_class, is_pset=False, is_qto=False):
def is_a(entity, ifc_class):
if entity.name() == ifc_class:
return True
return is_a(entity.supertype(), ifc_class) if entity.supertype() else False
@lru_cache()
def get_applicable(
self, ifc_class="", predefined_type="", pset_only=False, qto_only=False
) -> Generator[entity_instance, entity_instance, None]:
any_class = not ifc_class
if not any_class:
entity = self.schema.declaration_by_name(ifc_class)
for template in self.templates:
for prop_set in template.by_type("IfcPropertySetTemplate"):
if pset_only:
if prop_set.Name.startswith("Qto_"):
continue
if qto_only:
if not prop_set.Name.startswith("Qto_"):
continue
if any_class or self.is_applicable(entity, prop_set.ApplicableEntity or "IfcRoot", predefined_type):
yield prop_set
results = []
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_version)
entity = schema.declaration_by_name(ifc_class)
if is_pset:
search_items = applicable_psets.items()
elif is_qto:
search_items = applicable_qtos.items()
for ifc_class, pset_names in search_items:
if is_a(entity, ifc_class):
results.extend(pset_names)
return results
def get_applicable_names(self, ifc_class: str, predefined_type="", pset_only=False, qto_only=False) -> List[str]:
"""Return names instead of objects for other use eg. enum"""
return [prop_set.Name for prop_set in self.get_applicable(ifc_class, predefined_type, pset_only, qto_only)]
def is_applicable(self, entity: entity_instance, applicables: str, predefined_type="") -> bool:
"""applicables can have multiple possible patterns :
IfcBoilerType (IfcClass)
IfcBoilerType/STEAM (IfcClass/PREDEFINEDTYPE)
IfcBoilerType[PerformanceHistory] (IfcClass[PerformanceHistory])
IfcBoilerType/STEAM[PerformanceHistory] (IfcClass/PREDEFINEDTYPE[PerformanceHistory])
"""
for applicable in applicables.split(","):
match = re.match(r"(\w+)(\[\w+\])*/*(\w+)*(\[\w+\])*", applicable)
if not match:
continue
# Uncomment if usage found
# applicable_perf_history = match.group(2) or match.group(4)
if predefined_type and predefined_type != match.group(3):
continue
applicable_class = match.group(1)
if entity.name() == applicable_class:
return True
if entity.supertype():
return self.is_applicable(entity.supertype(), applicable_class)
return False
@lru_cache()
def get_by_name(self, name: str) -> Optional[entity_instance]:
for template in self.templates:
for prop_set in template.by_type("IfcPropertySetTemplate"):
if prop_set.Name == name:
return prop_set
return None
def is_templated(self, name: str) -> bool:
return bool(self.get_by_name(name))
@@ -18,6 +18,25 @@ def is_a(entity, ifc_class):
return False
def reassign_class(ifc_file, element, new_class):
try:
new_element = ifc_file.create_entity(new_class)
except:
print(f"Class of {element} could not be changed to {new_class}")
return element
new_attributes = [new_element.attribute_name(i) for i, attribute in enumerate(new_element)]
for i, attribute in enumerate(element):
try:
new_element[new_attributes.index(element.attribute_name(i))] = attribute
except:
continue
for inverse in ifc_file.get_inverse(element):
ifcopenshell.util.element.replace_attribute(inverse, element, new_element)
ifc_file.remove(element)
return new_element
class Migrator:
def __init__(self):
self.migrated_ids = {}
@@ -142,3 +142,17 @@ def convert(value, from_prefix, from_unit, to_prefix, to_unit):
value *= 1 / get_prefix_multiplier(to_prefix)
value *= 1 / get_prefix_multiplier(to_prefix)
return value
def calculate_unit_scale(file):
units = file.by_type("IfcUnitAssignment")[0]
unit_scale = 1
for unit in units.Units:
if not hasattr(unit, "UnitType") or unit.UnitType != "LENGTHUNIT":
continue
while unit.is_a("IfcConversionBasedUnit"):
unit_scale *= unit.ConversionFactor.ValueComponent.wrappedValue
unit = unit.ConversionFactor.UnitComponent
if unit.is_a("IfcSIUnit"):
unit_scale *= get_prefix_multiplier(unit.Prefix)
return unit_scale
+6 -1
View File
@@ -1866,7 +1866,12 @@ void IfcFile::removeEntity(IfcUtil::IfcBaseClass* entity) {
if (entity->declaration().is(*ifcroot_type_)) {
const std::string global_id = *entity->data().getArgument(0);
byguid.erase(byguid.find(global_id));
auto it = byguid.find(global_id);
if (it != byguid.end()) {
byguid.erase(it);
} else {
Logger::Warning("GlobalId on rooted instance not encountered in map");
}
}
byid.erase(byid.find(id));
+1 -2
View File
@@ -84,8 +84,7 @@ IF(PYTHONINTERP_FOUND AND NOT "${PYTHON_EXECUTABLE}" STREQUAL "")
MESSAGE(WARNING "Unable to locate Python site-package directory, unable to install the Python wrapper")
ELSE()
FILE(GLOB_RECURSE sourcefiles
"${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/*.py"
"${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/*.bnf"
"${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/*"
)
FOREACH(file ${sourcefiles})
FILE(RELATIVE_PATH relative "${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/" "${file}")
+119
View File
@@ -678,3 +678,122 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas
}
%}
%{
PyObject* get_info_cpp(IfcUtil::IfcBaseClass* v);
// @todo refactor this to remove duplication with the typemap.
// except this is calls the above function in case of instances.
PyObject* convert_cpp_attribute_to_python(IfcUtil::ArgumentType type, Argument& arg) {
if (!arg.isNull() && type != IfcUtil::Argument_DERIVED) {
try {
switch(type) {
case IfcUtil::Argument_INT: {
int v = arg;
return pythonize(v);
break; }
case IfcUtil::Argument_BOOL: {
bool v = arg;
return pythonize(v);
break; }
case IfcUtil::Argument_DOUBLE: {
double v = arg;
return pythonize(v);
break; }
case IfcUtil::Argument_ENUMERATION:
case IfcUtil::Argument_STRING: {
std::string v = arg;
return pythonize(v);
break; }
case IfcUtil::Argument_BINARY: {
boost::dynamic_bitset<> v = arg;
return pythonize(v);
break; }
case IfcUtil::Argument_AGGREGATE_OF_INT: {
std::vector<int> v = arg;
return pythonize_vector(v);
break; }
case IfcUtil::Argument_AGGREGATE_OF_DOUBLE: {
std::vector<double> v = arg;
return pythonize_vector(v);
break; }
case IfcUtil::Argument_AGGREGATE_OF_STRING: {
std::vector<std::string> v = arg;
return pythonize_vector(v);
break; }
case IfcUtil::Argument_ENTITY_INSTANCE: {
IfcUtil::IfcBaseClass* v = arg;
return get_info_cpp(v);
break; }
case IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE: {
IfcEntityList::ptr v = arg;
auto r = PyTuple_New(v->size());
for (unsigned i = 0; i < v->size(); ++i) {
PyTuple_SetItem(r, i, get_info_cpp((*v)[i]));
}
return r;
break; }
case IfcUtil::Argument_AGGREGATE_OF_BINARY: {
std::vector< boost::dynamic_bitset<> > v = arg;
return pythonize_vector(v);
break; }
case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_INT: {
std::vector< std::vector<int> > v = arg;
return pythonize_vector2(v);
break; }
case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE: {
std::vector< std::vector<double> > v = arg;
return pythonize_vector2(v);
break; }
case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE: {
IfcEntityListList::ptr vs = arg;
auto rs = PyTuple_New(vs->size());
for (auto it = vs->begin(); it != vs->end(); ++it) {
IfcEntityList::ptr v_i = arg;
auto r = PyTuple_New(v_i->size());
for (unsigned i = 0; i < v_i->size(); ++i) {
PyTuple_SetItem(r, i, get_info_cpp((*v_i)[i]));
}
PyTuple_SetItem(rs, std::distance(vs->begin(), it), r);
}
return rs;
break; }
case IfcUtil::Argument_EMPTY_AGGREGATE: {
return PyTuple_New(0);
break; }
}
} catch(...) {}
}
Py_INCREF(Py_None);
return Py_None;
}
%}
%inline %{
PyObject* get_info_cpp(IfcUtil::IfcBaseClass* v) {
PyObject *d = PyDict_New();
const std::vector<const IfcParse::attribute*> attrs = v->declaration().as_entity()->all_attributes();
std::vector<const IfcParse::attribute*>::const_iterator it = attrs.begin();
for (; it != attrs.end(); ++it) {
const std::string& name_cpp = (*it)->name();
auto name_py = pythonize(name_cpp);
auto attr_type = IfcUtil::from_parameter_type((*it)->type_of_attribute());
auto value_cpp = v->data().getArgument(std::distance(attrs.begin(), it));
auto value_py = convert_cpp_attribute_to_python(attr_type, *value_cpp);
PyDict_SetItem(d, name_py, value_py);
}
// @todo type and id can be static?
const std::string& type_cpp = "type";
auto type_py = pythonize(type_cpp);
const std::string& type_v_cpp = v->declaration().name();
auto type_v_py = pythonize(type_v_cpp);
PyDict_SetItem(d, type_py, type_v_py);
const std::string& id_cpp = "id";
auto id_py = pythonize(id_cpp);
auto id_v_py = pythonize(v->data().id());
PyDict_SetItem(d, id_py, id_v_py);
return d;
}
%}
-1
View File
@@ -54,7 +54,6 @@ public:
/// Applicable for OBJ, DAE, and SVG output.
USE_ELEMENT_STEPIDS = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 6),
/// Use step Z UP .
/// Applicable for OBJ output.
USE_Z_UP = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 7),
/// Number of different setting flags.
NUM_SETTINGS = 7
+117 -45
View File
@@ -24,6 +24,7 @@
#include <cstdio>
#include <limits>
#include <algorithm>
#include <numeric>
#include <gp_Pln.hxx>
#include <gp_Trsf.hxx>
@@ -67,6 +68,8 @@
#include <ShapeFix_Edge.hxx>
#include <HLRBRep_PolyHLRToShape.hxx>
#include "../ifcparse/IfcGlobalId.h"
#include "SvgSerializer.h"
@@ -292,16 +295,18 @@ void SvgSerializer::write(path_object& p, const TopoDS_Wire& wire) {
p.second.push_back(path);
}
SvgSerializer::path_object& SvgSerializer::start_path(IfcUtil::IfcBaseEntity* storey, const std::string& id) {
SvgSerializer::path_object& SvgSerializer::start_path(const gp_Pln& pln, IfcUtil::IfcBaseEntity* storey, const std::string& id) {
auto key = std::make_pair(std::make_pair(storey, ""), path_object());
SvgSerializer::path_object& p = paths.insert(key)->second;
drawing_metadata[key.first].pln_3d = pln;
p.first = id;
return p;
}
SvgSerializer::path_object& SvgSerializer::start_path(const std::string& drawing_name, const std::string& id) {
SvgSerializer::path_object& SvgSerializer::start_path(const gp_Pln& pln, const std::string& drawing_name, const std::string& id) {
auto key = std::make_pair(std::make_pair(nullptr, drawing_name), path_object());
SvgSerializer::path_object& p = paths.insert(key)->second;
drawing_metadata[key.first].pln_3d = pln;
p.first = id;
return p;
}
@@ -563,7 +568,7 @@ void SvgSerializer::write(const geometry_data& data) {
auto& compound_to_use = is_floor_plan_ ? compound : compound_unmirrored;
if (use_hlr && hlr) {
if (use_hlr && (hlr_poly || hlr_brep)) {
// Check if any of the bounding box points is on the correct side of the plane
Bnd_Box bb;
@@ -590,13 +595,19 @@ void SvgSerializer::write(const geometry_data& data) {
} else {
state = d < 0. ? -1 : 1;
}
if (state == 1) {
if (state == -1) {
any = true;
}
}
if (any) {
hlr->Add(compound_to_use);
if (hlr_brep) {
hlr_brep->Add(compound_to_use);
}
if (hlr_poly) {
BRepMesh_IncrementalMesh(compound_to_use, 0.10);
hlr_poly->Load(compound_to_use);
}
}
}
@@ -633,6 +644,15 @@ void SvgSerializer::write(const geometry_data& data) {
cut_z = zmin + 1.;
}
gp_Pln pln;
if (variant.which() < 2) {
pln = gp_Pln(gp_Pnt(0, 0, cut_z), gp::DZ());
}
else {
const auto& section = boost::get<vertical_section>(variant);
pln = section.plane;
}
gp_Vec bbmin(x1, y1, zmin);
gp_Vec bbmax(x2, y2, zmax);
auto bbdif = bbmax - bbmin;
@@ -641,9 +661,9 @@ void SvgSerializer::write(const geometry_data& data) {
if (data.product->declaration().is("IfcAnnotation") && (proj.Magnitude() > 1.e-5) && zmin >= range.first && zmin <= range.second) {
if (po == nullptr) {
if (storey) {
po = &start_path(storey, data.svg_name);
po = &start_path(pln, storey, data.svg_name);
} else {
po = &start_path(drawing_name, data.svg_name);
po = &start_path(pln, drawing_name, data.svg_name);
}
}
@@ -717,20 +737,12 @@ void SvgSerializer::write(const geometry_data& data) {
if (po == nullptr) {
if (storey) {
po = &start_path(storey, data.svg_name);
po = &start_path(pln, storey, data.svg_name);
} else {
po = &start_path(drawing_name, data.svg_name);
po = &start_path(pln, drawing_name, data.svg_name);
}
}
// Create a horizontal cross section 1 meter above the bottom point of the shape
gp_Pln pln;
if (variant.which() < 2) {
pln = gp_Pln(gp_Pnt(0, 0, cut_z), gp::DZ());
} else {
const auto& section = boost::get<vertical_section>(variant);
pln = section.plane;
}
TopoDS_Shape result = BRepAlgoAPI_Section(subshape, pln);
if (variant.which() == 2) {
@@ -889,7 +901,10 @@ void SvgSerializer::setBoundingRectangle(double width, double height) {
this->rescale = true;
}
void SvgSerializer::resize() {
std::array<std::array<double, 3>, 3> SvgSerializer::resize() {
// identity matrix;
std::array<std::array<double, 3>, 3> m = {{ {{1,0,0}},{{0,1,0}},{{0,0,1}} }};
if (rescale) {
// Scale the resulting image to a bounding rectangle specified by command line arguments
const double dx = xmax - xmin;
@@ -918,6 +933,8 @@ void SvgSerializer::resize() {
cy = ymin * sc;
}
m = {{ {{sc,0,-cx}},{{0,sc,-cy}},{{0,0,1}} }};
float_item_list::const_iterator it;
for (it = xcoords.begin() + xcoords_begin; it != xcoords.end(); ++it, ++xcoords_begin) {
double& v = (*it)->value();
@@ -931,6 +948,8 @@ void SvgSerializer::resize() {
(*it)->value() *= sc;
}
}
return m;
}
void SvgSerializer::resetScale() {
@@ -944,7 +963,12 @@ void SvgSerializer::resetScale() {
}
void SvgSerializer::finalize() {
resize();
auto m = resize();
// Update the paper space scale matrices
for (auto& p : paths) {
drawing_metadata[p.first].matrix_3 = m;
}
if (!deferred_section_data_.is_initialized() && (auto_section_ || auto_elevation_)) {
deferred_section_data_.emplace();
@@ -972,28 +996,28 @@ void SvgSerializer::finalize() {
if (auto_elevation_) {
{
gp_Pln pln(gp_Ax3(
gp_Pnt((xmin + xmax) / 2., -(ymin - 1.e-1), 0.),
gp_Dir(0, -1, 0),
gp_Dir(-1, 0, 0)));
gp_Pnt(0., -(ymin - 10.), 0.),
gp_Dir(0, 1, 0),
gp_Dir(1, 0, 0)));
deferred_section_data_->push_back(vertical_section{ pln , "Elevation South", true });
}
{
gp_Pln pln(gp_Ax3(
gp_Pnt(xmax + 1.e-1, (ymin + ymax) / -2., 0.),
gp_Dir(-1, 0, 0),
gp_Dir(0, -1, 0)));
gp_Pnt(xmax + 10., 0., 0.),
gp_Dir(1, 0, 0),
gp_Dir(0, 1, 0)));
deferred_section_data_->push_back(vertical_section{ pln , "Elevation East", true });
}
{
gp_Pln pln(gp_Ax3(
gp_Pnt((xmin + xmax) / 2., -(ymax + 1.e-1), 0.),
gp_Pnt(0., -(ymax + 10.), 0.),
gp_Dir(0, -1, 0),
gp_Dir(1, 0, 0)));
gp_Dir(-1, 0, 0)));
deferred_section_data_->push_back(vertical_section{ pln , "Elevation North", true });
}
{
gp_Pln pln(gp_Ax3(
gp_Pnt(xmin - 1.e-1, (ymin + ymax) / -2., 0.),
gp_Pnt(xmin - 10., 0., 0.),
gp_Dir(-1, 0, 0),
gp_Dir(0, -1, 0)));
deferred_section_data_->push_back(vertical_section{ pln , "Elevation West", true });
@@ -1001,7 +1025,6 @@ void SvgSerializer::finalize() {
}
resetScale();
if (deferred_section_data_ && deferred_section_data_->size() && element_buffer_.size()) {
@@ -1018,7 +1041,11 @@ void SvgSerializer::finalize() {
}
if (use_hlr) {
hlr = new HLRBRep_Algo;
if (use_hlr_poly_) {
hlr_poly = new HLRBRep_PolyAlgo;
} else {
hlr_brep = new HLRBRep_Algo;
}
}
section_data_ = std::vector<section_data>{ sd };
@@ -1028,18 +1055,30 @@ void SvgSerializer::finalize() {
if (use_hlr) {
const auto& section = boost::get<vertical_section>(sd);
gp_Ax2 transform = section.plane.Position().Ax2();
HLRAlgo_Projector projector(transform);
hlr->Projector(projector);
hlr->Update();
hlr->Hide();
gp_Trsf trsf;
trsf.SetTransformation(section.plane.Position());
HLRAlgo_Projector projector(trsf, false, 1.);
TopoDS_Shape hlr_compound_unmirrored;
if (use_hlr_poly_) {
hlr_poly->Projector(projector);
HLRBRep_HLRToShape hlr_shapes(hlr);
auto hlr_compound_unmirrored = hlr_shapes.VCompound();
hlr_poly->Update();
HLRBRep_PolyHLRToShape hlr_shapes;
hlr_shapes.Update(hlr_poly);
hlr_compound_unmirrored = hlr_shapes.VCompound();
} else {
hlr_brep->Projector(projector);
hlr_brep->Update();
hlr_brep->Hide();
HLRBRep_HLRToShape hlr_shapes(hlr_brep);
hlr_compound_unmirrored = hlr_shapes.VCompound();
}
if (!hlr_compound_unmirrored.IsNull()) {
// Compound 3D curves for mirroring to work
ShapeFix_Edge sfe;
TopExp_Explorer exp(hlr_compound_unmirrored, TopAbs_EDGE);
@@ -1059,7 +1098,7 @@ void SvgSerializer::finalize() {
exp.Init(hlr_compound, TopAbs_EDGE);
BRep_Builder B;
auto& po = start_path(drawing_name, "class=\"projection\"");
auto& po = start_path(section.plane, drawing_name, "class=\"projection\"");
for (; exp.More(); exp.Next()) {
TopoDS_Wire w;
B.MakeWire(w);
@@ -1070,12 +1109,15 @@ void SvgSerializer::finalize() {
}
}
resize();
auto m3 = resize();
auto k = std::make_pair(nullptr, drawing_name);
drawing_metadata[k].matrix_3 = m3;
resetScale();
if (use_hlr) {
hlr.Nullify();
}
if (hlr_brep) hlr_brep.Nullify();
if (hlr_poly) hlr_poly.Nullify();
}
}
@@ -1089,11 +1131,11 @@ void SvgSerializer::finalize() {
}
std::ostringstream oss;
if (it->first.first) {
svg_file << " <g " << nameElement(it->first.first) << ">\n";
svg_file << " <g " << nameElement(it->first.first) << " " << writeMetadata(drawing_metadata[it->first]) << ">\n";
} else {
auto n = it->first.second;
IfcUtil::escape_xml(n);
svg_file << " <g " << namespace_prefix_ << "name=\"" << n << "\" class=\"section\">\n";
svg_file << " <g " << namespace_prefix_ << "name=\"" << n << "\" class=\"section\" " << writeMetadata(drawing_metadata[it->first]) << ">\n";
}
}
svg_file << " <g " << it->second.first << ">\n";
@@ -1285,3 +1327,33 @@ void SvgSerializer::setSectionHeightsFromStoreys(double offset) {
section_data_->push_back(horizontal_plan_at_element{});
}
}
namespace {
std::string array_to_string(double v) {
return std::to_string(v);
}
template <typename T>
std::string array_to_string(const T& v) {
return "[" + std::accumulate(
v.begin() + 1, v.end(),
array_to_string(v.front()),
[](const std::string& accum, decltype(*v.cbegin())& item) {
return accum + "," + array_to_string(item);
}) + "]";
}
}
std::string SvgSerializer::writeMetadata(const drawing_meta& m) {
gp_Trsf trsf;
trsf.SetTransformation(m.pln_3d.Position(), gp::XOY());
auto m43 = IfcGeom::Matrix<real_t>(IfcGeom::ElementSettings(IfcGeom::IteratorSettings(), 1., ""), trsf).data();
std::array<std::array<double, 4>, 4> m4 = {{
{{ (double)m43[0], (double)m43[3], (double)m43[6], (double)m43[9] }},
{{ (double)m43[1], (double)m43[4], (double)m43[7], (double)m43[10] }},
{{ (double)m43[2], (double)m43[5], (double)m43[8], (double)m43[11] }},
{{ 0, 0, 0, 1 }}
}};
return namespace_prefix_ + "plane=\""+ array_to_string(m4) +"\" " +
namespace_prefix_ + "matrix3=\"" + array_to_string(m.matrix_3) + "\"";
}
+22 -5
View File
@@ -29,6 +29,8 @@
#include <HLRBRep_Algo.hxx>
#include <HLRBRep_HLRToShape.hxx>
#include <HLRBRep_PolyAlgo.hxx>
#include <HLRAlgo_Projector.hxx>
#include <gp_Pln.hxx>
#include <sstream>
@@ -107,6 +109,11 @@ struct geometry_data {
std::string ifc_name, svg_name;
};
struct drawing_meta {
gp_Pln pln_3d;
std::array<std::array<double, 3>, 3> matrix_3;
};
class SvgSerializer : public GeometrySerializer {
public:
typedef std::pair<std::string, std::vector<util::string_buffer> > path_object;
@@ -121,11 +128,12 @@ protected:
bool with_section_heights_from_storey_, rescale, print_space_names_, print_space_areas_;
bool draw_door_arcs_, is_floor_plan_;
bool auto_section_, auto_elevation_;
bool use_namespace_;
bool use_namespace_, use_hlr_poly_;
IfcParse::IfcFile* file;
IfcUtil::IfcBaseEntity* storey_;
std::multimap<drawing_key, path_object, storey_sorter> paths;
std::map<drawing_key, drawing_meta> drawing_metadata;
float_item_list xcoords, ycoords, radii;
size_t xcoords_begin, ycoords_begin, radii_begin;
@@ -134,7 +142,8 @@ protected:
std::list<geometry_data> element_buffer_;
Handle(HLRBRep_Algo) hlr;
Handle(HLRBRep_Algo) hlr_brep;
Handle(HLRBRep_PolyAlgo) hlr_poly;
std::string namespace_prefix_;
public:
@@ -154,6 +163,7 @@ public:
, auto_section_(false)
, auto_elevation_(false)
, use_namespace_(false)
, use_hlr_poly_(false)
, file(0)
, storey_(0)
, xcoords_begin(0)
@@ -171,8 +181,8 @@ public:
void write(const IfcGeom::BRepElement<real_t>* o);
void write(path_object& p, const TopoDS_Wire& wire);
void write(const geometry_data& data);
path_object& start_path(IfcUtil::IfcBaseEntity* storey, const std::string& id);
path_object& start_path(const std::string& drawing_name, const std::string& id);
path_object& start_path(const gp_Pln& p, IfcUtil::IfcBaseEntity* storey, const std::string& id);
path_object& start_path(const gp_Pln& p, const std::string& drawing_name, const std::string& id);
bool isTesselated() const { return false; }
void finalize();
void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
@@ -184,7 +194,7 @@ public:
void setPrintSpaceAreas(bool b) { print_space_areas_ = b; }
void setDrawDoorArcs(bool b) { draw_door_arcs_ = b; }
void resize();
std::array<std::array<double, 3>, 3> resize();
void resetScale();
void setSectionRef(const boost::optional<std::string>& s) {
@@ -207,6 +217,10 @@ public:
namespace_prefix_ = use_namespace_ ? "ifc:" : "data-";
}
void setUseHlrPoly(bool b) {
use_hlr_poly_ = b;
}
void setScale(double s) { scale_ = s; }
void setDrawingCenter(double x, double y) {
center_x_ = x; center_y_ = y;
@@ -221,6 +235,9 @@ public:
return GeometrySerializer::object_id(o);
}
}
protected:
std::string writeMetadata(const drawing_meta& m);
};
#endif