mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 02:47:48 +00:00
merge master v6.0
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
"""
|
||||
# Liang–Barsky 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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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>(§ion_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
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
%}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) + "\"";
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user