mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 23:36:20 +00:00
merge master v6.0
This commit is contained in:
@@ -1,12 +1,70 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
from behave.model import Scenario
|
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):
|
def before_all(context):
|
||||||
|
|
||||||
|
# get from userdata
|
||||||
userdata = context.config.userdata
|
userdata = context.config.userdata
|
||||||
context.ifcbasename = userdata["ifcbasename"]
|
context.ifcbasename = userdata["ifcbasename"]
|
||||||
context.localedir = userdata.get("localedir")
|
context.localedir = userdata.get("localedir")
|
||||||
|
|
||||||
# do not break after a failed scenario
|
# do not break after a failed scenario
|
||||||
# https://community.osarch.org/discussion/comment/3328/#Comment_3328
|
# 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
|
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 behave import step
|
||||||
|
|
||||||
from attributes_eleclasses_methods import all_element_attribs_have_a_value
|
import attributes_eleclasses_methods as aem
|
||||||
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
|
|
||||||
from utils import assert_elements
|
from utils import assert_elements
|
||||||
from utils import IfcFile
|
from utils import IfcFile
|
||||||
from utils import switch_locale
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "en"
|
||||||
|
|
||||||
|
|
||||||
@step("there are no {ifc_class} elements because {reason}")
|
@step("there are no {ifc_class} elements because {reason}")
|
||||||
def step_impl(context, ifc_class, reason):
|
def step_impl(context, ifc_class, reason):
|
||||||
switch_locale(context.localedir, "en")
|
switch_locale(context.localedir, the_lang)
|
||||||
no_element_class_ele(context, ifc_class, reason)
|
aem.no_eleclass_because_reason(
|
||||||
|
context,
|
||||||
|
ifc_class,
|
||||||
|
reason
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@step('all {ifc_class} elements class attributes have a value')
|
@step('all {ifc_class} elements class attributes have a value')
|
||||||
def step_impl(context, ifc_class):
|
def step_impl(context, ifc_class):
|
||||||
switch_locale(context.localedir, "en")
|
switch_locale(context.localedir, the_lang)
|
||||||
all_element_attribs_have_a_value(context, ifc_class)
|
aem.eleclass_have_class_attributes_with_a_value(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@step('all {ifc_class} elements have a name given')
|
@step('all {ifc_class} elements have a name given')
|
||||||
def step_impl(context, ifc_class):
|
def step_impl(context, ifc_class):
|
||||||
switch_locale(context.localedir, "en")
|
switch_locale(context.localedir, the_lang)
|
||||||
name_has_a_value(context, ifc_class)
|
aem.eleclass_has_name_with_a_value(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@step('all {ifc_class} elements have a description given')
|
@step('all {ifc_class} elements have a description given')
|
||||||
def step_impl(context, ifc_class):
|
def step_impl(context, ifc_class):
|
||||||
switch_locale(context.localedir, "en")
|
switch_locale(context.localedir, the_lang)
|
||||||
description_has_a_value(context, ifc_class)
|
aem.eleclass_has_description_with_a_value(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@step('all {ifc_class} elements have a name matching the pattern "{pattern}"')
|
@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
|
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.falseelems = []
|
||||||
context.falseguids = []
|
context.falseguids = []
|
||||||
@@ -41,7 +43,9 @@ def no_element_class_ele(context, ifc_class, reason):
|
|||||||
assert False, _("Error in falsecount, something went wrong.")
|
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
|
from ifcopenshell.ifcopenshell_wrapper import schema_by_name
|
||||||
# schema = schema_by_name("IFC2X3")
|
# 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.falseelems = []
|
||||||
context.falseguids = []
|
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.falseelems = []
|
||||||
context.falseguids = []
|
context.falseguids = []
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
from behave import step
|
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 assert_elements
|
||||||
from utils import IfcFile
|
from utils import IfcFile
|
||||||
from utils import switch_locale
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "en"
|
||||||
|
|
||||||
|
|
||||||
@step("all {ifc_class} elements have an {aproperty} property in the {pset} pset")
|
@step("all {ifc_class} elements have an {aproperty} property in the {pset} pset")
|
||||||
def step_impl(context, ifc_class, aproperty, pset):
|
def step_impl(context, ifc_class, aproperty, pset):
|
||||||
switch_locale(context.localedir, "en")
|
switch_locale(context.localedir, the_lang)
|
||||||
all_eleclass_elements_have_prop_in_pset(context, ifc_class, aproperty, pset)
|
apm.eleclass_has_property_in_pset(
|
||||||
|
context,
|
||||||
|
ifc_class,
|
||||||
|
aproperty,
|
||||||
|
pset
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
# ------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ from utils import assert_elements
|
|||||||
from utils import IfcFile
|
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.falseelems = []
|
||||||
context.falseguids = []
|
context.falseguids = []
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
from behave import step
|
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 assert_elements
|
||||||
from utils import IfcFile
|
from utils import IfcFile
|
||||||
from utils import switch_locale
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "en"
|
||||||
|
|
||||||
|
|
||||||
@step("All elements must be under {number} polygons")
|
@step("All elements must be under {number} polygons")
|
||||||
def step_impl(context, number):
|
def step_impl(context, number):
|
||||||
number = int(number)
|
number = int(number)
|
||||||
@@ -36,5 +39,9 @@ def step_impl(context, number):
|
|||||||
|
|
||||||
@step("all {ifc_class} elements have an {representation_class} representation")
|
@step("all {ifc_class} elements have an {representation_class} representation")
|
||||||
def step_impl(context, ifc_class, representation_class):
|
def step_impl(context, ifc_class, representation_class):
|
||||||
switch_locale(context.localedir, "en")
|
switch_locale(context.localedir, the_lang)
|
||||||
class_geometric_representation(context, ifc_class, representation_class)
|
gdm.eleclass_has_geometric_representation_of_specific_class(
|
||||||
|
context,
|
||||||
|
ifc_class,
|
||||||
|
representation_class
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
from behave import step
|
from behave import step
|
||||||
|
|
||||||
from geometric_detail_methods import class_geometric_representation
|
import geometric_detail_methods as gdm
|
||||||
from utils import switch_locale
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "de"
|
||||||
|
|
||||||
|
|
||||||
@step("Alle {ifc_class} Bauteile müssen eine geometrische Repräsentation der Klasse {representation_class} verwenden")
|
@step("Alle {ifc_class} Bauteile müssen eine geometrische Repräsentation der Klasse {representation_class} verwenden")
|
||||||
def step_impl(context, ifc_class, representation_class):
|
def step_impl(context, ifc_class, representation_class):
|
||||||
switch_locale(context.localedir, "de")
|
switch_locale(context.localedir, the_lang)
|
||||||
class_geometric_representation(context, ifc_class, representation_class)
|
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
|
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):
|
def is_item_a_representation(item, representation):
|
||||||
if "/" in representation:
|
if "/" in representation:
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ from ifcdata_methods import assert_schema
|
|||||||
from utils import IfcFile
|
from utils import IfcFile
|
||||||
from utils import switch_locale
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "en"
|
||||||
|
|
||||||
|
|
||||||
@step('The IFC schema "{schema}" must be provided')
|
@step('The IFC schema "{schema}" must be provided')
|
||||||
def step_impl(context, schema):
|
def step_impl(context, schema):
|
||||||
try:
|
try:
|
||||||
@@ -15,6 +19,7 @@ def step_impl(context, schema):
|
|||||||
except:
|
except:
|
||||||
assert False, f"The schema {schema} could not be loaded"
|
assert False, f"The schema {schema} could not be loaded"
|
||||||
|
|
||||||
|
|
||||||
@step('The IFC file "{file}" must be provided')
|
@step('The IFC file "{file}" must be provided')
|
||||||
def step_impl(context, file):
|
def step_impl(context, file):
|
||||||
try:
|
try:
|
||||||
@@ -22,6 +27,7 @@ def step_impl(context, file):
|
|||||||
except:
|
except:
|
||||||
assert False, f"The file {file} could not be loaded"
|
assert False, f"The file {file} could not be loaded"
|
||||||
|
|
||||||
|
|
||||||
@given('The IFC file has been provided through an argument')
|
@given('The IFC file has been provided through an argument')
|
||||||
def step_impl(context):
|
def step_impl(context):
|
||||||
try:
|
try:
|
||||||
@@ -29,6 +35,7 @@ def step_impl(context):
|
|||||||
except:
|
except:
|
||||||
assert False, f"The IFC {context.config.userdata.get('ifcfile')} file could not be loaded"
|
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')
|
@given('A file path has been provided through an argument')
|
||||||
def step_impl(context):
|
def step_impl(context):
|
||||||
try:
|
try:
|
||||||
@@ -36,9 +43,10 @@ def step_impl(context):
|
|||||||
except:
|
except:
|
||||||
assert False, f"The path {context.config.userdata.get('path')} could not be loaded"
|
assert False, f"The path {context.config.userdata.get('path')} could not be loaded"
|
||||||
|
|
||||||
|
|
||||||
@step("IFC data must use the {schema} schema")
|
@step("IFC data must use the {schema} schema")
|
||||||
def step_impl(context, schema):
|
def step_impl(context, schema):
|
||||||
switch_locale(context.localedir, "en")
|
switch_locale(context.localedir, the_lang)
|
||||||
assert_schema(context, schema)
|
assert_schema(context, schema)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ from behave import step
|
|||||||
from ifcdata_methods import assert_schema
|
from ifcdata_methods import assert_schema
|
||||||
from utils import switch_locale
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "de"
|
||||||
|
|
||||||
|
|
||||||
@step("Die IFC Daten müssen das {schema} Schema benutzen")
|
@step("Die IFC Daten müssen das {schema} Schema benutzen")
|
||||||
def step_impl(context, schema):
|
def step_impl(context, schema):
|
||||||
switch_locale(context.localedir, "de")
|
switch_locale(context.localedir, the_lang)
|
||||||
assert_schema(context, schema)
|
assert_schema(context, schema)
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ from behave import step
|
|||||||
from ifcdata_methods import assert_schema
|
from ifcdata_methods import assert_schema
|
||||||
from utils import switch_locale
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "fr"
|
||||||
|
|
||||||
|
|
||||||
@step("Les données IFC doivent utiliser le schéma {schema}")
|
@step("Les données IFC doivent utiliser le schéma {schema}")
|
||||||
def step_impl(context, schema):
|
def step_impl(context, schema):
|
||||||
switch_locale(context.localedir, "fr")
|
switch_locale(context.localedir, the_lang)
|
||||||
assert_schema(context, schema)
|
assert_schema(context, schema)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import ifcopenshell.util.geolocation
|
import ifcopenshell.util.geolocation
|
||||||
import numpy as np
|
import ifcopenshell.util.placement
|
||||||
from behave import step
|
from behave import step
|
||||||
|
|
||||||
from utils import assert_number
|
from utils import assert_number
|
||||||
@@ -7,31 +7,6 @@ from utils import assert_type
|
|||||||
from utils import IfcFile
|
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):
|
def get_decimal_points(value):
|
||||||
try:
|
try:
|
||||||
return len(value.split(".")[1])
|
return len(value.split(".")[1])
|
||||||
@@ -83,7 +58,7 @@ def step_impl(context, guid, easting, northing, elevation):
|
|||||||
element = IfcFile.by_guid(guid)
|
element = IfcFile.by_guid(guid)
|
||||||
if not element.ObjectPlacement:
|
if not element.ObjectPlacement:
|
||||||
assert False, "The element does not have an object placement: {}".format(element)
|
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(
|
e, n, h = ifcopenshell.util.geolocation.xyz2enh(
|
||||||
m[0][3],
|
m[0][3],
|
||||||
m[1][3],
|
m[1][3],
|
||||||
@@ -110,7 +85,7 @@ def step_impl(context, guid, x, y, z):
|
|||||||
element = IfcFile.by_guid(guid)
|
element = IfcFile.by_guid(guid)
|
||||||
if not element.ObjectPlacement:
|
if not element.ObjectPlacement:
|
||||||
assert False, "The element does not have an object placement: {}".format(element)
|
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_x = round(m[0][3], get_decimal_points(x))
|
||||||
element_y = round(m[1][3], get_decimal_points(y))
|
element_y = round(m[1][3], get_decimal_points(y))
|
||||||
element_z = round(m[2][3], get_decimal_points(z))
|
element_z = round(m[2][3], get_decimal_points(z))
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ def generate_report(
|
|||||||
html_template_file_path=""
|
html_template_file_path=""
|
||||||
):
|
):
|
||||||
|
|
||||||
# print("# Generating HTML reports now.")
|
print("# Generating HTML reports now.")
|
||||||
|
|
||||||
# get locale path
|
# get locale path
|
||||||
localedir = os.path.join(
|
localedir = os.path.join(
|
||||||
@@ -150,7 +150,7 @@ def generate_report(
|
|||||||
|
|
||||||
|
|
||||||
def get_html_template_strings():
|
def get_html_template_strings():
|
||||||
print(_("OpenBIM auditing is a feature of"))
|
|
||||||
return {
|
return {
|
||||||
"tr_lang": _("en"),
|
"tr_lang": _("en"),
|
||||||
"tr_success": _("Success"),
|
"tr_success": _("Success"),
|
||||||
|
|||||||
@@ -144,13 +144,13 @@ def run_intmp_tests(args={}):
|
|||||||
)
|
)
|
||||||
return False
|
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"] != "":
|
if "features" in args and args["features"] != "":
|
||||||
features_path = os.path.join(args["features"], "features")
|
the_features_path = os.path.join(args["features"], "features")
|
||||||
if not os.path.isdir(features_path):
|
if not os.path.isdir(the_features_path):
|
||||||
print(
|
print(
|
||||||
"The features directory does not exist: {}"
|
"The features directory does not exist: {}"
|
||||||
.format(features_path)
|
.format(the_features_path)
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
@@ -191,23 +191,47 @@ def run_intmp_tests(args={}):
|
|||||||
os.mkdir(run_path)
|
os.mkdir(run_path)
|
||||||
report_path = os.path.join(run_path, "report")
|
report_path = os.path.join(run_path, "report")
|
||||||
copy_features_path = os.path.join(run_path, "features")
|
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
|
# copy features files
|
||||||
# print(features_path)
|
# print(the_features_path)
|
||||||
# print(copy_features_path)
|
# print(copy_features_path)
|
||||||
if os.path.exists(features_path):
|
# parameter dirs_exist_ok=True, from py 3.8
|
||||||
shutil.copytree(features_path, copy_features_path)
|
# if os.path.exists(the_features_path):
|
||||||
|
# shutil.copytree(
|
||||||
|
# the_features_path,
|
||||||
|
# copy_features_path,
|
||||||
|
# dirs_exist_ok=True
|
||||||
|
# )
|
||||||
|
|
||||||
# replace ifcpath in feature files
|
# copy feature files and replace ifcpath in feature files
|
||||||
# IMHO better than copy the ifc file which could be 500 MB
|
# replaceing is IMHO better than copy the ifc file which could be 500 MB
|
||||||
feature_files = os.listdir(copy_features_path)
|
feature_files = os.listdir(the_features_path)
|
||||||
# print(feature_files)
|
# print(feature_files)
|
||||||
for feature_file in 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)
|
# print(feature_file)
|
||||||
|
# copy file
|
||||||
|
shutil.copyfile(
|
||||||
|
os.path.join(the_features_path, feature_file),
|
||||||
|
cp_feature_file
|
||||||
|
)
|
||||||
# search the line
|
# search the line
|
||||||
ff = open(feature_file, "r")
|
ff = open(cp_feature_file, "r")
|
||||||
lines = ff.readlines()
|
lines = ff.readlines()
|
||||||
ff.close()
|
ff.close()
|
||||||
theline = ""
|
theline = ""
|
||||||
@@ -228,32 +252,10 @@ def run_intmp_tests(args={}):
|
|||||||
# replace the line
|
# replace the line
|
||||||
if newifcline != "":
|
if newifcline != "":
|
||||||
# https://stackoverflow.com/a/290494
|
# 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
|
# the print replaces the line in the file
|
||||||
print(line.replace(theline, newifcline), end="")
|
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
|
# get advanced args
|
||||||
# print to console from inside step files, add "--no-capture" flag
|
# print to console from inside step files, add "--no-capture" flag
|
||||||
# https://github.com/behave/behave/issues/346
|
# https://github.com/behave/behave/issues/346
|
||||||
|
|||||||
@@ -5,15 +5,25 @@ bpy = sys.modules.get("bpy")
|
|||||||
|
|
||||||
if bpy is not None:
|
if bpy is not None:
|
||||||
import bpy
|
import bpy
|
||||||
import blenderbim.bim.module.covetool as module_covetool
|
import blenderbim.bim.module.root as module_root
|
||||||
import blenderbim.bim.module.model as module_model
|
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.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
|
from . import ui, prop, operator
|
||||||
|
|
||||||
classes = [
|
classes = [
|
||||||
operator.ReassignClass,
|
|
||||||
operator.AssignClass,
|
|
||||||
operator.UnassignClass,
|
|
||||||
operator.SelectClass,
|
operator.SelectClass,
|
||||||
operator.SelectType,
|
operator.SelectType,
|
||||||
operator.OpenUri,
|
operator.OpenUri,
|
||||||
@@ -27,7 +37,6 @@ if bpy is not None:
|
|||||||
operator.ValidateIfcFile,
|
operator.ValidateIfcFile,
|
||||||
operator.ExportIFC,
|
operator.ExportIFC,
|
||||||
operator.ImportIFC,
|
operator.ImportIFC,
|
||||||
operator.ProfileImportIFC,
|
|
||||||
operator.ColourByClass,
|
operator.ColourByClass,
|
||||||
operator.ColourByAttribute,
|
operator.ColourByAttribute,
|
||||||
operator.ColourByPset,
|
operator.ColourByPset,
|
||||||
@@ -45,8 +54,6 @@ if bpy is not None:
|
|||||||
operator.SelectSweptSolidInnerCurves,
|
operator.SelectSweptSolidInnerCurves,
|
||||||
operator.AssignSweptSolidExtrusion,
|
operator.AssignSweptSolidExtrusion,
|
||||||
operator.SelectSweptSolidExtrusion,
|
operator.SelectSweptSolidExtrusion,
|
||||||
operator.AddPset,
|
|
||||||
operator.RemovePset,
|
|
||||||
operator.AddQto,
|
operator.AddQto,
|
||||||
operator.RemoveQto,
|
operator.RemoveQto,
|
||||||
operator.AddMaterialPset,
|
operator.AddMaterialPset,
|
||||||
@@ -65,18 +72,6 @@ if bpy is not None:
|
|||||||
operator.AssignConstraint,
|
operator.AssignConstraint,
|
||||||
operator.UnassignConstraint,
|
operator.UnassignConstraint,
|
||||||
operator.RemoveObjectConstraint,
|
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.AddDocumentInformation,
|
||||||
operator.RemoveDocumentInformation,
|
operator.RemoveDocumentInformation,
|
||||||
operator.AssignDocumentInformation,
|
operator.AssignDocumentInformation,
|
||||||
@@ -86,11 +81,8 @@ if bpy is not None:
|
|||||||
operator.UnassignDocumentReference,
|
operator.UnassignDocumentReference,
|
||||||
operator.RemoveObjectDocumentReference,
|
operator.RemoveObjectDocumentReference,
|
||||||
operator.GenerateGlobalId,
|
operator.GenerateGlobalId,
|
||||||
operator.AddAttribute,
|
|
||||||
operator.RemoveAttribute,
|
|
||||||
operator.AddMaterialAttribute,
|
operator.AddMaterialAttribute,
|
||||||
operator.RemoveMaterialAttribute,
|
operator.RemoveMaterialAttribute,
|
||||||
operator.QuickProjectSetup,
|
|
||||||
operator.SelectGlobalId,
|
operator.SelectGlobalId,
|
||||||
operator.SelectAttribute,
|
operator.SelectAttribute,
|
||||||
operator.SelectPset,
|
operator.SelectPset,
|
||||||
@@ -107,8 +99,6 @@ if bpy is not None:
|
|||||||
operator.FetchLibraryInformation,
|
operator.FetchLibraryInformation,
|
||||||
operator.FetchExternalMaterial,
|
operator.FetchExternalMaterial,
|
||||||
operator.FetchObjectPassport,
|
operator.FetchObjectPassport,
|
||||||
operator.AddSubcontext,
|
|
||||||
operator.RemoveSubcontext,
|
|
||||||
operator.CutSection,
|
operator.CutSection,
|
||||||
operator.AddSheet,
|
operator.AddSheet,
|
||||||
operator.OpenSheet,
|
operator.OpenSheet,
|
||||||
@@ -133,8 +123,6 @@ if bpy is not None:
|
|||||||
operator.SmartClashGroup,
|
operator.SmartClashGroup,
|
||||||
operator.SelectSmartGroup,
|
operator.SelectSmartGroup,
|
||||||
operator.LoadSmartGroupsForActiveClashSet,
|
operator.LoadSmartGroupsForActiveClashSet,
|
||||||
operator.SwitchContext,
|
|
||||||
operator.RemoveContext,
|
|
||||||
operator.OpenUpstream,
|
operator.OpenUpstream,
|
||||||
operator.BIM_OT_ChangeClassificationLevel,
|
operator.BIM_OT_ChangeClassificationLevel,
|
||||||
operator.AddPropertySetTemplate,
|
operator.AddPropertySetTemplate,
|
||||||
@@ -151,7 +139,6 @@ if bpy is not None:
|
|||||||
operator.ImportIfcCsv,
|
operator.ImportIfcCsv,
|
||||||
operator.EyedropIfcCsv,
|
operator.EyedropIfcCsv,
|
||||||
operator.ReloadIfcFile,
|
operator.ReloadIfcFile,
|
||||||
operator.SelectSimilarType,
|
|
||||||
operator.AddIfcFile,
|
operator.AddIfcFile,
|
||||||
operator.RemoveIfcFile,
|
operator.RemoveIfcFile,
|
||||||
operator.SelectDocIfcFile,
|
operator.SelectDocIfcFile,
|
||||||
@@ -161,7 +148,6 @@ if bpy is not None:
|
|||||||
operator.AddVariable,
|
operator.AddVariable,
|
||||||
operator.RemoveVariable,
|
operator.RemoveVariable,
|
||||||
operator.PropagateTextData,
|
operator.PropagateTextData,
|
||||||
operator.PushRepresentation,
|
|
||||||
operator.ConvertLocalToGlobal,
|
operator.ConvertLocalToGlobal,
|
||||||
operator.ConvertGlobalToLocal,
|
operator.ConvertGlobalToLocal,
|
||||||
operator.GuessQuantity,
|
operator.GuessQuantity,
|
||||||
@@ -203,15 +189,7 @@ if bpy is not None:
|
|||||||
operator.RemoveDrawingStyleAttribute,
|
operator.RemoveDrawingStyleAttribute,
|
||||||
operator.CopyPropertyToSelection,
|
operator.CopyPropertyToSelection,
|
||||||
operator.CopyAttributeToSelection,
|
operator.CopyAttributeToSelection,
|
||||||
operator.CreateShapeFromStepId,
|
|
||||||
operator.SelectHighPolygonMeshes,
|
|
||||||
operator.InspectFromStepId,
|
|
||||||
operator.InspectFromObject,
|
|
||||||
operator.RewindInspector,
|
|
||||||
operator.RefreshDrawingList,
|
operator.RefreshDrawingList,
|
||||||
operator.GetRepresentationIfcParameters,
|
|
||||||
operator.BakeParametricGeometry,
|
|
||||||
operator.UpdateIfcRepresentation,
|
|
||||||
operator.SetBlenderClashSetA,
|
operator.SetBlenderClashSetA,
|
||||||
operator.SetBlenderClashSetB,
|
operator.SetBlenderClashSetB,
|
||||||
operator.ExecuteBlenderClash,
|
operator.ExecuteBlenderClash,
|
||||||
@@ -245,11 +223,8 @@ if bpy is not None:
|
|||||||
prop.Schedule,
|
prop.Schedule,
|
||||||
prop.DrawingStyle,
|
prop.DrawingStyle,
|
||||||
prop.Sheet,
|
prop.Sheet,
|
||||||
prop.Subcontext,
|
|
||||||
prop.Representation,
|
|
||||||
prop.PresentationLayer,
|
prop.PresentationLayer,
|
||||||
prop.BIMProperties,
|
prop.BIMProperties,
|
||||||
prop.BIMDebugProperties,
|
|
||||||
prop.DocProperties,
|
prop.DocProperties,
|
||||||
prop.BIMLibrary,
|
prop.BIMLibrary,
|
||||||
prop.MapConversion,
|
prop.MapConversion,
|
||||||
@@ -278,10 +253,6 @@ if bpy is not None:
|
|||||||
ui.BIM_PT_search,
|
ui.BIM_PT_search,
|
||||||
ui.BIM_PT_ifccsv,
|
ui.BIM_PT_ifccsv,
|
||||||
ui.BIM_PT_ifcclash,
|
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_qa,
|
||||||
ui.BIM_PT_library,
|
ui.BIM_PT_library,
|
||||||
ui.BIM_PT_gis,
|
ui.BIM_PT_gis,
|
||||||
@@ -290,15 +261,10 @@ if bpy is not None:
|
|||||||
ui.BIM_PT_cobie,
|
ui.BIM_PT_cobie,
|
||||||
ui.BIM_PT_patch,
|
ui.BIM_PT_patch,
|
||||||
ui.BIM_PT_mvd,
|
ui.BIM_PT_mvd,
|
||||||
ui.BIM_PT_debug,
|
|
||||||
ui.BIM_PT_material,
|
ui.BIM_PT_material,
|
||||||
ui.BIM_PT_mesh,
|
|
||||||
ui.BIM_PT_presentation_layer_data,
|
ui.BIM_PT_presentation_layer_data,
|
||||||
ui.BIM_PT_object,
|
|
||||||
ui.BIM_PT_object_material,
|
ui.BIM_PT_object_material,
|
||||||
ui.BIM_PT_object_psets,
|
|
||||||
ui.BIM_PT_object_qto,
|
ui.BIM_PT_object_qto,
|
||||||
ui.BIM_PT_representations,
|
|
||||||
ui.BIM_PT_classification_references,
|
ui.BIM_PT_classification_references,
|
||||||
ui.BIM_PT_documents,
|
ui.BIM_PT_documents,
|
||||||
ui.BIM_PT_constraint_relations,
|
ui.BIM_PT_constraint_relations,
|
||||||
@@ -322,9 +288,22 @@ if bpy is not None:
|
|||||||
ui.BIM_ADDON_preferences,
|
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_bcf.classes)
|
||||||
|
classes.extend(module_context.classes)
|
||||||
classes.extend(module_covetool.classes)
|
classes.extend(module_covetool.classes)
|
||||||
|
classes.extend(module_debug.classes)
|
||||||
|
classes.extend(module_geometry.classes)
|
||||||
classes.extend(module_model.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):
|
def menu_func_export(self, context):
|
||||||
self.layout.operator(operator.ExportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcjson)")
|
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_export.append(menu_func_export)
|
||||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||||
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
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.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
|
||||||
bpy.types.Scene.BIMLibrary = bpy.props.PointerProperty(type=prop.BIMLibrary)
|
bpy.types.Scene.BIMLibrary = bpy.props.PointerProperty(type=prop.BIMLibrary)
|
||||||
bpy.types.Scene.MapConversion = bpy.props.PointerProperty(type=prop.MapConversion)
|
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.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
|
||||||
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
|
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
|
||||||
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
|
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
|
||||||
|
module_root.register()
|
||||||
|
module_aggregate.register()
|
||||||
|
module_attribute.register()
|
||||||
module_bcf.register()
|
module_bcf.register()
|
||||||
|
module_context.register()
|
||||||
module_covetool.register()
|
module_covetool.register()
|
||||||
|
module_debug.register()
|
||||||
|
module_geometry.register()
|
||||||
module_model.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.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
|
||||||
|
bpy.app.handlers.load_post.append(prop.toggleDecorationsOnLoad)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
for cls in reversed(classes):
|
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_export.remove(menu_func_export)
|
||||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||||
del bpy.types.Scene.BIMProperties
|
del bpy.types.Scene.BIMProperties
|
||||||
del bpy.types.Scene.BIMDebugProperties
|
|
||||||
del bpy.types.Scene.DocProperties
|
del bpy.types.Scene.DocProperties
|
||||||
del bpy.types.Scene.MapConversion
|
del bpy.types.Scene.MapConversion
|
||||||
del bpy.types.Scene.TargetCRS
|
del bpy.types.Scene.TargetCRS
|
||||||
@@ -379,7 +370,20 @@ if bpy is not None:
|
|||||||
del bpy.types.Camera.BIMCameraProperties
|
del bpy.types.Camera.BIMCameraProperties
|
||||||
del bpy.types.TextCurve.BIMTextProperties
|
del bpy.types.TextCurve.BIMTextProperties
|
||||||
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
|
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_model.unregister()
|
||||||
|
module_geometry.unregister()
|
||||||
|
module_debug.unregister()
|
||||||
module_covetool.unregister()
|
module_covetool.unregister()
|
||||||
|
module_context.unregister()
|
||||||
module_bcf.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)
|
bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler)
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class BaseDecorator():
|
|||||||
|
|
||||||
DEF_GLSL = """
|
DEF_GLSL = """
|
||||||
#define PI 3.141592653589793
|
#define PI 3.141592653589793
|
||||||
#define COLOR vec4({color[0]}, {color[1]}, {color[2]}, {color[3]})
|
|
||||||
#define MAX_POINTS 64
|
#define MAX_POINTS 64
|
||||||
#define CIRCLE_SEGS 24
|
#define CIRCLE_SEGS 24
|
||||||
"""
|
"""
|
||||||
@@ -99,63 +98,28 @@ class BaseDecorator():
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
FRAG_GLSL = """
|
FRAG_GLSL = """
|
||||||
|
uniform vec4 color;
|
||||||
out vec4 fragColor;
|
out vec4 fragColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
fragColor = COLOR;
|
fragColor = color;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# class var for single handler
|
def __init__(self):
|
||||||
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)
|
|
||||||
# NB: libcode param doesn't work
|
# NB: libcode param doesn't work
|
||||||
return GPUShader(vertexcode=self.VERT_GLSL,
|
self.shader = GPUShader(vertexcode=self.VERT_GLSL,
|
||||||
fragcode=self.FRAG_GLSL,
|
fragcode=self.FRAG_GLSL,
|
||||||
geocode=self.LIB_GLSL + self.GEOM_GLSL,
|
geocode=self.LIB_GLSL + self.GEOM_GLSL,
|
||||||
defines=defines)
|
defines=self.DEF_GLSL)
|
||||||
|
|
||||||
def __call__(self):
|
def get_objects(self, collection):
|
||||||
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):
|
|
||||||
"""find relevant objects
|
"""find relevant objects
|
||||||
using class.basename
|
using class.basename
|
||||||
|
|
||||||
returns: iterable of blender objects
|
returns: iterable of blender objects
|
||||||
"""
|
"""
|
||||||
drawing = self.props.drawings[self.props.active_drawing_index]
|
return filter(lambda o: self.basename in o.name, collection.all_objects)
|
||||||
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
|
|
||||||
return filter(lambda o: self.basename in o.name, collection.objects)
|
|
||||||
|
|
||||||
def get_path_geom(self, obj, topo=True):
|
def get_path_geom(self, obj, topo=True):
|
||||||
"""Parses path geometry into line segments
|
"""Parses path geometry into line segments
|
||||||
@@ -223,13 +187,14 @@ class BaseDecorator():
|
|||||||
|
|
||||||
return vertices, indices
|
return vertices, indices
|
||||||
|
|
||||||
def decorate(self, object):
|
def decorate(self, context, object):
|
||||||
"""perform actuall drawing stuff"""
|
"""perform actuall drawing stuff"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def draw_lines(self, obj, vertices, indices, topology=None):
|
def draw_lines(self, context, obj, vertices, indices, topology=None):
|
||||||
region = self.context.region
|
region = context.region
|
||||||
region3d = self.context.region_data
|
region3d = context.region_data
|
||||||
|
color = context.scene.DocProperties.decorations_colour
|
||||||
|
|
||||||
fmt = GPUVertFormat()
|
fmt = GPUVertFormat()
|
||||||
fmt.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
|
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)
|
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.bind()
|
||||||
self.shader.uniform_float
|
self.shader.uniform_float
|
||||||
self.shader.uniform_float("viewMatrix", region3d.perspective_matrix)
|
self.shader.uniform_float("viewMatrix", region3d.perspective_matrix)
|
||||||
self.shader.uniform_float("winsize", (region.width, region.height))
|
self.shader.uniform_float("winsize", (region.width, region.height))
|
||||||
|
self.shader.uniform_float("color", color)
|
||||||
|
|
||||||
batch.draw(self.shader)
|
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
|
"""Draw text label
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -259,15 +231,21 @@ class BaseDecorator():
|
|||||||
|
|
||||||
aligned and centered at segment middle
|
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)
|
ang = -Vector((1, 0)).angle_signed(dir)
|
||||||
cos = math.cos(ang)
|
cos = math.cos(ang)
|
||||||
sin = math.sin(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
|
w, h = 0, 0
|
||||||
if center or vcenter:
|
if center or vcenter:
|
||||||
w, h = blf.dimensions(self.font_id, text)
|
w, h = blf.dimensions(font_id, text)
|
||||||
|
|
||||||
if center:
|
if center:
|
||||||
# horizontal centering
|
# horizontal centering
|
||||||
@@ -281,18 +259,18 @@ class BaseDecorator():
|
|||||||
# side-shifting
|
# side-shifting
|
||||||
pos += Vector((-sin, cos)) * gap
|
pos += Vector((-sin, cos)) * gap
|
||||||
|
|
||||||
blf.enable(self.font_id, blf.ROTATION)
|
blf.enable(font_id, blf.ROTATION)
|
||||||
blf.position(self.font_id, pos.x, pos.y, 0)
|
blf.position(font_id, pos.x, pos.y, 0)
|
||||||
|
|
||||||
blf.rotation(self.font_id, ang)
|
blf.rotation(font_id, ang)
|
||||||
blf.color(self.font_id, *self.props.decorations_colour)
|
blf.color(font_id, *color)
|
||||||
blf.draw(self.font_id, text)
|
blf.draw(font_id, text)
|
||||||
blf.disable(self.font_id, blf.ROTATION)
|
blf.disable(font_id, blf.ROTATION)
|
||||||
|
|
||||||
def format_value(self, value):
|
def format_value(self, context, value):
|
||||||
unit_system = bpy.context.scene.unit_settings.system
|
unit_system = context.scene.unit_settings.system
|
||||||
if unit_system == 'IMPERIAL':
|
if unit_system == 'IMPERIAL':
|
||||||
precision = bpy.context.scene.BIMProperties.imperial_precision
|
precision = context.scene.BIMProperties.imperial_precision
|
||||||
if precision == "NONE":
|
if precision == "NONE":
|
||||||
precision = 256
|
precision = 256
|
||||||
elif precision == "1":
|
elif precision == "1":
|
||||||
@@ -313,7 +291,6 @@ class DimensionDecorator(BaseDecorator):
|
|||||||
- puts metric text next to each segment
|
- puts metric text next to each segment
|
||||||
"""
|
"""
|
||||||
basename = "IfcAnnotation/Dimension"
|
basename = "IfcAnnotation/Dimension"
|
||||||
installed = None
|
|
||||||
|
|
||||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||||
#define ARROW_ANGLE PI / 12.0
|
#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)
|
verts, idxs, _ = self.get_path_geom(obj, topo=False)
|
||||||
self.draw_lines(obj, verts, idxs)
|
self.draw_lines(context, obj, verts, idxs)
|
||||||
self.draw_labels(obj, verts, idxs)
|
self.draw_labels(context, obj, verts, idxs)
|
||||||
|
|
||||||
def draw_labels(self, obj, vertices, indices):
|
def draw_labels(self, context, obj, vertices, indices):
|
||||||
region = self.context.region
|
region = context.region
|
||||||
region3d = self.context.region_data
|
region3d = context.region_data
|
||||||
for i0, i1 in indices:
|
for i0, i1 in indices:
|
||||||
v0 = Vector(vertices[i0])
|
v0 = Vector(vertices[i0])
|
||||||
v1 = Vector(vertices[i1])
|
v1 = Vector(vertices[i1])
|
||||||
@@ -394,8 +371,8 @@ class DimensionDecorator(BaseDecorator):
|
|||||||
if dir.length < 1:
|
if dir.length < 1:
|
||||||
continue
|
continue
|
||||||
length = (v1 - v0).length
|
length = (v1 - v0).length
|
||||||
text = self.format_value(length)
|
text = self.format_value(context, length)
|
||||||
self.draw_label(text, p0 + (dir) * .5, dir)
|
self.draw_label(context, text, p0 + (dir) * .5, dir)
|
||||||
|
|
||||||
|
|
||||||
class EqualityDecorator(DimensionDecorator):
|
class EqualityDecorator(DimensionDecorator):
|
||||||
@@ -405,11 +382,10 @@ class EqualityDecorator(DimensionDecorator):
|
|||||||
- puts 'EQ' label
|
- puts 'EQ' label
|
||||||
"""
|
"""
|
||||||
basename = "IfcAnnotation/Equal"
|
basename = "IfcAnnotation/Equal"
|
||||||
installed = None
|
|
||||||
|
|
||||||
def draw_labels(self, obj, vertices, indices):
|
def draw_labels(self, context, obj, vertices, indices):
|
||||||
region = self.context.region
|
region = context.region
|
||||||
region3d = self.context.region_data
|
region3d = context.region_data
|
||||||
for i0, i1 in indices:
|
for i0, i1 in indices:
|
||||||
v0 = Vector(vertices[i0])
|
v0 = Vector(vertices[i0])
|
||||||
v1 = Vector(vertices[i1])
|
v1 = Vector(vertices[i1])
|
||||||
@@ -418,7 +394,7 @@ class EqualityDecorator(DimensionDecorator):
|
|||||||
dir = p1 - p0
|
dir = p1 - p0
|
||||||
if dir.length < 1:
|
if dir.length < 1:
|
||||||
continue
|
continue
|
||||||
self.draw_label("EQ", p0 + (dir) * .5, dir)
|
self.draw_label(context, "EQ", p0 + (dir) * .5, dir)
|
||||||
|
|
||||||
|
|
||||||
class LeaderDecorator(BaseDecorator):
|
class LeaderDecorator(BaseDecorator):
|
||||||
@@ -427,7 +403,6 @@ class LeaderDecorator(BaseDecorator):
|
|||||||
- middle points w/out decorations
|
- middle points w/out decorations
|
||||||
"""
|
"""
|
||||||
basename = "IfcAnnotation/Leader"
|
basename = "IfcAnnotation/Leader"
|
||||||
installed = None
|
|
||||||
|
|
||||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||||
#define ARROW_ANGLE PI / 12.0
|
#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)
|
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):
|
class StairDecorator(BaseDecorator):
|
||||||
@@ -496,7 +471,6 @@ class StairDecorator(BaseDecorator):
|
|||||||
- middle points w/out decorations
|
- middle points w/out decorations
|
||||||
"""
|
"""
|
||||||
basename = "IfcAnnotation/Stair"
|
basename = "IfcAnnotation/Stair"
|
||||||
installed = None
|
|
||||||
|
|
||||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||||
#define CIRCLE_SIZE 8.0
|
#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)
|
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):
|
class HiddenDecorator(BaseDecorator):
|
||||||
basename = "IfcAnnotation/Hidden"
|
basename = "IfcAnnotation/Hidden"
|
||||||
installed = None
|
|
||||||
|
|
||||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||||
#define DASH_SIZE 16.0
|
#define DASH_SIZE 16.0
|
||||||
@@ -619,6 +592,7 @@ class HiddenDecorator(BaseDecorator):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
FRAG_GLSL = """
|
FRAG_GLSL = """
|
||||||
|
uniform vec4 color;
|
||||||
in vec2 gl_FragCoord;
|
in vec2 gl_FragCoord;
|
||||||
in float dist;
|
in float dist;
|
||||||
out vec4 fragColor;
|
out vec4 fragColor;
|
||||||
@@ -626,21 +600,20 @@ class HiddenDecorator(BaseDecorator):
|
|||||||
void main() {
|
void main() {
|
||||||
uint bit = uint(fract(dist / DASH_SIZE) * 32);
|
uint bit = uint(fract(dist / DASH_SIZE) * 32);
|
||||||
if ((DASH_PATTERN & (1U<<bit)) == 0U) discard;
|
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:
|
if obj.data.is_editmode:
|
||||||
verts, idxs = self.get_editmesh_geom(obj)
|
verts, idxs = self.get_editmesh_geom(obj)
|
||||||
else:
|
else:
|
||||||
verts, idxs = self.get_mesh_geom(obj)
|
verts, idxs = self.get_mesh_geom(obj)
|
||||||
self.draw_lines(obj, verts, idxs)
|
self.draw_lines(context, obj, verts, idxs)
|
||||||
|
|
||||||
|
|
||||||
class MiscDecorator(HiddenDecorator):
|
class MiscDecorator(HiddenDecorator):
|
||||||
basename = "IfcAnnotation/Misc"
|
basename = "IfcAnnotation/Misc"
|
||||||
installed = None
|
|
||||||
|
|
||||||
FRAG_GLSL = BaseDecorator.FRAG_GLSL
|
FRAG_GLSL = BaseDecorator.FRAG_GLSL
|
||||||
|
|
||||||
@@ -661,16 +634,15 @@ class LevelDecorator(BaseDecorator):
|
|||||||
continue
|
continue
|
||||||
yield [obj.matrix_world @ p.co for p in spline_points]
|
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)
|
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)
|
splines = self.get_splines(obj)
|
||||||
self.draw_labels(obj, splines)
|
self.draw_labels(context, obj, splines)
|
||||||
|
|
||||||
|
|
||||||
class PlanDecorator(LevelDecorator):
|
class PlanDecorator(LevelDecorator):
|
||||||
basename = "IfcAnnotation/Plan Level"
|
basename = "IfcAnnotation/Plan Level"
|
||||||
installed = None
|
|
||||||
|
|
||||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||||
#define CIRCLE_SIZE 8.0
|
#define CIRCLE_SIZE 8.0
|
||||||
@@ -736,9 +708,9 @@ class PlanDecorator(LevelDecorator):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def draw_labels(self, obj, splines):
|
def draw_labels(self, context, obj, splines):
|
||||||
region = self.context.region
|
region = context.region
|
||||||
region3d = self.context.region_data
|
region3d = context.region_data
|
||||||
for verts in splines:
|
for verts in splines:
|
||||||
v0 = verts[0]
|
v0 = verts[0]
|
||||||
v1 = verts[1]
|
v1 = verts[1]
|
||||||
@@ -747,13 +719,12 @@ class PlanDecorator(LevelDecorator):
|
|||||||
dir = p1 - p0
|
dir = p1 - p0
|
||||||
if dir.length < 1:
|
if dir.length < 1:
|
||||||
continue
|
continue
|
||||||
text = "RL " + self.format_value(verts[-1].z)
|
text = "RL " + self.format_value(context, verts[-1].z)
|
||||||
self.draw_label(text, p0, dir, gap=8, center=False)
|
self.draw_label(context, text, p0, dir, gap=8, center=False)
|
||||||
|
|
||||||
|
|
||||||
class SectionDecorator(LevelDecorator):
|
class SectionDecorator(LevelDecorator):
|
||||||
basename = "IfcAnnotation/Section Level"
|
basename = "IfcAnnotation/Section Level"
|
||||||
installed = None
|
|
||||||
|
|
||||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||||
#define CALLOUT_GAP 8.0
|
#define CALLOUT_GAP 8.0
|
||||||
@@ -816,9 +787,9 @@ class SectionDecorator(LevelDecorator):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def draw_labels(self, obj, splines):
|
def draw_labels(self, context, obj, splines):
|
||||||
region = self.context.region
|
region = context.region
|
||||||
region3d = self.context.region_data
|
region3d = context.region_data
|
||||||
for verts in splines:
|
for verts in splines:
|
||||||
v0 = verts[0]
|
v0 = verts[0]
|
||||||
v1 = verts[1]
|
v1 = verts[1]
|
||||||
@@ -827,8 +798,8 @@ class SectionDecorator(LevelDecorator):
|
|||||||
dir = p1 - p0
|
dir = p1 - p0
|
||||||
if dir.length < 1:
|
if dir.length < 1:
|
||||||
continue
|
continue
|
||||||
text = "RL " + self.format_value(verts[-1].z)
|
text = "RL " + self.format_value(context, verts[-1].z)
|
||||||
self.draw_label(text, p0 + dir.normalized() * 16, -dir, gap=16, center=False)
|
self.draw_label(context, text, p0 + dir.normalized() * 16, -dir, gap=16, center=False)
|
||||||
|
|
||||||
|
|
||||||
class BreakDecorator(BaseDecorator):
|
class BreakDecorator(BaseDecorator):
|
||||||
@@ -838,7 +809,6 @@ class BreakDecorator(BaseDecorator):
|
|||||||
Uses first two vertices in verts list.
|
Uses first two vertices in verts list.
|
||||||
"""
|
"""
|
||||||
basename = "IfcAnnotation/Break"
|
basename = "IfcAnnotation/Break"
|
||||||
installed = None
|
|
||||||
|
|
||||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||||
#define BREAK_LENGTH 32.0
|
#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:
|
if obj.data.is_editmode:
|
||||||
verts = self.get_editmesh_geom(obj)
|
verts = self.get_editmesh_geom(obj)
|
||||||
else:
|
else:
|
||||||
verts = self.get_mesh_geom(obj)
|
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):
|
def get_mesh_geom(self, obj):
|
||||||
# first vertices only
|
# first vertices only
|
||||||
@@ -914,7 +884,6 @@ class BreakDecorator(BaseDecorator):
|
|||||||
|
|
||||||
class GridDecorator(BaseDecorator):
|
class GridDecorator(BaseDecorator):
|
||||||
basename = "IfcGridAxis/"
|
basename = "IfcGridAxis/"
|
||||||
installed = None
|
|
||||||
|
|
||||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||||
#define CIRCLE_SIZE 16.0
|
#define CIRCLE_SIZE 16.0
|
||||||
@@ -983,6 +952,7 @@ class GridDecorator(BaseDecorator):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
FRAG_GLSL = """
|
FRAG_GLSL = """
|
||||||
|
uniform vec4 color;
|
||||||
in vec2 gl_FragCoord;
|
in vec2 gl_FragCoord;
|
||||||
in float dist;
|
in float dist;
|
||||||
out vec4 fragColor;
|
out vec4 fragColor;
|
||||||
@@ -990,7 +960,7 @@ class GridDecorator(BaseDecorator):
|
|||||||
void main() {
|
void main() {
|
||||||
uint bit = uint(fract(dist / DASH_SIZE) * 32);
|
uint bit = uint(fract(dist / DASH_SIZE) * 32);
|
||||||
if ((DASH_PATTERN & (1U<<bit)) == 0U) discard;
|
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]
|
vertices = [obj.matrix_world @ v.co for v in mesh.edges[0].verts]
|
||||||
return vertices
|
return vertices
|
||||||
|
|
||||||
def decorate(self, obj):
|
def decorate(self, context, obj):
|
||||||
if obj.data.is_editmode:
|
if obj.data.is_editmode:
|
||||||
verts = self.get_editmesh_geom(obj)
|
verts = self.get_editmesh_geom(obj)
|
||||||
else:
|
else:
|
||||||
verts = self.get_mesh_geom(obj)
|
verts = self.get_mesh_geom(obj)
|
||||||
self.draw_lines(obj, verts, [(0, 1)])
|
self.draw_lines(context, obj, verts, [(0, 1)])
|
||||||
self.draw_labels(obj, verts)
|
self.draw_labels(context, obj, verts)
|
||||||
|
|
||||||
def draw_labels(self, obj, vertices):
|
def draw_labels(self, context, obj, vertices):
|
||||||
region = self.context.region
|
region = context.region
|
||||||
region3d = self.context.region_data
|
region3d = context.region_data
|
||||||
v0 = Vector(vertices[0])
|
v0 = Vector(vertices[0])
|
||||||
v1 = Vector(vertices[1])
|
v1 = Vector(vertices[1])
|
||||||
p0 = location_3d_to_region_2d(region, region3d, v0)
|
p0 = location_3d_to_region_2d(region, region3d, v0)
|
||||||
p1 = location_3d_to_region_2d(region, region3d, v1)
|
p1 = location_3d_to_region_2d(region, region3d, v1)
|
||||||
dir = Vector((1, 0))
|
dir = Vector((1, 0))
|
||||||
text = obj.BIMObjectProperties.attributes['AxisTag'].string_value
|
text = obj.BIMObjectProperties.attributes['AxisTag'].string_value
|
||||||
self.draw_label(text, p0, dir, vcenter=True, gap=0)
|
self.draw_label(context, text, p0, dir, vcenter=True, gap=0)
|
||||||
self.draw_label(text, p1, dir, vcenter=True, gap=0)
|
self.draw_label(context, text, p1, dir, vcenter=True, gap=0)
|
||||||
|
|
||||||
|
|
||||||
all_decorators = [
|
class DecorationsHandler():
|
||||||
DimensionDecorator,
|
decorators_classes = [
|
||||||
EqualityDecorator,
|
DimensionDecorator,
|
||||||
GridDecorator,
|
EqualityDecorator,
|
||||||
HiddenDecorator,
|
GridDecorator,
|
||||||
LeaderDecorator,
|
HiddenDecorator,
|
||||||
MiscDecorator,
|
LeaderDecorator,
|
||||||
PlanDecorator,
|
MiscDecorator,
|
||||||
SectionDecorator,
|
PlanDecorator,
|
||||||
StairDecorator,
|
SectionDecorator,
|
||||||
BreakDecorator
|
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 zipfile
|
||||||
import tempfile
|
import tempfile
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.pset
|
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
@@ -60,7 +59,6 @@ class IfcParser:
|
|||||||
self.rel_contained_in_spatial_structure = {}
|
self.rel_contained_in_spatial_structure = {}
|
||||||
self.rel_nests = {}
|
self.rel_nests = {}
|
||||||
self.rel_space_boundaries = {}
|
self.rel_space_boundaries = {}
|
||||||
self.rel_defines_by_type = {}
|
|
||||||
self.rel_defines_by_qto = {}
|
self.rel_defines_by_qto = {}
|
||||||
self.rel_defines_by_pset = {}
|
self.rel_defines_by_pset = {}
|
||||||
self.rel_associates_document_object = {}
|
self.rel_associates_document_object = {}
|
||||||
@@ -372,9 +370,6 @@ class IfcParser:
|
|||||||
product.update(metadata_override)
|
product.update(metadata_override)
|
||||||
|
|
||||||
type_product = obj.BIMObjectProperties.relating_type
|
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"]:
|
if product["has_boundary_condition"]:
|
||||||
product["boundary_condition_class"] = obj.BIMObjectProperties.boundary_condition.name
|
product["boundary_condition_class"] = obj.BIMObjectProperties.boundary_condition.name
|
||||||
@@ -472,39 +467,28 @@ class IfcParser:
|
|||||||
continue
|
continue
|
||||||
results[item_key] = {"ifc": None, "raw": raw, "material": material, "attributes": {"Name": item.name}}
|
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:
|
if not obj.data:
|
||||||
return
|
return
|
||||||
qto_names = self.get_applicable_qtos(ifc_class)
|
applicable_qtos = schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
|
||||||
for name in qto_names:
|
for applicable_qto in applicable_qtos:
|
||||||
if name not in ifcopenshell.util.pset.qtos:
|
|
||||||
continue
|
|
||||||
has_automatic_value = False
|
has_automatic_value = False
|
||||||
props = ifcopenshell.util.pset.qtos[name]["HasPropertyTemplates"].keys()
|
|
||||||
guessed_values = {}
|
guessed_values = {}
|
||||||
for prop_name in props:
|
prop_names = [p.Name for p in applicable_qto.HasPropertyTemplates]
|
||||||
value = self.qto_calculator.guess_quantity(prop_name, props, obj)
|
for prop_name in prop_names:
|
||||||
|
value = self.qto_calculator.guess_quantity(prop_name, prop_names, obj)
|
||||||
if value:
|
if value:
|
||||||
guessed_values[prop_name] = value
|
guessed_values[prop_name] = value
|
||||||
has_automatic_value = True
|
has_automatic_value = True
|
||||||
if has_automatic_value:
|
if has_automatic_value:
|
||||||
qto = obj.BIMObjectProperties.qtos.add()
|
qto = obj.BIMObjectProperties.qtos.add()
|
||||||
qto.name = name
|
qto.name = applicable_qto.Name
|
||||||
for prop_name in props:
|
for prop_name in prop_names:
|
||||||
prop = qto.properties.add()
|
prop = qto.properties.add()
|
||||||
prop.name = prop_name
|
prop.name = prop_name
|
||||||
if prop_name in guessed_values:
|
if prop_name in guessed_values:
|
||||||
prop.string_value = str(guessed_values[prop_name])
|
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):
|
def get_product_relating_structure(self, product, obj):
|
||||||
relating_structure = obj.BIMObjectProperties.relating_structure
|
relating_structure = obj.BIMObjectProperties.relating_structure
|
||||||
if relating_structure:
|
if relating_structure:
|
||||||
@@ -1057,29 +1041,32 @@ class IfcParser:
|
|||||||
for context in self.ifc_export_settings.context_tree:
|
for context in self.ifc_export_settings.context_tree:
|
||||||
for subcontext in context["subcontexts"]:
|
for subcontext in context["subcontexts"]:
|
||||||
for target_view in subcontext["target_views"]:
|
for target_view in subcontext["target_views"]:
|
||||||
rep_context = self.get_obj_representation_context(obj, context["name"], subcontext["name"], target_view)
|
representation = self.get_shape_representation(obj, context["name"], subcontext["name"], target_view)
|
||||||
if rep_context:
|
if representation:
|
||||||
self.append_representation_in_context(obj, rep_context, name)
|
self.append_representation_in_context(obj, representation, name)
|
||||||
|
|
||||||
def get_obj_representation_context(self, obj, context, subcontext, target_view):
|
def get_shape_representation(self, obj, context, subcontext, target_view):
|
||||||
for c in obj.BIMObjectProperties.representation_contexts:
|
for representation in obj.BIMObjectProperties.representations:
|
||||||
if c.context == context and c.name == subcontext and c.target_view == target_view:
|
c = self.stored_file.by_id(representation.ifc_definition_id)
|
||||||
return c
|
if c.ContextType == context and c.ContextIdentifier == subcontext and c.TargetView == target_view:
|
||||||
if obj.BIMObjectProperties.representation_contexts:
|
return representation
|
||||||
|
if obj.BIMObjectProperties.representations:
|
||||||
return
|
return
|
||||||
if context == "Model" and subcontext == "Body" and target_view == "MODEL_VIEW":
|
# TODO: reimplement - see bug #1222
|
||||||
representation_context = obj.BIMObjectProperties.representation_contexts.add()
|
#if context == "Model" and subcontext == "Body" and target_view == "MODEL_VIEW":
|
||||||
representation_context.context = "Model"
|
# representation_context = obj.BIMObjectProperties.representation_contexts.add()
|
||||||
representation_context.name = "Body"
|
# representation_context.context = "Model"
|
||||||
representation_context.target_view = "MODEL_VIEW"
|
# representation_context.name = "Body"
|
||||||
return representation_context
|
# representation_context.target_view = "MODEL_VIEW"
|
||||||
|
# return representation_context
|
||||||
|
|
||||||
def append_representation_in_context(self, obj, rep_context, name):
|
def append_representation_in_context(self, obj, shape_representation, name):
|
||||||
context = rep_context.context
|
context_of_items = self.stored_file.by_id(shape_representation.ifc_definition_id).ContextOfItems
|
||||||
subcontext = rep_context.name
|
context = context_of_items.ContextType
|
||||||
target_view = rep_context.target_view
|
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[
|
self.representations[
|
||||||
"{}/{}/{}/{}".format(context, subcontext, target_view, name)
|
"{}/{}/{}/{}".format(context, subcontext, target_view, name)
|
||||||
] = self.get_representation(obj.data, obj, context, subcontext, target_view)
|
] = self.get_representation(obj.data, obj, context, subcontext, target_view)
|
||||||
@@ -1088,7 +1075,6 @@ class IfcParser:
|
|||||||
context_prefix = "/".join([context, subcontext, target_view])
|
context_prefix = "/".join([context, subcontext, target_view])
|
||||||
mesh_name = "/".join([context_prefix, name])
|
mesh_name = "/".join([context_prefix, name])
|
||||||
mesh = self.search_for_mesh_or_curve_data(mesh_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:
|
if mesh:
|
||||||
self.representations[mesh_name] = self.get_representation(mesh, obj, context, subcontext, target_view)
|
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":
|
if "Model/Box/MODEL_VIEW" in self.generated_subcontexts and context_prefix == "Model/Body/MODEL_VIEW":
|
||||||
@@ -1113,7 +1099,7 @@ class IfcParser:
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def get_representation(self, mesh, obj, context, subcontext, target_view):
|
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 {
|
return {
|
||||||
"ifc": None,
|
"ifc": None,
|
||||||
"raw": mesh,
|
"raw": mesh,
|
||||||
@@ -1121,9 +1107,9 @@ class IfcParser:
|
|||||||
"context": context,
|
"context": context,
|
||||||
"subcontext": subcontext,
|
"subcontext": subcontext,
|
||||||
"target_view": target_view,
|
"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": 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")
|
if hasattr(mesh, "BIMMeshProperties")
|
||||||
else None,
|
else None,
|
||||||
"is_parametric": mesh.BIMMeshProperties.is_parametric if hasattr(mesh, "BIMMeshProperties") else False,
|
"is_parametric": mesh.BIMMeshProperties.is_parametric if hasattr(mesh, "BIMMeshProperties") else False,
|
||||||
@@ -1345,8 +1331,9 @@ class IfcExporter:
|
|||||||
self.roundtrip_id_new_to_old = {}
|
self.roundtrip_id_new_to_old = {}
|
||||||
|
|
||||||
def export(self, selected_objects):
|
def export(self, selected_objects):
|
||||||
self.file = ifc.IfcStore.get_file()
|
self.stored_file = ifc.IfcStore.get_file() # See bug #1222
|
||||||
if self.file and self.ifc_export_settings.should_export_from_memory:
|
if self.stored_file and self.ifc_export_settings.should_export_from_memory:
|
||||||
|
self.file = self.stored_file
|
||||||
return self.write_ifc_file()
|
return self.write_ifc_file()
|
||||||
self.schema_version = self.ifc_export_settings.schema
|
self.schema_version = self.ifc_export_settings.schema
|
||||||
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.schema_version)
|
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.schema_version)
|
||||||
@@ -1383,7 +1370,6 @@ class IfcExporter:
|
|||||||
self.relate_objects_to_objects()
|
self.relate_objects_to_objects()
|
||||||
self.relate_elements_to_spatial_structures()
|
self.relate_elements_to_spatial_structures()
|
||||||
self.relate_nested_elements_to_hosted_elements()
|
self.relate_nested_elements_to_hosted_elements()
|
||||||
self.relate_objects_to_types()
|
|
||||||
self.relate_objects_to_qtos()
|
self.relate_objects_to_qtos()
|
||||||
self.relate_objects_to_psets()
|
self.relate_objects_to_psets()
|
||||||
self.relate_objects_to_opening_elements()
|
self.relate_objects_to_opening_elements()
|
||||||
@@ -1423,15 +1409,16 @@ class IfcExporter:
|
|||||||
self.file.wrapped_data.header.file_name.originating_system = "{} {}".format(
|
self.file.wrapped_data.header.file_name.originating_system = "{} {}".format(
|
||||||
self.get_application_name(), self.get_application_version()
|
self.get_application_name(), self.get_application_version()
|
||||||
)
|
)
|
||||||
if self.owner_history:
|
# TODO: reimplement. See #1222.
|
||||||
if self.schema_version == "IFC2X3":
|
#if self.owner_history:
|
||||||
self.file.wrapped_data.header.file_name.authorization = self.owner_history.OwningUser.ThePerson.Id
|
# if self.schema_version == "IFC2X3":
|
||||||
else:
|
# self.file.wrapped_data.header.file_name.authorization = self.owner_history.OwningUser.ThePerson.Id
|
||||||
self.file.wrapped_data.header.file_name.authorization = (
|
# else:
|
||||||
self.owner_history.OwningUser.ThePerson.Identification
|
# self.file.wrapped_data.header.file_name.authorization = (
|
||||||
)
|
# self.owner_history.OwningUser.ThePerson.Identification
|
||||||
else:
|
# )
|
||||||
self.file.wrapped_data.header.file_name.authorization = "Nobody"
|
#else:
|
||||||
|
# self.file.wrapped_data.header.file_name.authorization = "Nobody"
|
||||||
|
|
||||||
def get_application_name(self):
|
def get_application_name(self):
|
||||||
return "BlenderBIM"
|
return "BlenderBIM"
|
||||||
@@ -1699,13 +1686,15 @@ class IfcExporter:
|
|||||||
pset["ifc"] = self.file.create_entity("IfcMaterialProperties", **pset["attributes"])
|
pset["ifc"] = self.file.create_entity("IfcMaterialProperties", **pset["attributes"])
|
||||||
|
|
||||||
def create_qto_properties(self, qto):
|
def create_qto_properties(self, qto):
|
||||||
if qto["attributes"]["Name"] in ifcopenshell.util.pset.qtos:
|
qto_template = schema.ifc.psetqto.get_by_name(qto["attributes"]["Name"])
|
||||||
return self.create_templated_qto_properties(qto)
|
if qto_template:
|
||||||
|
return self.create_templated_qto_properties(qto, qto_template)
|
||||||
return self.create_custom_qto_properties(qto)
|
return self.create_custom_qto_properties(qto)
|
||||||
|
|
||||||
def create_pset_properties(self, pset):
|
def create_pset_properties(self, pset):
|
||||||
if pset["attributes"]["Name"] in ifcopenshell.util.pset.psets:
|
pset_template = schema.ifc.psetqto.get_by_name(pset["attributes"]["Name"])
|
||||||
return self.create_templated_pset_properties(pset)
|
if pset_template:
|
||||||
|
return self.create_templated_pset_properties(pset, pset_template)
|
||||||
return self.create_custom_pset_properties(pset)
|
return self.create_custom_pset_properties(pset)
|
||||||
|
|
||||||
def create_custom_pset_properties(self, pset):
|
def create_custom_pset_properties(self, pset):
|
||||||
@@ -1735,15 +1724,15 @@ class IfcExporter:
|
|||||||
)
|
)
|
||||||
return properties
|
return properties
|
||||||
|
|
||||||
def create_templated_pset_properties(self, pset):
|
def create_templated_pset_properties(self, pset, pset_template):
|
||||||
properties = []
|
properties = []
|
||||||
templates = ifcopenshell.util.pset.psets[pset["attributes"]["Name"]]["HasPropertyTemplates"]
|
for prop in pset_template.HasPropertyTemplates:
|
||||||
for name, data in templates.items():
|
name = prop.Name
|
||||||
if name not in pset["raw"]:
|
if name not in pset["raw"]:
|
||||||
continue
|
continue
|
||||||
if data.TemplateType == "P_SINGLEVALUE" or data.TemplateType == "P_ENUMERATEDVALUE":
|
if prop.TemplateType == "P_SINGLEVALUE" or prop.TemplateType == "P_ENUMERATEDVALUE":
|
||||||
if data.PrimaryMeasureType:
|
if prop.PrimaryMeasureType:
|
||||||
value_type = data.PrimaryMeasureType
|
value_type = prop.PrimaryMeasureType
|
||||||
else:
|
else:
|
||||||
# The IFC spec is missing some, so we provide a fallback
|
# The IFC spec is missing some, so we provide a fallback
|
||||||
value_type = "IfcLabel"
|
value_type = "IfcLabel"
|
||||||
@@ -1753,7 +1742,8 @@ class IfcExporter:
|
|||||||
properties.append(
|
properties.append(
|
||||||
self.file.create_entity("IfcPropertySingleValue", **{"Name": name, "NominalValue": nominal_value})
|
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:
|
if invalid_pset_keys:
|
||||||
self.ifc_export_settings.logger.error(
|
self.ifc_export_settings.logger.error(
|
||||||
"One or more properties were invalid in the pset {}: {}".format(
|
"One or more properties were invalid in the pset {}: {}".format(
|
||||||
@@ -1762,20 +1752,21 @@ class IfcExporter:
|
|||||||
)
|
)
|
||||||
return properties
|
return properties
|
||||||
|
|
||||||
def create_templated_qto_properties(self, qto):
|
def create_templated_qto_properties(self, qto, qto_template):
|
||||||
properties = []
|
properties = []
|
||||||
templates = ifcopenshell.util.pset.qtos[qto["attributes"]["Name"]]["HasPropertyTemplates"]
|
for prop in qto_template.HasPropertyTemplates:
|
||||||
for name, data in templates.items():
|
name = prop.Name
|
||||||
if name not in qto["raw"]:
|
if name not in qto["raw"]:
|
||||||
continue
|
continue
|
||||||
if data.TemplateType[0:2] == "Q_":
|
if prop.TemplateType[0:2] == "Q_":
|
||||||
value_basename = data.TemplateType[2:].title()
|
value_basename = prop.TemplateType[2:].title()
|
||||||
value_name = f"{value_basename}Value"
|
value_name = f"{value_basename}Value"
|
||||||
class_name = f"IfcQuantity{value_basename}"
|
class_name = f"IfcQuantity{value_basename}"
|
||||||
properties.append(
|
properties.append(
|
||||||
self.file.create_entity(class_name, **{"Name": name, value_name: float(qto["raw"][name])})
|
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:
|
if invalid_qto_keys:
|
||||||
self.ifc_export_settings.logger.error(
|
self.ifc_export_settings.logger.error(
|
||||||
"One or more properties were invalid in the qto {}/{}: {}".format(
|
"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.
|
# At the moment, we assume that styled items only apply to the body context.
|
||||||
if representation.RepresentationIdentifier != "Body":
|
if representation.RepresentationIdentifier != "Body":
|
||||||
continue
|
continue
|
||||||
rep_context = self.ifc_parser.get_obj_representation_context(product["raw"], "Model", "Body", "MODEL_VIEW")
|
rep = self.ifc_parser.get_shape_representation(product["raw"], "Model", "Body", "MODEL_VIEW")
|
||||||
if self.ifc_export_settings.should_roundtrip_native and rep_context and rep_context.ifc_definition_id:
|
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 native roundtripping, each slot could be a one to many relationship to items
|
||||||
for item in self.get_geometric_representation_items(representation):
|
for item in self.get_geometric_representation_items(representation):
|
||||||
original_id = self.roundtrip_id_new_to_old[item.id()]
|
original_id = self.roundtrip_id_new_to_old[item.id()]
|
||||||
@@ -3181,17 +3172,6 @@ class IfcExporter:
|
|||||||
[o["ifc"] for o in related_objects],
|
[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):
|
def relate_objects_to_qtos(self):
|
||||||
for relating_property_key, related_objects in self.ifc_parser.rel_defines_by_qto.items():
|
for relating_property_key, related_objects in self.ifc_parser.rel_defines_by_qto.items():
|
||||||
self.file.createIfcRelDefinesByProperties(
|
self.file.createIfcRelDefinesByProperties(
|
||||||
@@ -3468,6 +3448,7 @@ class IfcExporter:
|
|||||||
return co * self.ifc_parser.unit_scale
|
return co * self.ifc_parser.unit_scale
|
||||||
|
|
||||||
def write_ifc_file(self):
|
def write_ifc_file(self):
|
||||||
|
self.set_header()
|
||||||
extension = self.ifc_export_settings.output_file.split(".")[-1]
|
extension = self.ifc_export_settings.output_file.split(".")[-1]
|
||||||
if extension == "ifczip":
|
if extension == "ifczip":
|
||||||
with tempfile.TemporaryDirectory() as unzipped_path:
|
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_roundtrip_native = scene_bim.import_export_should_roundtrip_native
|
||||||
settings.should_export_from_memory = scene_bim.export_should_export_from_memory
|
settings.should_export_from_memory = scene_bim.export_should_export_from_memory
|
||||||
settings.context_tree = []
|
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
|
return settings
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import math
|
import math
|
||||||
|
from mathutils import geometry
|
||||||
|
from mathutils import Vector
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
|
||||||
@@ -285,3 +287,84 @@ def format_distance(value, isArea=False, hide_units=True):
|
|||||||
tx_dist = fmt % value
|
tx_dist = fmt % value
|
||||||
|
|
||||||
return tx_dist
|
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:
|
class IfcStore:
|
||||||
path = ""
|
path = ""
|
||||||
file = None
|
file = None
|
||||||
|
schema = None
|
||||||
pset_template_path = ""
|
pset_template_path = ""
|
||||||
pset_template_file = None
|
pset_template_file = None
|
||||||
|
|
||||||
@@ -15,3 +16,11 @@ class IfcStore:
|
|||||||
if IfcStore.path:
|
if IfcStore.path:
|
||||||
IfcStore.file = ifcopenshell.open(IfcStore.path)
|
IfcStore.file = ifcopenshell.open(IfcStore.path)
|
||||||
return IfcStore.file
|
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.geolocation
|
||||||
import ifcopenshell.util.selector
|
import ifcopenshell.util.selector
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.pset
|
import ifcopenshell.util.unit
|
||||||
import bpy
|
import bpy
|
||||||
import bmesh
|
import bmesh
|
||||||
import os
|
import os
|
||||||
@@ -20,8 +20,8 @@ import tempfile
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from . import helper
|
|
||||||
from . import ifc
|
from . import ifc
|
||||||
|
from . import schema
|
||||||
|
|
||||||
|
|
||||||
class FileCopy(threading.Thread):
|
class FileCopy(threading.Thread):
|
||||||
@@ -256,6 +256,7 @@ class MaterialCreator:
|
|||||||
|
|
||||||
def create_new_single(self, material):
|
def create_new_single(self, material):
|
||||||
self.materials[material.Name] = obj = bpy.data.materials.new(material.Name)
|
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)
|
self.ifc_importer.add_element_attributes(material, obj.BIMMaterialProperties)
|
||||||
for pset in getattr(material, "HasProperties", ()):
|
for pset in getattr(material, "HasProperties", ()):
|
||||||
self.ifc_importer.add_pset(pset, obj.BIMMaterialProperties)
|
self.ifc_importer.add_pset(pset, obj.BIMMaterialProperties)
|
||||||
@@ -286,6 +287,7 @@ class MaterialCreator:
|
|||||||
for style in styles:
|
for style in styles:
|
||||||
if not style.is_a("IfcSurfaceStyle"):
|
if not style.is_a("IfcSurfaceStyle"):
|
||||||
continue
|
continue
|
||||||
|
material.BIMMaterialProperties.ifc_style_id = int(style.id())
|
||||||
external_style = None
|
external_style = None
|
||||||
for surface_style in style.Styles:
|
for surface_style in style.Styles:
|
||||||
if surface_style.is_a("IfcSurfaceStyleShading"):
|
if surface_style.is_a("IfcSurfaceStyleShading"):
|
||||||
@@ -373,7 +375,6 @@ class IfcImporter:
|
|||||||
self.native_data = {}
|
self.native_data = {}
|
||||||
self.groups = {}
|
self.groups = {}
|
||||||
self.aggregates = {}
|
self.aggregates = {}
|
||||||
self.aggregate_collection = None
|
|
||||||
self.aggregate_collections = {}
|
self.aggregate_collections = {}
|
||||||
|
|
||||||
self.material_creator = MaterialCreator(ifc_import_settings, self)
|
self.material_creator = MaterialCreator(ifc_import_settings, self)
|
||||||
@@ -409,8 +410,6 @@ class IfcImporter:
|
|||||||
self.profile_code("Patching ifc")
|
self.profile_code("Patching ifc")
|
||||||
self.set_units()
|
self.set_units()
|
||||||
self.profile_code("Set units")
|
self.profile_code("Set units")
|
||||||
self.create_geometric_representation_contexts()
|
|
||||||
self.profile_code("Create contexts")
|
|
||||||
self.create_project()
|
self.create_project()
|
||||||
self.profile_code("Create project")
|
self.profile_code("Create project")
|
||||||
self.create_classifications()
|
self.create_classifications()
|
||||||
@@ -773,6 +772,7 @@ class IfcImporter:
|
|||||||
obj = self.existing_elements[element.GlobalId]
|
obj = self.existing_elements[element.GlobalId]
|
||||||
else:
|
else:
|
||||||
obj = bpy.data.objects.new(f"{element.is_a()}/{element.Name}", None)
|
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)
|
self.add_element_attributes(element, obj.BIMObjectProperties)
|
||||||
group_collection.objects.link(obj)
|
group_collection.objects.link(obj)
|
||||||
self.groups[element.GlobalId] = {"ifc": element, "blender": 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)
|
shape = ifcopenshell.geom.create_shape(self.settings_2d, axis.AxisCurve)
|
||||||
mesh = self.create_mesh(axis, shape)
|
mesh = self.create_mesh(axis, shape)
|
||||||
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
|
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
|
||||||
|
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||||
obj.matrix_world = matrix_world
|
obj.matrix_world = matrix_world
|
||||||
self.add_element_attributes(axis, obj.BIMObjectProperties)
|
self.add_element_attributes(axis, obj.BIMObjectProperties)
|
||||||
grid.objects.link(obj)
|
grid.objects.link(obj)
|
||||||
@@ -843,12 +844,10 @@ class IfcImporter:
|
|||||||
except:
|
except:
|
||||||
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
|
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
|
||||||
obj = bpy.data.objects.new(self.get_name(element), mesh)
|
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.material_creator.create(element, obj, mesh)
|
||||||
self.add_element_attributes(element, obj.BIMObjectProperties)
|
|
||||||
self.add_element_classifications(element, obj)
|
self.add_element_classifications(element, obj)
|
||||||
self.add_element_document_relations(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_collection.objects.link(obj)
|
||||||
self.type_products[element.GlobalId] = obj
|
self.type_products[element.GlobalId] = obj
|
||||||
|
|
||||||
@@ -950,11 +949,16 @@ class IfcImporter:
|
|||||||
mesh = self.create_native_mesh(element, shape)
|
mesh = self.create_native_mesh(element, shape)
|
||||||
if mesh is None:
|
if mesh is None:
|
||||||
mesh = self.create_mesh(element, shape)
|
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
|
self.meshes[mesh_name] = mesh
|
||||||
else:
|
else:
|
||||||
mesh = None
|
mesh = None
|
||||||
|
|
||||||
obj = bpy.data.objects.new(self.get_name(element), mesh)
|
obj = bpy.data.objects.new(self.get_name(element), mesh)
|
||||||
|
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||||
|
|
||||||
if shape:
|
if shape:
|
||||||
m = shape.transformation.matrix.data
|
m = shape.transformation.matrix.data
|
||||||
@@ -968,13 +972,10 @@ class IfcImporter:
|
|||||||
obj.matrix_world = self.get_element_matrix(element)
|
obj.matrix_world = self.get_element_matrix(element)
|
||||||
|
|
||||||
self.add_element_representation_items(element, obj)
|
self.add_element_representation_items(element, obj)
|
||||||
self.add_element_attributes(element, obj.BIMObjectProperties)
|
|
||||||
self.add_element_classifications(element, obj)
|
self.add_element_classifications(element, obj)
|
||||||
self.add_element_document_relations(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_opening_relation(element, obj)
|
||||||
self.add_product_definitions(element, obj)
|
self.add_product_definitions(element, obj)
|
||||||
self.add_product_representations(element, obj)
|
|
||||||
self.added_data[element.GlobalId] = obj
|
self.added_data[element.GlobalId] = obj
|
||||||
|
|
||||||
if element.is_a("IfcOpeningElement"):
|
if element.is_a("IfcOpeningElement"):
|
||||||
@@ -1309,7 +1310,6 @@ class IfcImporter:
|
|||||||
# Occurs when reloading a project
|
# Occurs when reloading a project
|
||||||
pass
|
pass
|
||||||
project_collection = bpy.context.view_layer.layer_collection.children[self.project["blender"].name]
|
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.opening_collection.name].hide_viewport = True
|
||||||
project_collection.children[self.type_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.mesh.normals_make_consistent(context_override)
|
||||||
bpy.ops.object.editmode_toggle(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):
|
def add_product_definitions(self, element, obj):
|
||||||
if not hasattr(element, "IsDefinedBy") or not element.IsDefinedBy:
|
if not hasattr(element, "IsDefinedBy") or not element.IsDefinedBy:
|
||||||
return
|
return
|
||||||
for definition in element.IsDefinedBy:
|
for definition in element.IsDefinedBy:
|
||||||
if not definition.is_a("IfcRelDefinesByProperties"):
|
if not definition.is_a("IfcRelDefinesByProperties"):
|
||||||
continue
|
continue
|
||||||
if definition.RelatingPropertyDefinition.is_a("IfcPropertySet"):
|
if definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"):
|
||||||
self.add_pset(definition.RelatingPropertyDefinition, obj.BIMObjectProperties)
|
|
||||||
elif definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"):
|
|
||||||
self.add_qto(definition.RelatingPropertyDefinition, obj)
|
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):
|
def add_pset(self, pset, props):
|
||||||
new_pset = props.psets.add()
|
new_pset = props.psets.add()
|
||||||
new_pset.name = pset.Name
|
new_pset.name = pset.Name
|
||||||
if new_pset.name in ifcopenshell.util.pset.psets:
|
pset_template = schema.ifc.psetqto.get_by_name(new_pset.name)
|
||||||
for prop_name in ifcopenshell.util.pset.psets[new_pset.name]["HasPropertyTemplates"].keys():
|
if pset_template:
|
||||||
|
for prop_name in (p.Name for p in pset_template.HasPropertyTemplates):
|
||||||
prop = new_pset.properties.add()
|
prop = new_pset.properties.add()
|
||||||
prop.name = prop_name
|
prop.name = prop_name
|
||||||
try:
|
try:
|
||||||
@@ -1421,8 +1395,9 @@ class IfcImporter:
|
|||||||
def add_qto(self, qto, obj):
|
def add_qto(self, qto, obj):
|
||||||
new_qto = obj.BIMObjectProperties.qtos.add()
|
new_qto = obj.BIMObjectProperties.qtos.add()
|
||||||
new_qto.name = str(qto.Name)
|
new_qto.name = str(qto.Name)
|
||||||
if new_qto.name in ifcopenshell.util.pset.qtos:
|
qto_template = schema.ifc.psetqto.get_by_name(new_qto.name)
|
||||||
for prop_name in ifcopenshell.util.pset.qtos[new_qto.name]["HasPropertyTemplates"].keys():
|
if qto_template:
|
||||||
|
for prop_name in (p.Name for p in qto_template.HasPropertyTemplates):
|
||||||
prop = new_qto.properties.add()
|
prop = new_qto.properties.add()
|
||||||
prop.name = prop_name
|
prop.name = prop_name
|
||||||
for prop in qto.Quantities:
|
for prop in qto.Quantities:
|
||||||
@@ -1438,11 +1413,6 @@ class IfcImporter:
|
|||||||
new_prop.name = prop.Name
|
new_prop.name = prop.Name
|
||||||
new_prop.string_value = str(value)
|
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):
|
def add_opening_relation(self, element, obj):
|
||||||
if not element.is_a("IfcOpeningElement"):
|
if not element.is_a("IfcOpeningElement"):
|
||||||
return
|
return
|
||||||
@@ -1499,7 +1469,7 @@ class IfcImporter:
|
|||||||
self.unit_scale *= unit.ConversionFactor.ValueComponent.wrappedValue
|
self.unit_scale *= unit.ConversionFactor.ValueComponent.wrappedValue
|
||||||
unit = unit.ConversionFactor.UnitComponent
|
unit = unit.ConversionFactor.UnitComponent
|
||||||
if unit.is_a("IfcSIUnit"):
|
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):
|
def set_units(self):
|
||||||
units = self.file.by_type("IfcUnitAssignment")[0]
|
units = self.file.by_type("IfcUnitAssignment")[0]
|
||||||
@@ -1530,28 +1500,6 @@ class IfcImporter:
|
|||||||
unit.Prefix + "/" if hasattr(unit, "Prefix") and unit.Prefix else "", name
|
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):
|
def create_project(self):
|
||||||
self.project = {"ifc": self.file.by_type("IfcProject")[0]}
|
self.project = {"ifc": self.file.by_type("IfcProject")[0]}
|
||||||
if self.project["ifc"].GlobalId in self.existing_elements:
|
if self.project["ifc"].GlobalId in self.existing_elements:
|
||||||
@@ -1743,30 +1691,23 @@ class IfcImporter:
|
|||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
rel_aggregates = [a for a in self.file.by_type("IfcRelAggregates") if a.RelatingObject.is_a("IfcElement")]
|
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:
|
for rel_aggregate in rel_aggregates:
|
||||||
self.create_aggregate(rel_aggregate)
|
self.create_aggregate(rel_aggregate)
|
||||||
|
|
||||||
def create_aggregate(self, 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
|
element = rel_aggregate.RelatingObject
|
||||||
|
|
||||||
obj = bpy.data.objects.new("{}/{}".format(element.is_a(), element.Name), None)
|
obj = bpy.data.objects.new("{}/{}".format(element.is_a(), element.Name), None)
|
||||||
obj.instance_type = "COLLECTION"
|
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||||
obj.instance_collection = collection
|
|
||||||
self.place_object_in_spatial_tree(element, obj)
|
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_classifications(element, obj)
|
||||||
self.add_element_document_relations(element, obj)
|
self.add_element_document_relations(element, obj)
|
||||||
self.add_defines_by_type_relation(element, obj)
|
|
||||||
self.add_product_definitions(element, obj)
|
self.add_product_definitions(element, obj)
|
||||||
self.aggregates[element.GlobalId] = obj
|
self.aggregates[element.GlobalId] = obj
|
||||||
self.aggregate_collections[rel_aggregate.id()] = collection
|
self.aggregate_collections[rel_aggregate.id()] = collection
|
||||||
@@ -1823,12 +1764,11 @@ class IfcImporter:
|
|||||||
return
|
return
|
||||||
|
|
||||||
obj = bpy.data.objects.new(self.get_name(element), mesh)
|
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.material_creator.create(element, obj, mesh)
|
||||||
obj.matrix_world = self.get_element_matrix(element, mesh_name)
|
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_classifications(element, obj)
|
||||||
self.add_element_document_relations(element, obj)
|
self.add_element_document_relations(element, obj)
|
||||||
self.add_defines_by_type_relation(element, obj)
|
|
||||||
self.add_product_definitions(element, obj)
|
self.add_product_definitions(element, obj)
|
||||||
self.added_data[element.GlobalId] = obj
|
self.added_data[element.GlobalId] = obj
|
||||||
|
|
||||||
@@ -1869,8 +1809,6 @@ class IfcImporter:
|
|||||||
):
|
):
|
||||||
container = element.ContainedInStructure[0].RelatingStructure
|
container = element.ContainedInStructure[0].RelatingStructure
|
||||||
if container.is_a("IfcSpace"):
|
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)
|
return self.place_object_in_spatial_tree(container, obj)
|
||||||
elif element.is_a("IfcGrid"):
|
elif element.is_a("IfcGrid"):
|
||||||
grid_collection = bpy.data.collections.get(obj.name)
|
grid_collection = bpy.data.collections.get(obj.name)
|
||||||
@@ -2052,23 +1990,6 @@ class IfcImporter:
|
|||||||
):
|
):
|
||||||
return representation.Items[0].MappingTarget
|
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):
|
def create_mesh(self, element, shape, is_curve=False):
|
||||||
try:
|
try:
|
||||||
if hasattr(shape, "geometry"):
|
if hasattr(shape, "geometry"):
|
||||||
@@ -2079,7 +2000,13 @@ class IfcImporter:
|
|||||||
if is_curve:
|
if is_curve:
|
||||||
return self.create_curve(geometry)
|
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:
|
if geometry.faces:
|
||||||
num_vertices = len(geometry.verts) // 3
|
num_vertices = len(geometry.verts) // 3
|
||||||
@@ -2120,7 +2047,6 @@ class IfcImporter:
|
|||||||
ios_materials.append(mat.name)
|
ios_materials.append(mat.name)
|
||||||
mesh["ios_materials"] = ios_materials
|
mesh["ios_materials"] = ios_materials
|
||||||
mesh["ios_material_ids"] = geometry.material_ids
|
mesh["ios_material_ids"] = geometry.material_ids
|
||||||
mesh.BIMMeshProperties.geometry_type = str(self.get_geometry_type(element))
|
|
||||||
return mesh
|
return mesh
|
||||||
except:
|
except:
|
||||||
self.ifc_import_settings.logger.error("Could not create mesh for %s", element)
|
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 json
|
||||||
import os
|
import os
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.pset
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from . import export_ifc
|
from . import export_ifc
|
||||||
from . import schema
|
from . import schema
|
||||||
@@ -9,6 +8,7 @@ from . import ifc
|
|||||||
from . import annotation
|
from . import annotation
|
||||||
from . import decoration
|
from . import decoration
|
||||||
import bpy
|
import bpy
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.app.handlers import persistent
|
from bpy.app.handlers import persistent
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
@@ -40,35 +40,19 @@ psettemplatefiles_enum = []
|
|||||||
propertysettemplates_enum = []
|
propertysettemplates_enum = []
|
||||||
classification_enum = []
|
classification_enum = []
|
||||||
attributes_enum = []
|
attributes_enum = []
|
||||||
psetnames_enum = []
|
psetnames = {}
|
||||||
qtonames_enum = []
|
qtonames_enum = []
|
||||||
materialattributes_enum = []
|
materialattributes_enum = []
|
||||||
materialtypes_enum = []
|
materialtypes_enum = []
|
||||||
contexts_enum = []
|
contexts_enum = []
|
||||||
subcontexts_enum = []
|
subcontexts_enum = []
|
||||||
target_views_enum = []
|
target_views_enum = []
|
||||||
persons_enum = []
|
|
||||||
organisations_enum = []
|
|
||||||
sheets_enum = []
|
sheets_enum = []
|
||||||
vector_styles_enum = []
|
vector_styles_enum = []
|
||||||
|
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
def setDefaultProperties(scene):
|
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:
|
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
|
||||||
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
|
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
|
||||||
drawing_style.name = "Technical"
|
drawing_style.name = "Technical"
|
||||||
@@ -140,14 +124,13 @@ def setDefaultProperties(scene):
|
|||||||
|
|
||||||
def getIfcPredefinedTypes(self, context):
|
def getIfcPredefinedTypes(self, context):
|
||||||
global types_enum
|
global types_enum
|
||||||
if len(types_enum) < 1:
|
file = IfcStore.get_file()
|
||||||
for name, data in schema.ifc.elements.items():
|
if len(types_enum) < 1 and file:
|
||||||
if name != self.ifc_class.strip():
|
declaration = IfcStore.get_schema().declaration_by_name(self.ifc_class)
|
||||||
continue
|
for attribute in declaration.attributes():
|
||||||
for attribute in data["attributes"]:
|
if attribute.name() == "PredefinedType":
|
||||||
if attribute["name"] != "PredefinedType":
|
types_enum.extend([(e, e, "") for e in attribute.type_of_attribute().declared_type().enumeration_items()])
|
||||||
continue
|
break
|
||||||
types_enum.extend([(e, e, "") for e in attribute["enum_values"]])
|
|
||||||
return types_enum
|
return types_enum
|
||||||
|
|
||||||
|
|
||||||
@@ -263,6 +246,7 @@ def refreshActiveDrawingIndex(self, context):
|
|||||||
|
|
||||||
def getIfcProducts(self, context):
|
def getIfcProducts(self, context):
|
||||||
global products_enum
|
global products_enum
|
||||||
|
file = IfcStore.get_file()
|
||||||
if len(products_enum) < 1:
|
if len(products_enum) < 1:
|
||||||
products_enum.extend(
|
products_enum.extend(
|
||||||
[
|
[
|
||||||
@@ -272,23 +256,38 @@ def getIfcProducts(self, context):
|
|||||||
"IfcElementType",
|
"IfcElementType",
|
||||||
"IfcSpatialElement",
|
"IfcSpatialElement",
|
||||||
"IfcGroup",
|
"IfcGroup",
|
||||||
"IfcStructural",
|
"IfcStructuralItem",
|
||||||
"IfcPositioningElement",
|
|
||||||
"IfcContext",
|
"IfcContext",
|
||||||
"IfcAnnotation",
|
"IfcAnnotation",
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
if file.schema == "IFC2X3":
|
||||||
|
products_enum[2] = ("IfcSpatialStructureElement", "IfcSpatialStructureElement", "")
|
||||||
return products_enum
|
return products_enum
|
||||||
|
|
||||||
|
|
||||||
def getIfcClasses(self, context):
|
def getIfcClasses(self, context):
|
||||||
global classes_enum
|
global classes_enum
|
||||||
if len(classes_enum) < 1:
|
file = IfcStore.get_file()
|
||||||
classes_enum.extend([(e, e, "") for e in getattr(schema.ifc, self.ifc_product)])
|
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
|
return classes_enum
|
||||||
|
|
||||||
|
|
||||||
|
def getAttributeEnumValues(self, context):
|
||||||
|
return [(e, e, "") for e in json.loads(self.enum_items)]
|
||||||
|
|
||||||
|
|
||||||
def getProfileDef(self, context):
|
def getProfileDef(self, context):
|
||||||
global profiledef_enum
|
global profiledef_enum
|
||||||
if len(profiledef_enum) < 1:
|
if len(profiledef_enum) < 1:
|
||||||
@@ -297,17 +296,27 @@ def getProfileDef(self, context):
|
|||||||
|
|
||||||
|
|
||||||
def getPersons(self, context):
|
def getPersons(self, context):
|
||||||
global persons_enum
|
from blenderbim.bim.module.owner.data import Data
|
||||||
persons_enum.clear()
|
if not Data.is_loaded:
|
||||||
persons_enum.extend([(p.name, p.name, "") for p in bpy.context.scene.BIMProperties.people])
|
Data.load()
|
||||||
return persons_enum
|
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):
|
def getOrganisations(self, context):
|
||||||
global organisations_enum
|
from blenderbim.bim.module.owner.data import Data
|
||||||
organisations_enum.clear()
|
if not Data.is_loaded:
|
||||||
organisations_enum.extend([(o.name, o.name, "") for o in bpy.context.scene.BIMProperties.organisations])
|
Data.load()
|
||||||
return organisations_enum
|
results = []
|
||||||
|
for ifc_id, organisation in Data.organisations.items():
|
||||||
|
results.append((str(ifc_id), organisation["Name"], ""))
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
def getIfcPatchRecipes(self, context):
|
def getIfcPatchRecipes(self, context):
|
||||||
@@ -359,11 +368,18 @@ def refreshTitleblocks(self, context):
|
|||||||
def toggleDecorations(self, context):
|
def toggleDecorations(self, context):
|
||||||
toggle = self.should_draw_decorations
|
toggle = self.should_draw_decorations
|
||||||
if toggle:
|
if toggle:
|
||||||
for dec in decoration.all_decorators:
|
decoration.DecorationsHandler.install(context)
|
||||||
dec.install(self, context)
|
|
||||||
else:
|
else:
|
||||||
for dec in decoration.all_decorators:
|
decoration.DecorationsHandler.uninstall()
|
||||||
dec.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):
|
def getScenarios(self, context):
|
||||||
@@ -430,22 +446,20 @@ def refreshReferences(self, context):
|
|||||||
|
|
||||||
|
|
||||||
def getPsetNames(self, context):
|
def getPsetNames(self, context):
|
||||||
global psetnames_enum
|
global psetnames
|
||||||
psetnames_enum.clear()
|
if "/" in context.active_object.name:
|
||||||
if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements:
|
ifc_class = context.active_object.name.split("/")[0]
|
||||||
pset_names = ifcopenshell.util.pset.get_applicable_psetqtos(
|
if ifc_class not in psetnames:
|
||||||
bpy.context.scene.BIMProperties.export_schema, context.active_object.name.split("/")[0], is_pset=True
|
psets = schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
||||||
)
|
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
||||||
psetnames_enum.extend([(p, p, "") for p in pset_names])
|
return psetnames[ifc_class]
|
||||||
return psetnames_enum
|
return []
|
||||||
|
|
||||||
|
|
||||||
def getMaterialPsetNames(self, context):
|
def getMaterialPsetNames(self, context):
|
||||||
global materialpsetnames_enum
|
global materialpsetnames_enum
|
||||||
materialpsetnames_enum.clear()
|
materialpsetnames_enum.clear()
|
||||||
pset_names = ifcopenshell.util.pset.get_applicable_psetqtos(
|
pset_names = schema.ifc.psetqto.get_applicable_names("IfcMaterial", pset_only=True)
|
||||||
bpy.context.scene.BIMProperties.export_schema, "IfcMaterial", is_pset=True
|
|
||||||
)
|
|
||||||
materialpsetnames_enum.extend([(p, p, "") for p in pset_names])
|
materialpsetnames_enum.extend([(p, p, "") for p in pset_names])
|
||||||
return materialpsetnames_enum
|
return materialpsetnames_enum
|
||||||
|
|
||||||
@@ -454,27 +468,11 @@ def getQtoNames(self, context):
|
|||||||
global qtonames_enum
|
global qtonames_enum
|
||||||
qtonames_enum.clear()
|
qtonames_enum.clear()
|
||||||
if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements:
|
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(
|
qto_names = schema.ifc.psetqto.get_applicable_names(context.active_object.name.split("/")[0], qto_only=True)
|
||||||
bpy.context.scene.BIMProperties.export_schema, context.active_object.name.split("/")[0], is_qto=True
|
|
||||||
)
|
|
||||||
qtonames_enum.extend([(q, q, "") for q in qto_names])
|
qtonames_enum.extend([(q, q, "") for q in qto_names])
|
||||||
return qtonames_enum
|
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):
|
def getApplicableMaterialAttributes(self, context):
|
||||||
global materialattributes_enum
|
global materialattributes_enum
|
||||||
materialattributes_enum.clear()
|
materialattributes_enum.clear()
|
||||||
@@ -508,6 +506,19 @@ def getMaterialTypes(self, context):
|
|||||||
return materialtypes_enum
|
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):
|
def getSubcontexts(self, context):
|
||||||
global subcontexts_enum
|
global subcontexts_enum
|
||||||
subcontexts_enum.clear()
|
subcontexts_enum.clear()
|
||||||
@@ -556,19 +567,9 @@ class Attribute(PropertyGroup):
|
|||||||
bool_value: BoolProperty(name="Value")
|
bool_value: BoolProperty(name="Value")
|
||||||
int_value: IntProperty(name="Value")
|
int_value: IntProperty(name="Value")
|
||||||
float_value: FloatProperty(name="Value")
|
float_value: FloatProperty(name="Value")
|
||||||
|
is_null: BoolProperty(name="Is Null")
|
||||||
|
enum_items: StringProperty(name="Value")
|
||||||
class Subcontext(PropertyGroup):
|
enum_value: EnumProperty(items=getAttributeEnumValues, name="Value")
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
class MaterialLayer(PropertyGroup):
|
class MaterialLayer(PropertyGroup):
|
||||||
@@ -685,8 +686,7 @@ class DocProperties(PropertyGroup):
|
|||||||
drawing_styles: CollectionProperty(name="Drawing Styles", type=DrawingStyle)
|
drawing_styles: CollectionProperty(name="Drawing Styles", type=DrawingStyle)
|
||||||
should_draw_decorations: BoolProperty(name="Should Draw Decorations", update=toggleDecorations)
|
should_draw_decorations: BoolProperty(name="Should Draw Decorations", update=toggleDecorations)
|
||||||
decorations_colour: FloatVectorProperty(name="Decorations Colour", subtype="COLOR", default=(1, 0, 0, 1),
|
decorations_colour: FloatVectorProperty(name="Decorations Colour", subtype="COLOR", default=(1, 0, 0, 1),
|
||||||
min=0.0, max=1.0, size=4,
|
min=0.0, max=1.0, size=4)
|
||||||
update=toggleDecorations)
|
|
||||||
|
|
||||||
|
|
||||||
class BIMCameraProperties(PropertyGroup):
|
class BIMCameraProperties(PropertyGroup):
|
||||||
@@ -1097,6 +1097,7 @@ class Address(PropertyGroup):
|
|||||||
name: StringProperty(name="Name", default="IfcPostalAddress") # Stores IfcPostalAddress or IfcTelecomAddress
|
name: StringProperty(name="Name", default="IfcPostalAddress") # Stores IfcPostalAddress or IfcTelecomAddress
|
||||||
purpose: EnumProperty(
|
purpose: EnumProperty(
|
||||||
items=[
|
items=[
|
||||||
|
("None", "None", ""),
|
||||||
("OFFICE", "OFFICE", "An office address."),
|
("OFFICE", "OFFICE", "An office address."),
|
||||||
("SITE", "SITE", "A site address."),
|
("SITE", "SITE", "A site address."),
|
||||||
("HOME", "HOME", "A home address."),
|
("HOME", "HOME", "A home address."),
|
||||||
@@ -1117,7 +1118,7 @@ class Address(PropertyGroup):
|
|||||||
country: StringProperty(name="Country")
|
country: StringProperty(name="Country")
|
||||||
|
|
||||||
telephone_numbers: StringProperty(name="Telephone Numbers")
|
telephone_numbers: StringProperty(name="Telephone Numbers")
|
||||||
fascimile_numbers: StringProperty(name="Fascimile Numbers")
|
facsimile_numbers: StringProperty(name="Facsimile Numbers")
|
||||||
pager_number: StringProperty(name="Pager Number")
|
pager_number: StringProperty(name="Pager Number")
|
||||||
electronic_mail_addresses: StringProperty(name="Emails")
|
electronic_mail_addresses: StringProperty(name="Emails")
|
||||||
www_home_page_url: StringProperty(name="Websites")
|
www_home_page_url: StringProperty(name="Websites")
|
||||||
@@ -1158,12 +1159,9 @@ class Role(PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class Organisation(PropertyGroup):
|
class Organisation(PropertyGroup):
|
||||||
|
identification: StringProperty(name="Identification")
|
||||||
name: StringProperty(name="Name")
|
name: StringProperty(name="Name")
|
||||||
description: StringProperty(name="Description")
|
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):
|
class Person(PropertyGroup):
|
||||||
@@ -1173,10 +1171,6 @@ class Person(PropertyGroup):
|
|||||||
middle_names: StringProperty(name="Middle Names")
|
middle_names: StringProperty(name="Middle Names")
|
||||||
prefix_titles: StringProperty(name="Prefixes")
|
prefix_titles: StringProperty(name="Prefixes")
|
||||||
suffix_titles: StringProperty(name="Suffixes")
|
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):
|
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_faceted_brep: BoolProperty(name="Export with Faceted Breps", default=False)
|
||||||
export_should_force_triangulation: BoolProperty(name="Export with Triangulation", 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_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_ignore_building_coordinates: BoolProperty(name="Import Ignoring Building Coordinates", default=False)
|
||||||
import_should_reset_absolute_coordinates: BoolProperty(name="Import Resetting Absolute 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_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_use_legacy: BoolProperty(name="Import with Legacy Importer", default=False)
|
||||||
import_should_import_native: BoolProperty(name="Import Native Representations", 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_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_with_profiling: BoolProperty(name="Import with Profiling", default=True)
|
||||||
import_should_import_aggregates: BoolProperty(name="Import Aggregates", 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_should_offset_model: BoolProperty(name="Import and Offset Model", default=False)
|
||||||
import_model_offset_coordinates: StringProperty(name="Model Offset Coordinates", default="0,0,0")
|
import_model_offset_coordinates: StringProperty(name="Model Offset Coordinates", default="0,0,0")
|
||||||
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
|
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
|
||||||
person: EnumProperty(items=getPersons, name="Person")
|
|
||||||
organisation: EnumProperty(items=getOrganisations, name="Organisation")
|
person: PointerProperty(type=Person)
|
||||||
people: CollectionProperty(name="People", type=Person)
|
active_person_id: IntProperty(name="Active Person Id")
|
||||||
organisations: CollectionProperty(name="Organisations", type=Organisation)
|
organisation: PointerProperty(type=Organisation)
|
||||||
active_person_index: IntProperty(name="Active Person Index")
|
active_organisation_id: IntProperty(name="Active Organisation Id")
|
||||||
active_organisation_index: IntProperty(name="Active Organisation Index")
|
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_georeferencing: BoolProperty(name="Has Georeferencing", default=False)
|
||||||
has_library: BoolProperty(name="Has Project Library", default=False)
|
has_library: BoolProperty(name="Has Project Library", default=False)
|
||||||
search_regex: BoolProperty(name="Search With Regex", 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)
|
classification: EnumProperty(items=getClassifications, name="Classification", update=refreshReferences)
|
||||||
active_classification_name: StringProperty(name="Active Classification Name")
|
active_classification_name: StringProperty(name="Active Classification Name")
|
||||||
classifications: CollectionProperty(name="Classifications", type=Classification)
|
classifications: CollectionProperty(name="Classifications", type=Classification)
|
||||||
has_model_context: BoolProperty(name="Has Model Context", default=True)
|
contexts: EnumProperty(items=getContexts, name="Contexts")
|
||||||
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)
|
|
||||||
available_contexts: EnumProperty(items=[("Model", "Model", ""), ("Plan", "Plan", "")], name="Available Contexts")
|
available_contexts: EnumProperty(items=[("Model", "Model", ""), ("Plan", "Plan", "")], name="Available Contexts")
|
||||||
available_subcontexts: EnumProperty(items=getSubcontexts, name="Available Subcontexts")
|
available_subcontexts: EnumProperty(items=getSubcontexts, name="Available Subcontexts")
|
||||||
available_target_views: EnumProperty(items=getTargetViews, name="Available Target Views")
|
available_target_views: EnumProperty(items=getTargetViews, name="Available Target Views")
|
||||||
@@ -1487,14 +1484,23 @@ class BoundaryCondition(PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class BIMObjectProperties(PropertyGroup):
|
class BIMObjectProperties(PropertyGroup):
|
||||||
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
is_reassigning_class: BoolProperty(name="Is Reassigning Class")
|
is_reassigning_class: BoolProperty(name="Is Reassigning Class")
|
||||||
global_ids: CollectionProperty(name="GlobalIds", type=GlobalId)
|
global_ids: CollectionProperty(name="GlobalIds", type=GlobalId)
|
||||||
attributes: CollectionProperty(name="Attributes", type=Attribute)
|
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_type: PointerProperty(name="Type Product", type=bpy.types.Object)
|
||||||
relating_structure: PointerProperty(name="Spatial Container", 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)
|
psets: CollectionProperty(name="Psets", type=PsetQto)
|
||||||
qtos: CollectionProperty(name="Qtos", type=PsetQto)
|
qtos: CollectionProperty(name="Qtos", type=PsetQto)
|
||||||
applicable_attributes: EnumProperty(items=getApplicableAttributes, name="Attribute Names")
|
|
||||||
document_references: CollectionProperty(name="Document References", type=DocumentReference)
|
document_references: CollectionProperty(name="Document References", type=DocumentReference)
|
||||||
active_document_reference_index: IntProperty(name="Active Document Reference Index")
|
active_document_reference_index: IntProperty(name="Active Document Reference Index")
|
||||||
constraints: CollectionProperty(name="Constraints", type=Constraint)
|
constraints: CollectionProperty(name="Constraints", type=Constraint)
|
||||||
@@ -1508,20 +1514,10 @@ class BIMObjectProperties(PropertyGroup):
|
|||||||
has_boundary_condition: BoolProperty(name="Has Boundary Condition")
|
has_boundary_condition: BoolProperty(name="Has Boundary Condition")
|
||||||
boundary_condition: PointerProperty(name="Boundary Condition", type=BoundaryCondition)
|
boundary_condition: PointerProperty(name="Boundary Condition", type=BoundaryCondition)
|
||||||
structural_member_connection: PointerProperty(name="Structural Member Connection", type=bpy.types.Object)
|
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 applies to IfcSite's SiteAddress and IfcBuilding's BuildingAddress
|
||||||
address: PointerProperty(name="Address", type=Address)
|
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):
|
class BIMMaterialProperties(PropertyGroup):
|
||||||
is_external: BoolProperty(name="Has External Definition")
|
is_external: BoolProperty(name="Has External Definition")
|
||||||
location: StringProperty(name="Location")
|
location: StringProperty(name="Location")
|
||||||
@@ -1531,6 +1527,9 @@ class BIMMaterialProperties(PropertyGroup):
|
|||||||
psets: CollectionProperty(name="Psets", type=PsetQto)
|
psets: CollectionProperty(name="Psets", type=PsetQto)
|
||||||
attributes: CollectionProperty(name="Attributes", type=Attribute)
|
attributes: CollectionProperty(name="Attributes", type=Attribute)
|
||||||
applicable_attributes: EnumProperty(items=getApplicableMaterialAttributes, name="Attribute Names")
|
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):
|
class SweptSolid(PropertyGroup):
|
||||||
@@ -1551,13 +1550,13 @@ class ItemSlotMap(PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class BIMMeshProperties(PropertyGroup):
|
class BIMMeshProperties(PropertyGroup):
|
||||||
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
is_native: BoolProperty(name="Is Native", default=False)
|
is_native: BoolProperty(name="Is Native", default=False)
|
||||||
is_swept_solid: BoolProperty(name="Is Swept Solid")
|
is_swept_solid: BoolProperty(name="Is Swept Solid")
|
||||||
swept_solids: CollectionProperty(name="Swept Solids", type=SweptSolid)
|
swept_solids: CollectionProperty(name="Swept Solids", type=SweptSolid)
|
||||||
is_parametric: BoolProperty(name="Is Parametric", default=False)
|
is_parametric: BoolProperty(name="Is Parametric", default=False)
|
||||||
geometry_type: StringProperty(name="Geometry Type")
|
|
||||||
ifc_definition: StringProperty(name="IFC Definition")
|
ifc_definition: StringProperty(name="IFC Definition")
|
||||||
ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter)
|
ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter)
|
||||||
active_representation_item_index: IntProperty(name="Active Representation Item Index")
|
active_representation_item_index: IntProperty(name="Active Representation Item Index")
|
||||||
presentation_layer_index: IntProperty(name="Presentation Layer Index", default=-1)
|
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:
|
class IfcSchema:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.schema_dir = os.path.join(cwd, "schema") # TODO: make configurable
|
self.schema_dir = Path(cwd).joinpath("schema") # TODO: make configurable
|
||||||
self.data_dir = os.path.join(cwd, "data") # TODO: make configurable
|
self.data_dir = Path(cwd).joinpath("data") # TODO: make configurable
|
||||||
# TODO: Make it less troublesome
|
# TODO: Make it less troublesome
|
||||||
self.products = [
|
self.products = [
|
||||||
"IfcContext",
|
|
||||||
"IfcElement",
|
"IfcElement",
|
||||||
"IfcSpatialElement",
|
"IfcSpatialElement",
|
||||||
"IfcGroup",
|
"IfcGroup",
|
||||||
@@ -29,10 +28,11 @@ class IfcSchema:
|
|||||||
self.elements = {}
|
self.elements = {}
|
||||||
|
|
||||||
self.property_files = []
|
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:
|
for path in property_paths:
|
||||||
ifcopenshell.util.pset.load_property_set_template(path)
|
self.psetqto.templates.append(ifcopenshell.open(path))
|
||||||
ifcopenshell.util.pset.load_property_set_template(os.path.join(self.schema_dir, "Pset_IFC4_ADD2.ifc"))
|
|
||||||
|
|
||||||
self.classification_files = {}
|
self.classification_files = {}
|
||||||
self.classifications = {}
|
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
|
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):
|
class BIM_PT_object_material(Panel):
|
||||||
bl_label = "IFC Object Material"
|
bl_label = "IFC Object Material"
|
||||||
bl_idname = "BIM_PT_object_material"
|
bl_idname = "BIM_PT_object_material"
|
||||||
@@ -243,73 +143,6 @@ class BIM_PT_object_material(Panel):
|
|||||||
row.prop(attribute, "string_value", text="")
|
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):
|
class BIM_PT_object_qto(Panel):
|
||||||
bl_label = "IFC Object Quantity Sets"
|
bl_label = "IFC Object Quantity Sets"
|
||||||
bl_idname = "BIM_PT_object_qto"
|
bl_idname = "BIM_PT_object_qto"
|
||||||
@@ -626,48 +459,6 @@ class BIM_PT_constraint_relations(Panel):
|
|||||||
layout.label(text="Constraint is invalid")
|
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):
|
class BIM_PT_classification_references(Panel):
|
||||||
bl_label = "IFC Classification References"
|
bl_label = "IFC Classification References"
|
||||||
bl_idname = "BIM_PT_classification_references"
|
bl_idname = "BIM_PT_classification_references"
|
||||||
@@ -794,70 +585,6 @@ class BIM_PT_classifications(Panel):
|
|||||||
row.prop(props, "classifications")
|
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):
|
class BIM_PT_presentation_layer_data(Panel):
|
||||||
bl_label = "IFC Presentation Layers"
|
bl_label = "IFC Presentation Layers"
|
||||||
bl_idname = "BIM_PT_presentation"
|
bl_idname = "BIM_PT_presentation"
|
||||||
@@ -1303,213 +1030,6 @@ class BIM_PT_text(Panel):
|
|||||||
row.prop(variable, "prop_key")
|
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):
|
class BIM_PT_bim(Panel):
|
||||||
bl_label = "Building Information Modeling"
|
bl_label = "Building Information Modeling"
|
||||||
bl_idname = "BIM_PT_bim"
|
bl_idname = "BIM_PT_bim"
|
||||||
@@ -1526,9 +1046,6 @@ class BIM_PT_bim(Panel):
|
|||||||
|
|
||||||
layout.label(text="System Setup:")
|
layout.label(text="System Setup:")
|
||||||
|
|
||||||
row = layout.row()
|
|
||||||
row.operator("bim.quick_project_setup")
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(bim_properties, "schema_dir")
|
row.prop(bim_properties, "schema_dir")
|
||||||
row.operator("bim.select_schema_dir", icon="FILE_FOLDER", text="")
|
row.operator("bim.select_schema_dir", icon="FILE_FOLDER", text="")
|
||||||
@@ -1548,22 +1065,6 @@ class BIM_PT_bim(Panel):
|
|||||||
|
|
||||||
layout.label(text="IFC Categorisation:")
|
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 = layout.row(align=True)
|
||||||
row.operator("bim.select_class")
|
row.operator("bim.select_class")
|
||||||
row.operator("bim.select_type")
|
row.operator("bim.select_type")
|
||||||
@@ -1850,6 +1351,7 @@ class BIM_PT_patch(Panel):
|
|||||||
class BIM_PT_mvd(Panel):
|
class BIM_PT_mvd(Panel):
|
||||||
bl_label = "Model View Definitions (MVD)"
|
bl_label = "Model View Definitions (MVD)"
|
||||||
bl_idname = "BIM_PT_mvd"
|
bl_idname = "BIM_PT_mvd"
|
||||||
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
bl_space_type = "PROPERTIES"
|
bl_space_type = "PROPERTIES"
|
||||||
bl_region_type = "WINDOW"
|
bl_region_type = "WINDOW"
|
||||||
bl_context = "scene"
|
bl_context = "scene"
|
||||||
@@ -2360,68 +1862,6 @@ class BIM_PT_misc_utilities(Panel):
|
|||||||
row.operator("bim.snap_spaces_together")
|
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):
|
def ifc_units(self, context):
|
||||||
scene = context.scene
|
scene = context.scene
|
||||||
props = context.scene.BIMProperties
|
props = context.scene.BIMProperties
|
||||||
|
|||||||
@@ -376,6 +376,8 @@ int main(int argc, char** argv) {
|
|||||||
"Creates SVG elevation drawings automatically based on model extents")
|
"Creates SVG elevation drawings automatically based on model extents")
|
||||||
("svg-xmlns",
|
("svg-xmlns",
|
||||||
"Stores name and guid in a separate namespace as opposed to data-name, data-guid")
|
"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")
|
("door-arcs", "Draw door openings arcs for IfcDoor elements")
|
||||||
("section-height", po::value<double>(§ion_height),
|
("section-height", po::value<double>(§ion_height),
|
||||||
"Specifies the cut section height for SVG 2D geometry.")
|
"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_types = vmap.count("use-element-types") != 0;
|
||||||
const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 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 use_z_up = vmap.count("use-z-up") != 0;
|
||||||
|
|
||||||
const bool no_normals = vmap.count("no-normals") != 0;
|
const bool no_normals = vmap.count("no-normals") != 0;
|
||||||
const bool center_model = vmap.count("center-model") != 0;
|
const bool center_model = vmap.count("center-model") != 0;
|
||||||
const bool center_model_geometry = vmap.count("center-model-geometry") != 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_NAMES, use_element_names);
|
||||||
settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids);
|
settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids);
|
||||||
settings.set(SerializerSettings::USE_Z_UP, use_z_up);
|
settings.set(SerializerSettings::USE_Z_UP, use_z_up);
|
||||||
|
|
||||||
settings.set(SerializerSettings::USE_ELEMENT_STEPIDS, use_element_stepids);
|
settings.set(SerializerSettings::USE_ELEMENT_STEPIDS, use_element_stepids);
|
||||||
settings.set(SerializerSettings::USE_MATERIAL_NAMES, use_material_names);
|
settings.set(SerializerSettings::USE_MATERIAL_NAMES, use_material_names);
|
||||||
settings.set(SerializerSettings::USE_ELEMENT_TYPES, use_element_types);
|
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())->setAutoElevation(true);
|
||||||
}
|
}
|
||||||
static_cast<SvgSerializer*>(serializer.get())->setUseNamespace(vmap.count("svg-xmlns") > 0);
|
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) {
|
if (relative_center_x && relative_center_y) {
|
||||||
static_cast<SvgSerializer*>(serializer.get())->setDrawingCenter(*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
|
||||||
import ifcopenshell.util.selector
|
import ifcopenshell.util.selector
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
import ifcopenshell.util.schema
|
||||||
import csv
|
import csv
|
||||||
import lark
|
import lark
|
||||||
import argparse
|
import argparse
|
||||||
@@ -13,7 +14,7 @@ class IfcAttributeExtractor:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def set_element_key(ifc_file, element, key, value):
|
def set_element_key(ifc_file, element, key, value):
|
||||||
if key == "type" and element.is_a() != 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):
|
if hasattr(element, key):
|
||||||
setattr(element, key, value)
|
setattr(element, key, value)
|
||||||
return element
|
return element
|
||||||
@@ -32,24 +33,6 @@ class IfcAttributeExtractor:
|
|||||||
return element
|
return element
|
||||||
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
|
@staticmethod
|
||||||
def get_element_qto(element, name):
|
def get_element_qto(element, name):
|
||||||
for relationship in element.IsDefinedBy:
|
for relationship in element.IsDefinedBy:
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ def open(fn):
|
|||||||
raise IOError("Unable to open file for reading")
|
raise IOError("Unable to open file for reading")
|
||||||
|
|
||||||
|
|
||||||
def create_entity(type, *args, **kwargs):
|
def create_entity(type, schema='IFC4', *args, **kwargs):
|
||||||
e = entity_instance(type)
|
e = entity_instance((schema, type))
|
||||||
attrs = list(enumerate(args)) + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
|
attrs = list(enumerate(args)) + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
|
||||||
for idx, arg in attrs:
|
for idx, arg in attrs:
|
||||||
e[idx] = arg
|
e[idx] = arg
|
||||||
|
|||||||
@@ -271,3 +271,10 @@ class entity_instance(object):
|
|||||||
return return_type(_())
|
return return_type(_())
|
||||||
|
|
||||||
__dict__ = property(get_info)
|
__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:
|
if item == old:
|
||||||
new_attribute[j] = new
|
new_attribute[j] = new
|
||||||
element[i] = new_attribute
|
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
|
import ifcopenshell
|
||||||
|
from ifcopenshell.entity_instance import entity_instance
|
||||||
property_set_template_files = []
|
|
||||||
psets = {}
|
|
||||||
qtos = {}
|
|
||||||
applicable_psets = {}
|
|
||||||
applicable_qtos = {}
|
|
||||||
|
|
||||||
|
|
||||||
def load_property_set_template(path):
|
class PsetQto:
|
||||||
property_set_template_files.append(ifcopenshell.open(path))
|
templates_path = {
|
||||||
for prop in property_set_template_files[-1].by_type("IfcPropertySetTemplate"):
|
"IFC4": "Pset_IFC4_ADD2.ifc",
|
||||||
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)
|
|
||||||
|
|
||||||
|
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):
|
@lru_cache()
|
||||||
def is_a(entity, ifc_class):
|
def get_applicable(
|
||||||
if entity.name() == ifc_class:
|
self, ifc_class="", predefined_type="", pset_only=False, qto_only=False
|
||||||
return True
|
) -> Generator[entity_instance, entity_instance, None]:
|
||||||
return is_a(entity.supertype(), ifc_class) if entity.supertype() else False
|
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 = []
|
def get_applicable_names(self, ifc_class: str, predefined_type="", pset_only=False, qto_only=False) -> List[str]:
|
||||||
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_version)
|
"""Return names instead of objects for other use eg. enum"""
|
||||||
entity = schema.declaration_by_name(ifc_class)
|
return [prop_set.Name for prop_set in self.get_applicable(ifc_class, predefined_type, pset_only, qto_only)]
|
||||||
if is_pset:
|
|
||||||
search_items = applicable_psets.items()
|
def is_applicable(self, entity: entity_instance, applicables: str, predefined_type="") -> bool:
|
||||||
elif is_qto:
|
"""applicables can have multiple possible patterns :
|
||||||
search_items = applicable_qtos.items()
|
IfcBoilerType (IfcClass)
|
||||||
for ifc_class, pset_names in search_items:
|
IfcBoilerType/STEAM (IfcClass/PREDEFINEDTYPE)
|
||||||
if is_a(entity, ifc_class):
|
IfcBoilerType[PerformanceHistory] (IfcClass[PerformanceHistory])
|
||||||
results.extend(pset_names)
|
IfcBoilerType/STEAM[PerformanceHistory] (IfcClass/PREDEFINEDTYPE[PerformanceHistory])
|
||||||
return results
|
"""
|
||||||
|
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
|
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:
|
class Migrator:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.migrated_ids = {}
|
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)
|
||||||
value *= 1 / get_prefix_multiplier(to_prefix)
|
value *= 1 / get_prefix_multiplier(to_prefix)
|
||||||
return value
|
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_)) {
|
if (entity->declaration().is(*ifcroot_type_)) {
|
||||||
const std::string global_id = *entity->data().getArgument(0);
|
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));
|
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")
|
MESSAGE(WARNING "Unable to locate Python site-package directory, unable to install the Python wrapper")
|
||||||
ELSE()
|
ELSE()
|
||||||
FILE(GLOB_RECURSE sourcefiles
|
FILE(GLOB_RECURSE sourcefiles
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/*.py"
|
"${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/*"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/*.bnf"
|
|
||||||
)
|
)
|
||||||
FOREACH(file ${sourcefiles})
|
FOREACH(file ${sourcefiles})
|
||||||
FILE(RELATIVE_PATH relative "${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/" "${file}")
|
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.
|
/// Applicable for OBJ, DAE, and SVG output.
|
||||||
USE_ELEMENT_STEPIDS = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 6),
|
USE_ELEMENT_STEPIDS = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 6),
|
||||||
/// Use step Z UP .
|
/// Use step Z UP .
|
||||||
/// Applicable for OBJ output.
|
|
||||||
USE_Z_UP = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 7),
|
USE_Z_UP = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 7),
|
||||||
/// Number of different setting flags.
|
/// Number of different setting flags.
|
||||||
NUM_SETTINGS = 7
|
NUM_SETTINGS = 7
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
#include <gp_Pln.hxx>
|
#include <gp_Pln.hxx>
|
||||||
#include <gp_Trsf.hxx>
|
#include <gp_Trsf.hxx>
|
||||||
@@ -67,6 +68,8 @@
|
|||||||
|
|
||||||
#include <ShapeFix_Edge.hxx>
|
#include <ShapeFix_Edge.hxx>
|
||||||
|
|
||||||
|
#include <HLRBRep_PolyHLRToShape.hxx>
|
||||||
|
|
||||||
#include "../ifcparse/IfcGlobalId.h"
|
#include "../ifcparse/IfcGlobalId.h"
|
||||||
|
|
||||||
#include "SvgSerializer.h"
|
#include "SvgSerializer.h"
|
||||||
@@ -292,16 +295,18 @@ void SvgSerializer::write(path_object& p, const TopoDS_Wire& wire) {
|
|||||||
p.second.push_back(path);
|
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());
|
auto key = std::make_pair(std::make_pair(storey, ""), path_object());
|
||||||
SvgSerializer::path_object& p = paths.insert(key)->second;
|
SvgSerializer::path_object& p = paths.insert(key)->second;
|
||||||
|
drawing_metadata[key.first].pln_3d = pln;
|
||||||
p.first = id;
|
p.first = id;
|
||||||
return p;
|
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());
|
auto key = std::make_pair(std::make_pair(nullptr, drawing_name), path_object());
|
||||||
SvgSerializer::path_object& p = paths.insert(key)->second;
|
SvgSerializer::path_object& p = paths.insert(key)->second;
|
||||||
|
drawing_metadata[key.first].pln_3d = pln;
|
||||||
p.first = id;
|
p.first = id;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@@ -563,7 +568,7 @@ void SvgSerializer::write(const geometry_data& data) {
|
|||||||
|
|
||||||
auto& compound_to_use = is_floor_plan_ ? compound : compound_unmirrored;
|
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
|
// Check if any of the bounding box points is on the correct side of the plane
|
||||||
Bnd_Box bb;
|
Bnd_Box bb;
|
||||||
@@ -590,13 +595,19 @@ void SvgSerializer::write(const geometry_data& data) {
|
|||||||
} else {
|
} else {
|
||||||
state = d < 0. ? -1 : 1;
|
state = d < 0. ? -1 : 1;
|
||||||
}
|
}
|
||||||
if (state == 1) {
|
if (state == -1) {
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (any) {
|
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.;
|
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 bbmin(x1, y1, zmin);
|
||||||
gp_Vec bbmax(x2, y2, zmax);
|
gp_Vec bbmax(x2, y2, zmax);
|
||||||
auto bbdif = bbmax - bbmin;
|
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 (data.product->declaration().is("IfcAnnotation") && (proj.Magnitude() > 1.e-5) && zmin >= range.first && zmin <= range.second) {
|
||||||
if (po == nullptr) {
|
if (po == nullptr) {
|
||||||
if (storey) {
|
if (storey) {
|
||||||
po = &start_path(storey, data.svg_name);
|
po = &start_path(pln, storey, data.svg_name);
|
||||||
} else {
|
} 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 (po == nullptr) {
|
||||||
if (storey) {
|
if (storey) {
|
||||||
po = &start_path(storey, data.svg_name);
|
po = &start_path(pln, storey, data.svg_name);
|
||||||
} else {
|
} 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);
|
TopoDS_Shape result = BRepAlgoAPI_Section(subshape, pln);
|
||||||
|
|
||||||
if (variant.which() == 2) {
|
if (variant.which() == 2) {
|
||||||
@@ -889,7 +901,10 @@ void SvgSerializer::setBoundingRectangle(double width, double height) {
|
|||||||
this->rescale = true;
|
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) {
|
if (rescale) {
|
||||||
// Scale the resulting image to a bounding rectangle specified by command line arguments
|
// Scale the resulting image to a bounding rectangle specified by command line arguments
|
||||||
const double dx = xmax - xmin;
|
const double dx = xmax - xmin;
|
||||||
@@ -918,6 +933,8 @@ void SvgSerializer::resize() {
|
|||||||
cy = ymin * sc;
|
cy = ymin * sc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m = {{ {{sc,0,-cx}},{{0,sc,-cy}},{{0,0,1}} }};
|
||||||
|
|
||||||
float_item_list::const_iterator it;
|
float_item_list::const_iterator it;
|
||||||
for (it = xcoords.begin() + xcoords_begin; it != xcoords.end(); ++it, ++xcoords_begin) {
|
for (it = xcoords.begin() + xcoords_begin; it != xcoords.end(); ++it, ++xcoords_begin) {
|
||||||
double& v = (*it)->value();
|
double& v = (*it)->value();
|
||||||
@@ -931,6 +948,8 @@ void SvgSerializer::resize() {
|
|||||||
(*it)->value() *= sc;
|
(*it)->value() *= sc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SvgSerializer::resetScale() {
|
void SvgSerializer::resetScale() {
|
||||||
@@ -944,7 +963,12 @@ void SvgSerializer::resetScale() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SvgSerializer::finalize() {
|
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_)) {
|
if (!deferred_section_data_.is_initialized() && (auto_section_ || auto_elevation_)) {
|
||||||
deferred_section_data_.emplace();
|
deferred_section_data_.emplace();
|
||||||
@@ -972,28 +996,28 @@ void SvgSerializer::finalize() {
|
|||||||
if (auto_elevation_) {
|
if (auto_elevation_) {
|
||||||
{
|
{
|
||||||
gp_Pln pln(gp_Ax3(
|
gp_Pln pln(gp_Ax3(
|
||||||
gp_Pnt((xmin + xmax) / 2., -(ymin - 1.e-1), 0.),
|
gp_Pnt(0., -(ymin - 10.), 0.),
|
||||||
gp_Dir(0, -1, 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 South", true });
|
deferred_section_data_->push_back(vertical_section{ pln , "Elevation South", true });
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
gp_Pln pln(gp_Ax3(
|
gp_Pln pln(gp_Ax3(
|
||||||
gp_Pnt(xmax + 1.e-1, (ymin + ymax) / -2., 0.),
|
gp_Pnt(xmax + 10., 0., 0.),
|
||||||
gp_Dir(-1, 0, 0),
|
gp_Dir(1, 0, 0),
|
||||||
gp_Dir(0, -1, 0)));
|
gp_Dir(0, 1, 0)));
|
||||||
deferred_section_data_->push_back(vertical_section{ pln , "Elevation East", true });
|
deferred_section_data_->push_back(vertical_section{ pln , "Elevation East", true });
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
gp_Pln pln(gp_Ax3(
|
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(0, -1, 0),
|
||||||
gp_Dir(1, 0, 0)));
|
gp_Dir(-1, 0, 0)));
|
||||||
deferred_section_data_->push_back(vertical_section{ pln , "Elevation North", true });
|
deferred_section_data_->push_back(vertical_section{ pln , "Elevation North", true });
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
gp_Pln pln(gp_Ax3(
|
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(-1, 0, 0),
|
||||||
gp_Dir(0, -1, 0)));
|
gp_Dir(0, -1, 0)));
|
||||||
deferred_section_data_->push_back(vertical_section{ pln , "Elevation West", true });
|
deferred_section_data_->push_back(vertical_section{ pln , "Elevation West", true });
|
||||||
@@ -1002,7 +1026,6 @@ void SvgSerializer::finalize() {
|
|||||||
|
|
||||||
resetScale();
|
resetScale();
|
||||||
|
|
||||||
|
|
||||||
if (deferred_section_data_ && deferred_section_data_->size() && element_buffer_.size()) {
|
if (deferred_section_data_ && deferred_section_data_->size() && element_buffer_.size()) {
|
||||||
|
|
||||||
// Draw door arcs only on floor plans.
|
// Draw door arcs only on floor plans.
|
||||||
@@ -1018,7 +1041,11 @@ void SvgSerializer::finalize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (use_hlr) {
|
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 };
|
section_data_ = std::vector<section_data>{ sd };
|
||||||
@@ -1028,18 +1055,30 @@ void SvgSerializer::finalize() {
|
|||||||
|
|
||||||
if (use_hlr) {
|
if (use_hlr) {
|
||||||
const auto& section = boost::get<vertical_section>(sd);
|
const auto& section = boost::get<vertical_section>(sd);
|
||||||
gp_Ax2 transform = section.plane.Position().Ax2();
|
|
||||||
HLRAlgo_Projector projector(transform);
|
|
||||||
hlr->Projector(projector);
|
|
||||||
|
|
||||||
hlr->Update();
|
gp_Trsf trsf;
|
||||||
hlr->Hide();
|
trsf.SetTransformation(section.plane.Position());
|
||||||
|
HLRAlgo_Projector projector(trsf, false, 1.);
|
||||||
|
|
||||||
HLRBRep_HLRToShape hlr_shapes(hlr);
|
TopoDS_Shape hlr_compound_unmirrored;
|
||||||
auto hlr_compound_unmirrored = hlr_shapes.VCompound();
|
|
||||||
|
if (use_hlr_poly_) {
|
||||||
|
hlr_poly->Projector(projector);
|
||||||
|
|
||||||
|
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()) {
|
if (!hlr_compound_unmirrored.IsNull()) {
|
||||||
|
|
||||||
// Compound 3D curves for mirroring to work
|
// Compound 3D curves for mirroring to work
|
||||||
ShapeFix_Edge sfe;
|
ShapeFix_Edge sfe;
|
||||||
TopExp_Explorer exp(hlr_compound_unmirrored, TopAbs_EDGE);
|
TopExp_Explorer exp(hlr_compound_unmirrored, TopAbs_EDGE);
|
||||||
@@ -1059,7 +1098,7 @@ void SvgSerializer::finalize() {
|
|||||||
|
|
||||||
exp.Init(hlr_compound, TopAbs_EDGE);
|
exp.Init(hlr_compound, TopAbs_EDGE);
|
||||||
BRep_Builder B;
|
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()) {
|
for (; exp.More(); exp.Next()) {
|
||||||
TopoDS_Wire w;
|
TopoDS_Wire w;
|
||||||
B.MakeWire(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();
|
resetScale();
|
||||||
|
|
||||||
if (use_hlr) {
|
if (hlr_brep) hlr_brep.Nullify();
|
||||||
hlr.Nullify();
|
if (hlr_poly) hlr_poly.Nullify();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1089,11 +1131,11 @@ void SvgSerializer::finalize() {
|
|||||||
}
|
}
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
if (it->first.first) {
|
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 {
|
} else {
|
||||||
auto n = it->first.second;
|
auto n = it->first.second;
|
||||||
IfcUtil::escape_xml(n);
|
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";
|
svg_file << " <g " << it->second.first << ">\n";
|
||||||
@@ -1285,3 +1327,33 @@ void SvgSerializer::setSectionHeightsFromStoreys(double offset) {
|
|||||||
section_data_->push_back(horizontal_plan_at_element{});
|
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_Algo.hxx>
|
||||||
#include <HLRBRep_HLRToShape.hxx>
|
#include <HLRBRep_HLRToShape.hxx>
|
||||||
|
#include <HLRBRep_PolyAlgo.hxx>
|
||||||
|
#include <HLRAlgo_Projector.hxx>
|
||||||
#include <gp_Pln.hxx>
|
#include <gp_Pln.hxx>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -107,6 +109,11 @@ struct geometry_data {
|
|||||||
std::string ifc_name, svg_name;
|
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 {
|
class SvgSerializer : public GeometrySerializer {
|
||||||
public:
|
public:
|
||||||
typedef std::pair<std::string, std::vector<util::string_buffer> > path_object;
|
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 with_section_heights_from_storey_, rescale, print_space_names_, print_space_areas_;
|
||||||
bool draw_door_arcs_, is_floor_plan_;
|
bool draw_door_arcs_, is_floor_plan_;
|
||||||
bool auto_section_, auto_elevation_;
|
bool auto_section_, auto_elevation_;
|
||||||
bool use_namespace_;
|
bool use_namespace_, use_hlr_poly_;
|
||||||
|
|
||||||
IfcParse::IfcFile* file;
|
IfcParse::IfcFile* file;
|
||||||
IfcUtil::IfcBaseEntity* storey_;
|
IfcUtil::IfcBaseEntity* storey_;
|
||||||
std::multimap<drawing_key, path_object, storey_sorter> paths;
|
std::multimap<drawing_key, path_object, storey_sorter> paths;
|
||||||
|
std::map<drawing_key, drawing_meta> drawing_metadata;
|
||||||
|
|
||||||
float_item_list xcoords, ycoords, radii;
|
float_item_list xcoords, ycoords, radii;
|
||||||
size_t xcoords_begin, ycoords_begin, radii_begin;
|
size_t xcoords_begin, ycoords_begin, radii_begin;
|
||||||
@@ -134,7 +142,8 @@ protected:
|
|||||||
|
|
||||||
std::list<geometry_data> element_buffer_;
|
std::list<geometry_data> element_buffer_;
|
||||||
|
|
||||||
Handle(HLRBRep_Algo) hlr;
|
Handle(HLRBRep_Algo) hlr_brep;
|
||||||
|
Handle(HLRBRep_PolyAlgo) hlr_poly;
|
||||||
|
|
||||||
std::string namespace_prefix_;
|
std::string namespace_prefix_;
|
||||||
public:
|
public:
|
||||||
@@ -154,6 +163,7 @@ public:
|
|||||||
, auto_section_(false)
|
, auto_section_(false)
|
||||||
, auto_elevation_(false)
|
, auto_elevation_(false)
|
||||||
, use_namespace_(false)
|
, use_namespace_(false)
|
||||||
|
, use_hlr_poly_(false)
|
||||||
, file(0)
|
, file(0)
|
||||||
, storey_(0)
|
, storey_(0)
|
||||||
, xcoords_begin(0)
|
, xcoords_begin(0)
|
||||||
@@ -171,8 +181,8 @@ public:
|
|||||||
void write(const IfcGeom::BRepElement<real_t>* o);
|
void write(const IfcGeom::BRepElement<real_t>* o);
|
||||||
void write(path_object& p, const TopoDS_Wire& wire);
|
void write(path_object& p, const TopoDS_Wire& wire);
|
||||||
void write(const geometry_data& data);
|
void write(const geometry_data& data);
|
||||||
path_object& start_path(IfcUtil::IfcBaseEntity* storey, const std::string& id);
|
path_object& start_path(const gp_Pln& p, 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, const std::string& drawing_name, const std::string& id);
|
||||||
bool isTesselated() const { return false; }
|
bool isTesselated() const { return false; }
|
||||||
void finalize();
|
void finalize();
|
||||||
void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
|
void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
|
||||||
@@ -184,7 +194,7 @@ public:
|
|||||||
void setPrintSpaceAreas(bool b) { print_space_areas_ = b; }
|
void setPrintSpaceAreas(bool b) { print_space_areas_ = b; }
|
||||||
void setDrawDoorArcs(bool b) { draw_door_arcs_ = b; }
|
void setDrawDoorArcs(bool b) { draw_door_arcs_ = b; }
|
||||||
|
|
||||||
void resize();
|
std::array<std::array<double, 3>, 3> resize();
|
||||||
void resetScale();
|
void resetScale();
|
||||||
|
|
||||||
void setSectionRef(const boost::optional<std::string>& s) {
|
void setSectionRef(const boost::optional<std::string>& s) {
|
||||||
@@ -207,6 +217,10 @@ public:
|
|||||||
namespace_prefix_ = use_namespace_ ? "ifc:" : "data-";
|
namespace_prefix_ = use_namespace_ ? "ifc:" : "data-";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setUseHlrPoly(bool b) {
|
||||||
|
use_hlr_poly_ = b;
|
||||||
|
}
|
||||||
|
|
||||||
void setScale(double s) { scale_ = s; }
|
void setScale(double s) { scale_ = s; }
|
||||||
void setDrawingCenter(double x, double y) {
|
void setDrawingCenter(double x, double y) {
|
||||||
center_x_ = x; center_y_ = y;
|
center_x_ = x; center_y_ = y;
|
||||||
@@ -221,6 +235,9 @@ public:
|
|||||||
return GeometrySerializer::object_id(o);
|
return GeometrySerializer::object_id(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string writeMetadata(const drawing_meta& m);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user