mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-17 22:11:36 +00:00
update mager
This commit is contained in:
@@ -36,7 +36,7 @@ OPTION(IFCXML_SUPPORT "Build IfcParse with ifcXML support (requires libxml2)." O
|
|||||||
OPTION(ENABLE_BUILD_OPTIMIZATIONS "Enable certain compiler and linker optimizations on RelWithDebInfo and Release builds." OFF)
|
OPTION(ENABLE_BUILD_OPTIMIZATIONS "Enable certain compiler and linker optimizations on RelWithDebInfo and Release builds." OFF)
|
||||||
OPTION(IFCCONVERT_DOUBLE_PRECISION "IfcConvert: Use double precision floating-point numbers." ON)
|
OPTION(IFCCONVERT_DOUBLE_PRECISION "IfcConvert: Use double precision floating-point numbers." ON)
|
||||||
OPTION(BUILD_IFCGEOM "Build IfcGeom." ON)
|
OPTION(BUILD_IFCGEOM "Build IfcGeom." ON)
|
||||||
OPTION(BUILD_IFCPYTHON "Build IfcPython." ON)
|
OPTION(BUILD_IFCPYTHON "Build IfcPython." OFF)
|
||||||
OPTION(BUILD_EXAMPLES "Build example applications." ON)
|
OPTION(BUILD_EXAMPLES "Build example applications." ON)
|
||||||
OPTION(BUILD_GEOMSERVER "Build IfcGeomServer executable." ON)
|
OPTION(BUILD_GEOMSERVER "Build IfcGeomServer executable." ON)
|
||||||
OPTION(BUILD_CONVERT "Build IfcConvert executable." ON)
|
OPTION(BUILD_CONVERT "Build IfcConvert executable." ON)
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# ****************************************************************************
|
||||||
|
# line endings
|
||||||
|
|
||||||
|
# to suppress line ending changes in github, add ?w=1 at link end of a diff
|
||||||
|
|
||||||
|
# for more information see
|
||||||
|
# FreeCAD source code src/Mod/.gitattributes
|
||||||
|
# FreeCAD forum topic https://forum.freecadweb.org/viewtopic.php?f=17&t=41117
|
||||||
|
# FreeCAD pull request https://github.com/FreeCAD/FreeCAD/pull/2752
|
||||||
|
|
||||||
|
|
||||||
|
# get all used file types
|
||||||
|
|
||||||
|
# in a directory in a bash use
|
||||||
|
# find . -type f -name '*.*' | sed 's|.*\.||' | sort -u
|
||||||
|
|
||||||
|
# search for a specific file ending
|
||||||
|
# find . -type f -name '*.ico'
|
||||||
|
|
||||||
|
|
||||||
|
# normalize the line endings of the following files
|
||||||
|
# standard files
|
||||||
|
*.feature text
|
||||||
|
*.html text
|
||||||
|
*.ifc text
|
||||||
|
*.md text
|
||||||
|
*.po text
|
||||||
|
*.pot text
|
||||||
|
*.py text
|
||||||
|
|
||||||
|
|
||||||
|
# files which are human readable
|
||||||
|
# but for which it is not sure if normalize is ok
|
||||||
|
# svg
|
||||||
|
|
||||||
|
|
||||||
|
# binary files
|
||||||
|
# ico
|
||||||
|
# mo
|
||||||
|
|
||||||
|
|
||||||
|
# line endings of the directories commented will be normalized
|
||||||
|
# bimtester/** -text
|
||||||
|
# examples/** -text
|
||||||
|
|
||||||
|
|
||||||
|
# line endings of the directories NOT commented will NOT be normalized
|
||||||
|
# Be carefully changes here could affect a lot of files automatically!
|
||||||
|
# none ATM
|
||||||
|
|
||||||
|
|
||||||
|
# use git to manually correct the file endings
|
||||||
|
# git add --renormalize .
|
||||||
@@ -15,8 +15,11 @@ def before_all(context):
|
|||||||
|
|
||||||
# get from userdata
|
# get from userdata
|
||||||
userdata = context.config.userdata
|
userdata = context.config.userdata
|
||||||
context.ifcbasename = userdata["ifcbasename"]
|
|
||||||
context.localedir = userdata.get("localedir")
|
context.localedir = userdata.get("localedir")
|
||||||
|
context.ifcfile = userdata["ifcfile"]
|
||||||
|
context.ifcbasename = os.path.basename(
|
||||||
|
os.path.splitext(context.ifcfile)[0]
|
||||||
|
)
|
||||||
|
|
||||||
# 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
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def create_logfile(thelogfile, ifcbasename):
|
||||||
|
|
||||||
|
logfile = open(thelogfile, "w")
|
||||||
|
logfile.write("BIMTester log file\n")
|
||||||
|
logfile.write("------------------\n\n")
|
||||||
|
logfile.write("ifc base file name: {}\n".format(ifcbasename))
|
||||||
|
logfile.close()
|
||||||
|
|
||||||
|
|
||||||
|
def append_logfile(thecontext, step):
|
||||||
|
|
||||||
|
# step attributes (also these set by user) scope is the scenario
|
||||||
|
# https://behave.readthedocs.io/en/latest/thecontext_attributes.html
|
||||||
|
print("Step '{}' failed".format(step.name))
|
||||||
|
|
||||||
|
# log file
|
||||||
|
logfile = open(thecontext.thelogfile, "a")
|
||||||
|
logfile.write("\n\nStep '{}' failed\n".format(step.name))
|
||||||
|
if hasattr(thecontext, "falseelems"):
|
||||||
|
logfile.write("{}\n".format(
|
||||||
|
json.dumps(thecontext.falseelems, indent=4)
|
||||||
|
))
|
||||||
|
if hasattr(thecontext, "falseprops"):
|
||||||
|
logfile.write("{}\n".format(
|
||||||
|
json.dumps(thecontext.falseprops, indent=4)
|
||||||
|
))
|
||||||
|
logfile.close()
|
||||||
@@ -9,17 +9,25 @@ from utils import switch_locale
|
|||||||
the_lang = "en"
|
the_lang = "en"
|
||||||
|
|
||||||
|
|
||||||
@step("there are no {ifc_class} elements because {reason}")
|
@step("There are no {ifc_class} elements")
|
||||||
def step_impl(context, ifc_class, reason):
|
def step_impl(context, ifc_class):
|
||||||
switch_locale(context.localedir, the_lang)
|
switch_locale(context.localedir, the_lang)
|
||||||
aem.no_eleclass_because_reason(
|
aem.no_eleclass(
|
||||||
context,
|
context,
|
||||||
ifc_class,
|
ifc_class
|
||||||
reason
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@step('all {ifc_class} elements class attributes have a value')
|
@step("There are no {ifc_class} elements because {reason}")
|
||||||
|
def step_impl(context, ifc_class, reason):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
aem.no_eleclass(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@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, the_lang)
|
switch_locale(context.localedir, the_lang)
|
||||||
aem.eleclass_have_class_attributes_with_a_value(
|
aem.eleclass_have_class_attributes_with_a_value(
|
||||||
@@ -28,7 +36,7 @@ def step_impl(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, the_lang)
|
switch_locale(context.localedir, the_lang)
|
||||||
aem.eleclass_has_name_with_a_value(
|
aem.eleclass_has_name_with_a_value(
|
||||||
@@ -37,7 +45,7 @@ def step_impl(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, the_lang)
|
switch_locale(context.localedir, the_lang)
|
||||||
aem.eleclass_has_description_with_a_value(
|
aem.eleclass_has_description_with_a_value(
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
import attributes_eleclasses_methods as aem
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "de"
|
||||||
|
|
||||||
|
|
||||||
|
@step("Es sind keine {ifc_class} Bauteile vorhanden")
|
||||||
|
def step_impl(context, ifc_class):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
aem.no_eleclass(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@step("Aus folgendem Grund gibt es keine {ifc_class} Bauteile: {reason}")
|
||||||
|
def step_impl(context, ifc_class, reason):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
aem.no_eleclass(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@step("Alle {ifc_class} Bauteilklassenattribute haben einen Wert")
|
||||||
|
def step_impl(context, ifc_class):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
aem.eleclass_have_class_attributes_with_a_value(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@step("Bei allen {ifc_class} Bauteile ist der Name angegeben")
|
||||||
|
def step_impl(context, ifc_class):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
aem.eleclass_has_name_with_a_value(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@step("Bei allen {ifc_class} Bauteile ist die Beschreibung angegeben")
|
||||||
|
def step_impl(context, ifc_class):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
aem.eleclass_has_description_with_a_value(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
import attributes_eleclasses_methods as aem
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "fr"
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@step("There are no {ifc_class} elements")
|
||||||
|
def step_impl(context, ifc_class):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
aem.no_eleclass(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@step("There are no {ifc_class} elements because {reason}")
|
||||||
|
def step_impl(context, ifc_class, reason):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
aem.no_eleclass(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@step("All {ifc_class} elements class attributes have a value")
|
||||||
|
def step_impl(context, ifc_class):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
aem.eleclass_have_class_attributes_with_a_value(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@step("All {ifc_class} elements have a name given")
|
||||||
|
def step_impl(context, ifc_class):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
aem.eleclass_has_name_with_a_value(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@step("All {ifc_class} elements have a description given")
|
||||||
|
def step_impl(context, ifc_class):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
aem.eleclass_has_description_with_a_value(
|
||||||
|
context,
|
||||||
|
ifc_class
|
||||||
|
)
|
||||||
|
"""
|
||||||
@@ -4,8 +4,8 @@ from utils import assert_elements
|
|||||||
from utils import IfcFile
|
from utils import IfcFile
|
||||||
|
|
||||||
|
|
||||||
def no_eleclass_because_reason(
|
def no_eleclass(
|
||||||
context, ifc_class, reason
|
context, ifc_class
|
||||||
):
|
):
|
||||||
|
|
||||||
context.falseelems = []
|
context.falseelems = []
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
import attributes_psets_methods as apm
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "de"
|
||||||
|
|
||||||
|
|
||||||
|
@step("An alle {ifc_class} Bauteile ist im PSet {pset} das Attribut {aproperty} angehängt")
|
||||||
|
def step_impl(context, ifc_class, aproperty, pset):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
apm.eleclass_has_property_in_pset(
|
||||||
|
context,
|
||||||
|
ifc_class,
|
||||||
|
aproperty,
|
||||||
|
pset
|
||||||
|
)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
import attributes_psets_methods as apm
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "fr"
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@step("All {ifc_class} elements have an {aproperty} property in the {pset} pset")
|
||||||
|
def step_impl(context, ifc_class, aproperty, pset):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
apm.eleclass_has_property_in_pset(
|
||||||
|
context,
|
||||||
|
ifc_class,
|
||||||
|
aproperty,
|
||||||
|
pset
|
||||||
|
)
|
||||||
|
"""
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
import geometric_detail_methods as gdm
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "fr"
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@step("all {ifc_class} elements have an {representation_class} representation")
|
||||||
|
def step_impl(context, ifc_class, representation_class):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
gdm.eleclass_has_geometric_representation_of_specific_class(
|
||||||
|
context,
|
||||||
|
ifc_class,
|
||||||
|
representation_class
|
||||||
|
)
|
||||||
|
"""
|
||||||
@@ -2,7 +2,7 @@ import gettext # noqa
|
|||||||
from behave import given
|
from behave import given
|
||||||
from behave import step
|
from behave import step
|
||||||
|
|
||||||
from ifcdata_methods import assert_schema
|
import ifcdata_methods as idm
|
||||||
from utils import IfcFile
|
from utils import IfcFile
|
||||||
from utils import switch_locale
|
from utils import switch_locale
|
||||||
|
|
||||||
@@ -28,12 +28,10 @@ def step_impl(context, file):
|
|||||||
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:
|
switch_locale(context.localedir, the_lang)
|
||||||
IfcFile.load(context.config.userdata.get("ifcfile"))
|
idm.provide_ifcfile_by_argument(context)
|
||||||
except:
|
|
||||||
assert False, f"The IFC {context.config.userdata.get('ifcfile')} file could not be loaded"
|
|
||||||
|
|
||||||
|
|
||||||
@given('A file path has been provided through an argument')
|
@given('A file path has been provided through an argument')
|
||||||
@@ -47,7 +45,7 @@ def step_impl(context):
|
|||||||
@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, the_lang)
|
switch_locale(context.localedir, the_lang)
|
||||||
assert_schema(context, schema)
|
idm.has_ifcdata_specific_schema(context, schema)
|
||||||
|
|
||||||
|
|
||||||
@step('The IFC file "{file}" is exempt from being provided')
|
@step('The IFC file "{file}" is exempt from being provided')
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
from behave import step
|
from behave import step
|
||||||
|
|
||||||
from ifcdata_methods import assert_schema
|
import ifcdata_methods as idm
|
||||||
from utils import switch_locale
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
the_lang = "de"
|
the_lang = "de"
|
||||||
|
|
||||||
|
|
||||||
@step("Die IFC Daten müssen das {schema} Schema benutzen")
|
@given("Die IFC-Datei wurde durch einen Startparameter zur Verfügung gestellt")
|
||||||
|
def step_impl(context):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
idm.provide_ifcfile_by_argument(context)
|
||||||
|
|
||||||
|
|
||||||
|
@step("Die IFC-Daten müssen das {schema} Schema benutzen")
|
||||||
def step_impl(context, schema):
|
def step_impl(context, schema):
|
||||||
switch_locale(context.localedir, the_lang)
|
switch_locale(context.localedir, the_lang)
|
||||||
assert_schema(context, schema)
|
idm.has_ifcdata_specific_schema(context, schema)
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
from behave import step
|
from behave import step
|
||||||
|
|
||||||
from ifcdata_methods import assert_schema
|
import ifcdata_methods as idm
|
||||||
from utils import switch_locale
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
the_lang = "fr"
|
the_lang = "fr"
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@given("The IFC file has been provided through an argument")
|
||||||
|
def step_impl(context):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
idm.provide_ifcfile_by_argument(context)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@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, the_lang)
|
switch_locale(context.localedir, the_lang)
|
||||||
assert_schema(context, schema)
|
idm.has_ifcdata_specific_schema(context, schema)
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
import ifcdata_methods as idm
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "it"
|
||||||
|
|
||||||
|
|
||||||
|
@given("Il file IFC è stato fornito attraverso un argumento")
|
||||||
|
def step_impl(context):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
idm.provide_ifcfile_by_argument(context)
|
||||||
|
|
||||||
|
|
||||||
|
@step("I dati IFC devono seguire lo schema {schema}")
|
||||||
|
def step_impl(context, schema):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
idm.has_ifcdata_specific_schema(context, schema)
|
||||||
@@ -1,7 +1,17 @@
|
|||||||
from utils import IfcFile
|
from utils import IfcFile
|
||||||
|
|
||||||
|
|
||||||
def assert_schema(context, target_schema):
|
def provide_ifcfile_by_argument(context):
|
||||||
|
try:
|
||||||
|
IfcFile.load(context.config.userdata.get("ifcfile"))
|
||||||
|
except:
|
||||||
|
assert False, (
|
||||||
|
_("The IFC {} file could not be loaded")
|
||||||
|
.format(context.config.userdata.get('ifcfile'))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def has_ifcdata_specific_schema(context, target_schema):
|
||||||
real_schema = IfcFile.get().schema
|
real_schema = IfcFile.get().schema
|
||||||
assert real_schema == target_schema, (
|
assert real_schema == target_schema, (
|
||||||
_("We expected a schema of {} but instead got {}")
|
_("We expected a schema of {} but instead got {}")
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
import ifcdata_methods as idm
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "nl"
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@given("The IFC file has been provided through an argument")
|
||||||
|
def step_impl(context):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
idm.provide_ifcfile_by_argument(context)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@step("IFC-gegevens moeten het {schema} -schema gebruiken")
|
||||||
|
def step_impl(context, schema):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
idm.has_ifcdata_specific_schema(context, schema)
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
from utils import assert_attribute
|
||||||
|
from utils import IfcFile
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "de"
|
||||||
|
|
||||||
|
|
||||||
|
@step("Die Globale Identifikationskennung (Globally Unique Identifier = GUID) des Projektes ist {guid}")
|
||||||
|
def step_impl(context, guid):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
assert_attribute(IfcFile.get().by_type("IfcProject")[0], "GlobalId", guid)
|
||||||
|
|
||||||
|
|
||||||
|
@step('Der Name, die Abkürzung oder die Kurzkennung des Projektes ist "{value}"')
|
||||||
|
def step_impl(context, value):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
assert_attribute(IfcFile.get().by_type("IfcProject")[0], "Name", value)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
from utils import assert_attribute
|
||||||
|
from utils import IfcFile
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "fr"
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@step("The project must have an identifier of {guid}")
|
||||||
|
def step_impl(context, guid):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
assert_attribute(IfcFile.get().by_type("IfcProject")[0], "GlobalId", guid)
|
||||||
|
|
||||||
|
|
||||||
|
@step('The project name, code, or short identifier must be "{value}"')
|
||||||
|
def step_impl(context, value):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
assert_attribute(IfcFile.get().by_type("IfcProject")[0], "Name", value)
|
||||||
|
"""
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
from utils import assert_attribute
|
||||||
|
from utils import IfcFile
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "it"
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@step("The project must have an identifier of {guid}")
|
||||||
|
def step_impl(context, guid):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
assert_attribute(IfcFile.get().by_type("IfcProject")[0], "GlobalId", guid)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@step('Il nome del progetto, codice o identificatore breve deve essere "{value}"')
|
||||||
|
def step_impl(context, value):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
assert_attribute(IfcFile.get().by_type("IfcProject")[0], "Name", value)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
from utils import assert_attribute
|
||||||
|
from utils import IfcFile
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
|
the_lang = "nl"
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TODO the next line needs translation
|
||||||
|
@step("The project must have an identifier of {guid}")
|
||||||
|
def step_impl(context, guid):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
assert_attribute(IfcFile.get().by_type("IfcProject")[0], "GlobalId", guid)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@step('De projectnaam, code of korte ID moet "{value}"')
|
||||||
|
def step_impl(context, value):
|
||||||
|
switch_locale(context.localedir, the_lang)
|
||||||
|
assert_attribute(IfcFile.get().by_type("IfcProject")[0], "Name", value)
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
import fileinput
|
||||||
|
|
||||||
|
|
||||||
|
def create_zoom_smartview(sm_file, ifcbasename):
|
||||||
|
|
||||||
|
smf = open(sm_file, "w")
|
||||||
|
smf.write('<?xml version="1.0"?>\n')
|
||||||
|
smf.write("<bimcollabsmartviewfile>\n")
|
||||||
|
smf.write(" <version>5</version>\n")
|
||||||
|
smf.write(" <applicationversion>Win - Version: ")
|
||||||
|
# next line belongs to last, because of line length
|
||||||
|
smf.write("3.4 (build 3.4.13.559)</applicationversion>\n")
|
||||||
|
smf.write("</bimcollabsmartviewfile>\n")
|
||||||
|
smf.write("\n")
|
||||||
|
smf.write("<SMARTVIEWSETS>\n")
|
||||||
|
smf.write(" <SMARTVIEWSET>\n")
|
||||||
|
smf.write(" <TITLE>BIMTester {}</TITLE>\n".format(ifcbasename))
|
||||||
|
smf.write(" <DESCRIPTION></DESCRIPTION>\n")
|
||||||
|
smf.write(" <GUID>a2ddfaf7-97f2-4519-aabd-f2d94f6b4d6b</GUID>\n")
|
||||||
|
smf.write(" <MODIFICATIONDATE>2020-10-30T13:23:30")
|
||||||
|
# next line belongs to last, because of line length
|
||||||
|
smf.write("</MODIFICATIONDATE>\n")
|
||||||
|
smf.write(" <SMARTVIEWS>\n")
|
||||||
|
smf.write(" </SMARTVIEWS>\n")
|
||||||
|
smf.write(" </SMARTVIEWSET>\n")
|
||||||
|
smf.write("</SMARTVIEWSETS>\n")
|
||||||
|
smf.close()
|
||||||
|
|
||||||
|
|
||||||
|
def append_zoom_smartview(sm_file, step_name, false_elements_guid):
|
||||||
|
|
||||||
|
# build the smartview string
|
||||||
|
smview_string = " <SMARTVIEW>\n"
|
||||||
|
smview_string += (
|
||||||
|
" <TITLE>GUID filter, {}</TITLE>\n"
|
||||||
|
.format(step_name)
|
||||||
|
)
|
||||||
|
smview_string += "{}\n".format(each_smartview_string_before)
|
||||||
|
for guid in false_elements_guid:
|
||||||
|
smview_string += (
|
||||||
|
"{}{}{}\n".format(
|
||||||
|
rule_string_before,
|
||||||
|
guid,
|
||||||
|
rule_string_after)
|
||||||
|
)
|
||||||
|
smview_string += "{}\n".format(each_smartview_string_after)
|
||||||
|
|
||||||
|
# insert smartview string into file
|
||||||
|
theline = " </SMARTVIEWS>"
|
||||||
|
newtext = smview_string + theline
|
||||||
|
for line in fileinput.FileInput(sm_file, inplace=True):
|
||||||
|
# the print replaces the line in the file
|
||||||
|
# and add the line afterwards
|
||||||
|
print(line.replace(theline, newtext), end="")
|
||||||
|
|
||||||
|
|
||||||
|
each_smartview_string_title = """ <SMARTVIEW>
|
||||||
|
<TITLE>Filter GUID</TITLE>
|
||||||
|
<DESCRIPTION></DESCRIPTION>"""
|
||||||
|
|
||||||
|
|
||||||
|
each_smartview_string_before = """ <CREATOR>bernd@bimstatik.ch</CREATOR>
|
||||||
|
<CREATIONDATE>2020-10-30T13:18:45</CREATIONDATE>
|
||||||
|
<MODIFIER>bernd@bimstatik.ch</MODIFIER>
|
||||||
|
<MODIFICATIONDATE>2020-10-30T13:23:30</MODIFICATIONDATE>
|
||||||
|
<GUID>15fda94f-b4bf-43be-8ef4-15d3121137e1</GUID>
|
||||||
|
<RULES>
|
||||||
|
<RULE>
|
||||||
|
<IFCTYPE>Any</IFCTYPE>
|
||||||
|
<PROPERTY>
|
||||||
|
<NAME>None</NAME>
|
||||||
|
<PROPERTYSETNAME>None</PROPERTYSETNAME>
|
||||||
|
<TYPE>None</TYPE>
|
||||||
|
<VALUETYPE>None</VALUETYPE>
|
||||||
|
<UNIT>None</UNIT>
|
||||||
|
</PROPERTY>
|
||||||
|
<CONDITION>
|
||||||
|
<TYPE>Is</TYPE>
|
||||||
|
<VALUE></VALUE>
|
||||||
|
</CONDITION>
|
||||||
|
<ACTION>
|
||||||
|
<TYPE>AddSetColored</TYPE>
|
||||||
|
<R>187</R>
|
||||||
|
<G>187</G>
|
||||||
|
<B>187</B>
|
||||||
|
</ACTION>
|
||||||
|
</RULE>
|
||||||
|
<RULE>
|
||||||
|
<IFCTYPE>Any</IFCTYPE>
|
||||||
|
<PROPERTY>
|
||||||
|
<NAME>None</NAME>
|
||||||
|
<PROPERTYSETNAME>None</PROPERTYSETNAME>
|
||||||
|
<TYPE>None</TYPE>
|
||||||
|
<VALUETYPE>None</VALUETYPE>
|
||||||
|
<UNIT>None</UNIT>
|
||||||
|
</PROPERTY>
|
||||||
|
<CONDITION>
|
||||||
|
<TYPE>Is</TYPE>
|
||||||
|
<VALUE></VALUE>
|
||||||
|
</CONDITION>
|
||||||
|
<ACTION>
|
||||||
|
<TYPE>SetTransparent</TYPE>
|
||||||
|
</ACTION>
|
||||||
|
</RULE>"""
|
||||||
|
|
||||||
|
|
||||||
|
each_smartview_string_after = """ </RULES>
|
||||||
|
<INFORMATIONTAKEOFF>
|
||||||
|
<PROPERTYSETNAME>None</PROPERTYSETNAME>
|
||||||
|
<PROPERTYNAME>None</PROPERTYNAME>
|
||||||
|
<OPERATION>0</OPERATION>
|
||||||
|
</INFORMATIONTAKEOFF>
|
||||||
|
</SMARTVIEW>"""
|
||||||
|
|
||||||
|
|
||||||
|
rule_string_before = """ <RULE>
|
||||||
|
<IFCTYPE>Any</IFCTYPE>
|
||||||
|
<PROPERTY>
|
||||||
|
<NAME>GUID</NAME>
|
||||||
|
<PROPERTYSETNAME>Summary</PROPERTYSETNAME>
|
||||||
|
<TYPE>Summary</TYPE>
|
||||||
|
<VALUETYPE>StringValue</VALUETYPE>
|
||||||
|
<UNIT>None</UNIT>
|
||||||
|
</PROPERTY>
|
||||||
|
<CONDITION>
|
||||||
|
<TYPE>Is</TYPE>
|
||||||
|
<VALUE>"""
|
||||||
|
|
||||||
|
|
||||||
|
rule_string_after = """</VALUE>
|
||||||
|
</CONDITION>
|
||||||
|
<ACTION>
|
||||||
|
<TYPE>SetColored</TYPE>
|
||||||
|
<R>255</R>
|
||||||
|
<G>10</G>
|
||||||
|
<B>10</B>
|
||||||
|
</ACTION>
|
||||||
|
</RULE>"""
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
# TODO: improve layout, start with feature file path and beside button !!!!!
|
# TODO: all args should be passed to the gui,
|
||||||
|
# either pass them further to behave or make it possible to edit them before
|
||||||
# TODO: if browse widgets will be canceled, last QLineEdit should be restored
|
# TODO: if browse widgets will be canceled, last QLineEdit should be restored
|
||||||
# TODO: keep path or file if in browse widget canceled
|
# TODO: keep path or file if in browse widget canceled
|
||||||
|
# TODO: make a frame around features direcory chooser and
|
||||||
|
# use features path from ifc button
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@@ -15,25 +18,32 @@ class GuiWidgetBimTester(QtWidgets.QWidget):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
features="",
|
featurespath="",
|
||||||
ifcfile=""
|
ifcfile="",
|
||||||
|
get_featurepath_from_ifcpath=False,
|
||||||
|
args=[]
|
||||||
):
|
):
|
||||||
super(GuiWidgetBimTester, self).__init__()
|
super(GuiWidgetBimTester, self).__init__()
|
||||||
|
|
||||||
# get features dir
|
|
||||||
user_path = os.path.expanduser("~")
|
user_path = os.path.expanduser("~")
|
||||||
# print(features)
|
|
||||||
self.initial_featurespath = features
|
# get features dir
|
||||||
if not os.path.isdir(self.initial_featurespath):
|
# print(featurespath)
|
||||||
self.initial_featurespath = user_path
|
if not os.path.isdir(featurespath):
|
||||||
print(self.initial_featurespath)
|
featurespath = user_path
|
||||||
|
|
||||||
# get ifc file
|
# get ifc file
|
||||||
# print(ifcfile)
|
# print(ifcfile)
|
||||||
|
if not os.path.isfile(ifcfile):
|
||||||
|
ifcfile = user_path
|
||||||
|
|
||||||
|
self.initial_featurespath = featurespath
|
||||||
self.initial_ifcfile = ifcfile
|
self.initial_ifcfile = ifcfile
|
||||||
if not os.path.isfile(self.initial_ifcfile):
|
self.get_featurepath_from_ifcpath = get_featurepath_from_ifcpath
|
||||||
self.initial_ifcfile = user_path
|
self.args = args
|
||||||
print(self.initial_ifcfile)
|
# print(self.initial_featurespath)
|
||||||
|
# print(self.initial_ifcfile)
|
||||||
|
# print(self.get_featurepath_from_ifcpath)
|
||||||
|
|
||||||
# init ui
|
# init ui
|
||||||
self._setup_ui()
|
self._setup_ui()
|
||||||
@@ -143,6 +153,9 @@ class GuiWidgetBimTester(QtWidgets.QWidget):
|
|||||||
layout.setRowStretch(0, 10)
|
layout.setRowStretch(0, 10)
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
# set button state, needs to be here after gui has beed set up
|
||||||
|
self.featuredirfromifc_cb.setChecked(self.get_featurepath_from_ifcpath)
|
||||||
|
|
||||||
# **********************************************************
|
# **********************************************************
|
||||||
def select_ifcfile(self):
|
def select_ifcfile(self):
|
||||||
# print(self.get_ifcfile())
|
# print(self.get_ifcfile())
|
||||||
@@ -162,11 +175,8 @@ class GuiWidgetBimTester(QtWidgets.QWidget):
|
|||||||
def featuredirfromifc_clicked(self):
|
def featuredirfromifc_clicked(self):
|
||||||
if self.featuredirfromifc_cb.isChecked() is True:
|
if self.featuredirfromifc_cb.isChecked() is True:
|
||||||
self.set_featurefilesdir("")
|
self.set_featurefilesdir("")
|
||||||
# TODO
|
|
||||||
self.featurefilesdir_text.setEnabled(False)
|
self.featurefilesdir_text.setEnabled(False)
|
||||||
self.feafilesdir_browse_btn.setEnabled(False)
|
self.feafilesdir_browse_btn.setEnabled(False)
|
||||||
# deactivate feature path browser button
|
|
||||||
# deactivate lineedit text
|
|
||||||
else:
|
else:
|
||||||
self.set_featurefilesdir(self.initial_featurespath)
|
self.set_featurefilesdir(self.initial_featurespath)
|
||||||
self.featurefilesdir_text.setEnabled(True)
|
self.featurefilesdir_text.setEnabled(True)
|
||||||
@@ -193,18 +203,42 @@ class GuiWidgetBimTester(QtWidgets.QWidget):
|
|||||||
|
|
||||||
# **********************************************************
|
# **********************************************************
|
||||||
def run_bimtester(self):
|
def run_bimtester(self):
|
||||||
print("Run BIMTester")
|
print("Run BIMTester by the GUI")
|
||||||
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
|
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
|
||||||
|
|
||||||
# get features dir
|
# get features dir
|
||||||
if self.featuredirfromifc_cb.isChecked() is True:
|
if self.featuredirfromifc_cb.isChecked() is True:
|
||||||
the_features_path = os.path.dirname(os.path.realpath(
|
ifcfile = self.get_ifcfile()
|
||||||
self.get_ifcfile()
|
|
||||||
))
|
|
||||||
print(
|
print(
|
||||||
"Make sure the feature files are beside "
|
"Make sure the feature files are beside "
|
||||||
"the ifc file in a directory named 'features'."
|
"the ifc file in a directory named 'features'."
|
||||||
)
|
)
|
||||||
|
if ifcfile == "":
|
||||||
|
# os.path.realpath("") would return the cmd dir and not ""
|
||||||
|
print(
|
||||||
|
"No ifcfile given, "
|
||||||
|
"thus features files path will be set to ''."
|
||||||
|
)
|
||||||
|
the_features_path = ""
|
||||||
|
elif os.path.isfile(ifcfile) is not True:
|
||||||
|
# if ifcfile does not exist set features path to ""
|
||||||
|
print(
|
||||||
|
"The ifcfile does not exist, "
|
||||||
|
"thus features files path will be set to ''."
|
||||||
|
)
|
||||||
|
the_features_path = ""
|
||||||
|
else:
|
||||||
|
ifcfilepath = os.path.dirname(os.path.realpath(ifcfile))
|
||||||
|
if os.path.isdir(ifcfilepath):
|
||||||
|
the_features_path = ifcfilepath
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
"ifcfilepath does not exist, "
|
||||||
|
"thus features files path will be set to ''."
|
||||||
|
"this shold never happen, please debug. "
|
||||||
|
)
|
||||||
|
the_features_path = ""
|
||||||
|
|
||||||
else:
|
else:
|
||||||
the_features_path = self.get_featurefilesdir()
|
the_features_path = self.get_featurefilesdir()
|
||||||
print(the_features_path)
|
print(the_features_path)
|
||||||
@@ -213,11 +247,13 @@ class GuiWidgetBimTester(QtWidgets.QWidget):
|
|||||||
the_ifcfile = self.get_ifcfile()
|
the_ifcfile = self.get_ifcfile()
|
||||||
print(the_ifcfile)
|
print(the_ifcfile)
|
||||||
|
|
||||||
|
# overwrite the_features_path and ifcfile in args
|
||||||
|
patched_args = self.args
|
||||||
|
patched_args["featuresdir"] = the_features_path
|
||||||
|
patched_args["ifcfile"] = the_ifcfile
|
||||||
|
|
||||||
# run bimtester
|
# run bimtester
|
||||||
status = run_all(
|
status = run_all(patched_args)
|
||||||
the_features_path,
|
|
||||||
the_ifcfile,
|
|
||||||
)
|
|
||||||
print(status)
|
print(status)
|
||||||
|
|
||||||
QtWidgets.QApplication.restoreOverrideCursor()
|
QtWidgets.QApplication.restoreOverrideCursor()
|
||||||
|
|||||||
@@ -7,150 +7,158 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PROJECT VERSION\n"
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
"POT-Creation-Date: 2020-12-23 11:03+0100\n"
|
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||||
"PO-Revision-Date: 2020-12-23 11:20+0100\n"
|
"PO-Revision-Date: 2020-12-23 11:20+0100\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
"Language-Team: de <LL@li.org>\n"
|
"Language-Team: de <LL@li.org>\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=utf-8\n"
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: Babel 2.6.0\n"
|
"Generated-By: Babel 2.6.0\n"
|
||||||
"X-Generator: Poedit 2.2.1\n"
|
|
||||||
|
|
||||||
#: reports.py:153 reports.py:160
|
#: reports.py:158
|
||||||
msgid "OpenBIM auditing is a feature of"
|
|
||||||
msgstr "OpenBIM auditing ist eine Funktionalität von"
|
|
||||||
|
|
||||||
#: reports.py:155
|
|
||||||
msgid "en"
|
msgid "en"
|
||||||
msgstr "de"
|
msgstr "de"
|
||||||
|
|
||||||
#: reports.py:156
|
#: reports.py:159
|
||||||
msgid "Success"
|
msgid "Success"
|
||||||
msgstr "Bestanden"
|
msgstr "Bestanden"
|
||||||
|
|
||||||
#: reports.py:157
|
#: reports.py:160
|
||||||
msgid "Failure"
|
msgid "Failure"
|
||||||
msgstr "Durchgefallen"
|
msgstr "Durchgefallen"
|
||||||
|
|
||||||
#: reports.py:158
|
#: reports.py:161
|
||||||
msgid "Tests passed"
|
msgid "Tests passed"
|
||||||
msgstr "Erfolgreiche Tests"
|
msgstr "Erfolgreiche Tests"
|
||||||
|
|
||||||
#: reports.py:159
|
#: reports.py:162
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr "Dauer"
|
msgstr "Dauer"
|
||||||
|
|
||||||
#: reports.py:161
|
#: reports.py:163
|
||||||
|
msgid "OpenBIM auditing is a feature of"
|
||||||
|
msgstr "OpenBIM auditing ist eine Funktionalität von"
|
||||||
|
|
||||||
|
#: reports.py:164
|
||||||
msgid "and"
|
msgid "and"
|
||||||
msgstr "und"
|
msgstr "und"
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:24
|
#: features/steps/attributes_eleclasses_methods.py:26
|
||||||
msgid "All {elemcount} elements in the file are {ifc_class}."
|
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:32
|
#: features/steps/attributes_eleclasses_methods.py:34
|
||||||
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:41 features/steps/utils.py:158
|
#: features/steps/attributes_eleclasses_methods.py:43
|
||||||
|
#: features/steps/utils.py:158
|
||||||
msgid "Error in falsecount, something went wrong."
|
msgid "Error in falsecount, something went wrong."
|
||||||
msgstr "Fehler in falsecount, es ist etwas falsch gelaufen."
|
msgstr "Fehler in falsecount, es ist etwas falsch gelaufen."
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:80
|
#: features/steps/attributes_eleclasses_methods.py:84
|
||||||
msgid ""
|
msgid ""
|
||||||
"For all {elemcount} {ifc_class} elements at least one of these class attributes "
|
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||||
"{parameter} has no value."
|
"attributes {parameter} has no value."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:81
|
#: features/steps/attributes_eleclasses_methods.py:85
|
||||||
msgid ""
|
msgid ""
|
||||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at least "
|
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||||
"one of these class attributes {parameter} has no value: {falseelems}"
|
" least one of these class attributes {parameter} has no value: "
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:82
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:108
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:133
|
|
||||||
#: features/steps/attributes_psets_methods.py:31
|
|
||||||
#: features/steps/geometric_detail_methods.py:52
|
|
||||||
#: features/steps/geometric_detail_methods.py:127
|
|
||||||
msgid "There are no {ifc_class} elements in the IFC file."
|
|
||||||
msgstr "Es sind keine {ifc_class} Bauteile in der IFC-Datei."
|
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:106
|
|
||||||
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:107
|
|
||||||
msgid ""
|
|
||||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not set: "
|
|
||||||
"{falseelems}"
|
"{falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:131
|
#: features/steps/attributes_eleclasses_methods.py:86
|
||||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
#: features/steps/attributes_eleclasses_methods.py:112
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:139
|
||||||
|
#: features/steps/attributes_psets_methods.py:33
|
||||||
|
#: features/steps/geometric_detail_methods.py:56
|
||||||
|
#: features/steps/geometric_detail_methods.py:134
|
||||||
|
msgid "There are no {ifc_class} elements in the IFC file."
|
||||||
|
msgstr "Es sind keine {ifc_class} Bauteile in der IFC-Datei."
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:110
|
||||||
|
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:132
|
#: features/steps/attributes_eleclasses_methods.py:111
|
||||||
msgid ""
|
msgid ""
|
||||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||||
"set: {falseelems}"
|
"set: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_psets_methods.py:29
|
#: features/steps/attributes_eleclasses_methods.py:137
|
||||||
msgid ""
|
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||||
"All {elemcount} {ifc_class} elements are missing the property {parameter} in "
|
|
||||||
"the pset."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_psets_methods.py:30
|
#: features/steps/attributes_eleclasses_methods.py:138
|
||||||
msgid ""
|
msgid ""
|
||||||
"The following {falsecount} of {elemcount} {ifc_class} elements are missing the "
|
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||||
"property {parameter} in the pset: {falseelems}"
|
"is not set: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:50
|
#: features/steps/attributes_psets_methods.py:31
|
||||||
|
msgid ""
|
||||||
|
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||||
|
" in the pset."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: features/steps/attributes_psets_methods.py:32
|
||||||
|
msgid ""
|
||||||
|
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||||
|
"missing the property {parameter} in the pset: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: features/steps/geometric_detail_methods.py:54
|
||||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Alle {elemcount} {ifc_class} Bauteile haben keine geometrische Repräsentation "
|
"Alle {elemcount} {ifc_class} Bauteile haben keine geometrische "
|
||||||
"der Klasse {parameter}."
|
"Repräsentation der Klasse {parameter}."
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:51
|
#: features/steps/geometric_detail_methods.py:55
|
||||||
msgid ""
|
msgid ""
|
||||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||||
"{parameter} representation: {falseelems}"
|
"{parameter} representation: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Die Anzahl Bauteile {falsecount} von allen {elemcount} {ifc_class} Bauteilen "
|
"Die Anzahl Bauteile {falsecount} von allen {elemcount} {ifc_class} "
|
||||||
"haben keine geometrische Repräsentation der Klasse {parameter}: {falseelems}"
|
"Bauteilen haben keine geometrische Repräsentation der Klasse {parameter}:"
|
||||||
|
" {falseelems}"
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:125
|
#: features/steps/geometric_detail_methods.py:132
|
||||||
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Die geometrischen Repräsentationen von allen {elemcount} {ifc_class} Bauteilen "
|
"Die geometrischen Repräsentationen von allen {elemcount} {ifc_class} "
|
||||||
"haben Fehler."
|
"Bauteilen haben Fehler."
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:126
|
#: features/steps/geometric_detail_methods.py:133
|
||||||
msgid ""
|
msgid ""
|
||||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements have "
|
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||||
"errors: {falseelems}"
|
"have errors: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Die geometrischen Repräsentationen von {falsecount} von allen {elemcount} "
|
"Die geometrischen Repräsentationen von {falsecount} von allen {elemcount}"
|
||||||
"{ifc_class} Bauteilen haben Fehler: {falseelems}"
|
" {ifc_class} Bauteilen haben Fehler: {falseelems}"
|
||||||
|
|
||||||
#: features/steps/ifcdata_methods.py:7
|
#: features/steps/ifcdata_methods.py:9
|
||||||
|
msgid "The IFC {} file could not be loaded"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:17
|
||||||
msgid "We expected a schema of {} but instead got {}"
|
msgid "We expected a schema of {} but instead got {}"
|
||||||
msgstr "Wir haben das Schema {} erwartet, aber die Daten nutzen das Schema {}"
|
msgstr "Wir haben das Schema {} erwartet, aber die Daten nutzen das Schema {}"
|
||||||
|
|
||||||
#~ msgid "The geometry of all {} {} elements have errors."
|
#~ msgid "The geometry of all {} {} elements have errors."
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Die geometrischen Repräsentationen von allen {} {} Bauteilen haben Fehler."
|
#~ "Die geometrischen Repräsentationen von allen"
|
||||||
|
#~ " {} {} Bauteilen haben Fehler."
|
||||||
|
|
||||||
#~ msgid "The geometry of {} out of all {} {} elements have errors: {}"
|
#~ msgid "The geometry of {} out of all {} {} elements have errors: {}"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Die geometrischen Repräsentationen von {} von allen {} {} Bauteilen haben "
|
#~ "Die geometrischen Repräsentationen von {} "
|
||||||
|
#~ "von allen {} {} Bauteilen haben "
|
||||||
#~ "Fehler: {}"
|
#~ "Fehler: {}"
|
||||||
|
|
||||||
#~ msgid "There are no {} elements in the IFC file."
|
#~ msgid "There are no {} elements in the IFC file."
|
||||||
@@ -158,26 +166,35 @@ msgstr "Wir haben das Schema {} erwartet, aber die Daten nutzen das Schema {}"
|
|||||||
|
|
||||||
#~ msgid "All {} {} elements are not a IfcFacetedBrep representation."
|
#~ msgid "All {} {} elements are not a IfcFacetedBrep representation."
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Alle {} {} Bauteile haben keine geometrische Repräsentation der Klasse "
|
#~ "Alle {} {} Bauteile haben keine "
|
||||||
|
#~ "geometrische Repräsentation der Klasse "
|
||||||
#~ "IfcFacetedBrep."
|
#~ "IfcFacetedBrep."
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ "The following {} of {} {} elements are not a IfcFacetedBrep representation: "
|
#~ "The following {} of {} {} elements"
|
||||||
|
#~ " are not a IfcFacetedBrep representation:"
|
||||||
|
#~ " {}"
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "Die Anzahl Bauteile {} von allen "
|
||||||
|
#~ "{} {} Bauteilen haben keine geometrische"
|
||||||
|
#~ " Repräsentation der Klasse IfcFacetedBrep: "
|
||||||
#~ "{}"
|
#~ "{}"
|
||||||
#~ msgstr ""
|
|
||||||
#~ "Die Anzahl Bauteile {} von allen {} {} Bauteilen haben keine geometrische "
|
|
||||||
#~ "Repräsentation der Klasse IfcFacetedBrep: {}"
|
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ "All {elemcount} {ifc_class} elements are not a IfcFacetedBrep representation."
|
#~ "All {elemcount} {ifc_class} elements are "
|
||||||
|
#~ "not a IfcFacetedBrep representation."
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Alle {elemcount} {ifc_class} Bauteile haben keine geometrische "
|
#~ "Alle {elemcount} {ifc_class} Bauteile haben"
|
||||||
#~ "Repräsentation der Klasse IfcFacetedBrep."
|
#~ " keine geometrische Repräsentation der "
|
||||||
|
#~ "Klasse IfcFacetedBrep."
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ "The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
#~ "The following {falsecount} of {elemcount} "
|
||||||
|
#~ "{ifc_class} elements are not a "
|
||||||
#~ "IfcFacetedBrep representation: {falseelems}"
|
#~ "IfcFacetedBrep representation: {falseelems}"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Die Anzahl Bauteile {falsecount} von allen {elemcount} {ifc_class} Bauteilen "
|
#~ "Die Anzahl Bauteile {falsecount} von "
|
||||||
#~ "haben keine geometrische Repräsentation der Klasse IfcFacetedBrep: "
|
#~ "allen {elemcount} {ifc_class} Bauteilen haben"
|
||||||
#~ "{falseelems}"
|
#~ " keine geometrische Repräsentation der "
|
||||||
|
#~ "Klasse IfcFacetedBrep: {falseelems}"
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PROJECT VERSION\n"
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
"POT-Creation-Date: 2020-12-23 11:03+0100\n"
|
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||||
"PO-Revision-Date: 2020-12-18 11:41+0100\n"
|
"PO-Revision-Date: 2020-12-18 11:41+0100\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language: en\n"
|
"Language: en\n"
|
||||||
@@ -18,122 +18,126 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: Babel 2.6.0\n"
|
"Generated-By: Babel 2.6.0\n"
|
||||||
|
|
||||||
#: reports.py:153 reports.py:160
|
#: reports.py:158
|
||||||
msgid "OpenBIM auditing is a feature of"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: reports.py:155
|
|
||||||
msgid "en"
|
msgid "en"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: reports.py:156
|
#: reports.py:159
|
||||||
msgid "Success"
|
msgid "Success"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: reports.py:157
|
#: reports.py:160
|
||||||
msgid "Failure"
|
msgid "Failure"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: reports.py:158
|
#: reports.py:161
|
||||||
msgid "Tests passed"
|
msgid "Tests passed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: reports.py:159
|
#: reports.py:162
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: reports.py:161
|
#: reports.py:163
|
||||||
|
msgid "OpenBIM auditing is a feature of"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: reports.py:164
|
||||||
msgid "and"
|
msgid "and"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:24
|
#: features/steps/attributes_eleclasses_methods.py:26
|
||||||
msgid "All {elemcount} elements in the file are {ifc_class}."
|
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:32
|
#: features/steps/attributes_eleclasses_methods.py:34
|
||||||
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:41
|
#: features/steps/attributes_eleclasses_methods.py:43
|
||||||
#: features/steps/utils.py:158
|
#: features/steps/utils.py:158
|
||||||
msgid "Error in falsecount, something went wrong."
|
msgid "Error in falsecount, something went wrong."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:80
|
#: features/steps/attributes_eleclasses_methods.py:84
|
||||||
msgid ""
|
msgid ""
|
||||||
"For all {elemcount} {ifc_class} elements at least one of these class "
|
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||||
"attributes {parameter} has no value."
|
"attributes {parameter} has no value."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:81
|
#: features/steps/attributes_eleclasses_methods.py:85
|
||||||
msgid ""
|
msgid ""
|
||||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||||
" least one of these class attributes {parameter} has no value: "
|
" least one of these class attributes {parameter} has no value: "
|
||||||
"{falseelems}"
|
"{falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:82
|
#: features/steps/attributes_eleclasses_methods.py:86
|
||||||
#: features/steps/attributes_eleclasses_methods.py:108
|
#: features/steps/attributes_eleclasses_methods.py:112
|
||||||
#: features/steps/attributes_eleclasses_methods.py:133
|
#: features/steps/attributes_eleclasses_methods.py:139
|
||||||
#: features/steps/attributes_psets_methods.py:31
|
#: features/steps/attributes_psets_methods.py:33
|
||||||
#: features/steps/geometric_detail_methods.py:52
|
#: features/steps/geometric_detail_methods.py:56
|
||||||
#: features/steps/geometric_detail_methods.py:127
|
#: features/steps/geometric_detail_methods.py:134
|
||||||
msgid "There are no {ifc_class} elements in the IFC file."
|
msgid "There are no {ifc_class} elements in the IFC file."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:106
|
#: features/steps/attributes_eleclasses_methods.py:110
|
||||||
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:107
|
#: features/steps/attributes_eleclasses_methods.py:111
|
||||||
msgid ""
|
msgid ""
|
||||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||||
"set: {falseelems}"
|
"set: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:131
|
#: features/steps/attributes_eleclasses_methods.py:137
|
||||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:132
|
#: features/steps/attributes_eleclasses_methods.py:138
|
||||||
msgid ""
|
msgid ""
|
||||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||||
"is not set: {falseelems}"
|
"is not set: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_psets_methods.py:29
|
#: features/steps/attributes_psets_methods.py:31
|
||||||
msgid ""
|
msgid ""
|
||||||
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||||
" in the pset."
|
" in the pset."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_psets_methods.py:30
|
#: features/steps/attributes_psets_methods.py:32
|
||||||
msgid ""
|
msgid ""
|
||||||
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||||
"missing the property {parameter} in the pset: {falseelems}"
|
"missing the property {parameter} in the pset: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:50
|
#: features/steps/geometric_detail_methods.py:54
|
||||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:51
|
#: features/steps/geometric_detail_methods.py:55
|
||||||
msgid ""
|
msgid ""
|
||||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||||
"{parameter} representation: {falseelems}"
|
"{parameter} representation: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:125
|
#: features/steps/geometric_detail_methods.py:132
|
||||||
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:126
|
#: features/steps/geometric_detail_methods.py:133
|
||||||
msgid ""
|
msgid ""
|
||||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||||
"have errors: {falseelems}"
|
"have errors: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/ifcdata_methods.py:7
|
#: features/steps/ifcdata_methods.py:9
|
||||||
|
msgid "The IFC {} file could not be loaded"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:17
|
||||||
msgid "We expected a schema of {} but instead got {}"
|
msgid "We expected a schema of {} but instead got {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -7,131 +7,137 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PROJECT VERSION\n"
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
"POT-Creation-Date: 2020-12-23 11:03+0100\n"
|
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||||
"PO-Revision-Date: 2020-12-23 11:21+0100\n"
|
"PO-Revision-Date: 2020-12-23 11:21+0100\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
"Language-Team: fr <LL@li.org>\n"
|
"Language-Team: fr <LL@li.org>\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=utf-8\n"
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: Babel 2.6.0\n"
|
"Generated-By: Babel 2.6.0\n"
|
||||||
"X-Generator: Poedit 2.2.1\n"
|
|
||||||
|
|
||||||
#: reports.py:153 reports.py:160
|
#: reports.py:158
|
||||||
msgid "OpenBIM auditing is a feature of"
|
|
||||||
msgstr "L'audit OpenBIM auditing est une fonctionnalité de"
|
|
||||||
|
|
||||||
#: reports.py:155
|
|
||||||
msgid "en"
|
msgid "en"
|
||||||
msgstr "fr"
|
msgstr "fr"
|
||||||
|
|
||||||
#: reports.py:156
|
#: reports.py:159
|
||||||
msgid "Success"
|
msgid "Success"
|
||||||
msgstr "Succès"
|
msgstr "Succès"
|
||||||
|
|
||||||
#: reports.py:157
|
#: reports.py:160
|
||||||
msgid "Failure"
|
msgid "Failure"
|
||||||
msgstr "Échec"
|
msgstr "Échec"
|
||||||
|
|
||||||
#: reports.py:158
|
#: reports.py:161
|
||||||
msgid "Tests passed"
|
msgid "Tests passed"
|
||||||
msgstr "Tests réussis"
|
msgstr "Tests réussis"
|
||||||
|
|
||||||
#: reports.py:159
|
#: reports.py:162
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr "Durée"
|
msgstr "Durée"
|
||||||
|
|
||||||
#: reports.py:161
|
#: reports.py:163
|
||||||
|
msgid "OpenBIM auditing is a feature of"
|
||||||
|
msgstr "L'audit OpenBIM auditing est une fonctionnalité de"
|
||||||
|
|
||||||
|
#: reports.py:164
|
||||||
msgid "and"
|
msgid "and"
|
||||||
msgstr "et"
|
msgstr "et"
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:24
|
#: features/steps/attributes_eleclasses_methods.py:26
|
||||||
msgid "All {elemcount} elements in the file are {ifc_class}."
|
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:32
|
#: features/steps/attributes_eleclasses_methods.py:34
|
||||||
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:41 features/steps/utils.py:158
|
#: features/steps/attributes_eleclasses_methods.py:43
|
||||||
|
#: features/steps/utils.py:158
|
||||||
msgid "Error in falsecount, something went wrong."
|
msgid "Error in falsecount, something went wrong."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:80
|
#: features/steps/attributes_eleclasses_methods.py:84
|
||||||
msgid ""
|
msgid ""
|
||||||
"For all {elemcount} {ifc_class} elements at least one of these class attributes "
|
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||||
"{parameter} has no value."
|
"attributes {parameter} has no value."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:81
|
#: features/steps/attributes_eleclasses_methods.py:85
|
||||||
msgid ""
|
msgid ""
|
||||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at least "
|
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||||
"one of these class attributes {parameter} has no value: {falseelems}"
|
" least one of these class attributes {parameter} has no value: "
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:82
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:108
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:133
|
|
||||||
#: features/steps/attributes_psets_methods.py:31
|
|
||||||
#: features/steps/geometric_detail_methods.py:52
|
|
||||||
#: features/steps/geometric_detail_methods.py:127
|
|
||||||
msgid "There are no {ifc_class} elements in the IFC file."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:106
|
|
||||||
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:107
|
|
||||||
msgid ""
|
|
||||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not set: "
|
|
||||||
"{falseelems}"
|
"{falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:131
|
#: features/steps/attributes_eleclasses_methods.py:86
|
||||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
#: features/steps/attributes_eleclasses_methods.py:112
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:139
|
||||||
|
#: features/steps/attributes_psets_methods.py:33
|
||||||
|
#: features/steps/geometric_detail_methods.py:56
|
||||||
|
#: features/steps/geometric_detail_methods.py:134
|
||||||
|
msgid "There are no {ifc_class} elements in the IFC file."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:132
|
#: features/steps/attributes_eleclasses_methods.py:110
|
||||||
|
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:111
|
||||||
msgid ""
|
msgid ""
|
||||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||||
"set: {falseelems}"
|
"set: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_psets_methods.py:29
|
#: features/steps/attributes_eleclasses_methods.py:137
|
||||||
msgid ""
|
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||||
"All {elemcount} {ifc_class} elements are missing the property {parameter} in "
|
|
||||||
"the pset."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_psets_methods.py:30
|
#: features/steps/attributes_eleclasses_methods.py:138
|
||||||
msgid ""
|
msgid ""
|
||||||
"The following {falsecount} of {elemcount} {ifc_class} elements are missing the "
|
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||||
"property {parameter} in the pset: {falseelems}"
|
"is not set: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:50
|
#: features/steps/attributes_psets_methods.py:31
|
||||||
|
msgid ""
|
||||||
|
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||||
|
" in the pset."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: features/steps/attributes_psets_methods.py:32
|
||||||
|
msgid ""
|
||||||
|
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||||
|
"missing the property {parameter} in the pset: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: features/steps/geometric_detail_methods.py:54
|
||||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:51
|
#: features/steps/geometric_detail_methods.py:55
|
||||||
msgid ""
|
msgid ""
|
||||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||||
"{parameter} representation: {falseelems}"
|
"{parameter} representation: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:125
|
#: features/steps/geometric_detail_methods.py:132
|
||||||
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:126
|
#: features/steps/geometric_detail_methods.py:133
|
||||||
msgid ""
|
msgid ""
|
||||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements have "
|
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||||
"errors: {falseelems}"
|
"have errors: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/ifcdata_methods.py:7
|
#: features/steps/ifcdata_methods.py:9
|
||||||
|
msgid "The IFC {} file could not be loaded"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:17
|
||||||
msgid "We expected a schema of {} but instead got {}"
|
msgid "We expected a schema of {} but instead got {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
# Italian translations for PROJECT.
|
||||||
|
# Copyright (C) 2020 ORGANIZATION
|
||||||
|
# This file is distributed under the same license as the PROJECT project.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
|
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||||
|
"PO-Revision-Date: 2020-12-18 11:41+0100\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language: it\n"
|
||||||
|
"Language-Team: it <LL@li.org>\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: Babel 2.6.0\n"
|
||||||
|
|
||||||
|
#: reports.py:158
|
||||||
|
msgid "en"
|
||||||
|
msgstr "it"
|
||||||
|
|
||||||
|
#: reports.py:159
|
||||||
|
msgid "Success"
|
||||||
|
msgstr "Successo"
|
||||||
|
|
||||||
|
#: reports.py:160
|
||||||
|
msgid "Failure"
|
||||||
|
msgstr "Errore"
|
||||||
|
|
||||||
|
#: reports.py:161
|
||||||
|
msgid "Tests passed"
|
||||||
|
msgstr "Test superati"
|
||||||
|
|
||||||
|
#: reports.py:162
|
||||||
|
msgid "Duration"
|
||||||
|
msgstr "Durata"
|
||||||
|
|
||||||
|
#: reports.py:163
|
||||||
|
msgid "OpenBIM auditing is a feature of"
|
||||||
|
msgstr "L'auditing OpenBIM è una caratteristica di"
|
||||||
|
|
||||||
|
#: reports.py:164
|
||||||
|
msgid "and"
|
||||||
|
msgstr "e"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:26
|
||||||
|
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||||
|
msgstr "Tutti {elemcount} elementi nel file sono classificati {ifc_class}"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:34
|
||||||
|
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"{falsecount} di {elemcount} elementi sono elementi {ifc_class}: "
|
||||||
|
"{falseelems}"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:43
|
||||||
|
#: features/steps/utils.py:158
|
||||||
|
msgid "Error in falsecount, something went wrong."
|
||||||
|
msgstr "Errore in falsecount, qualcosa è andato storto."
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:84
|
||||||
|
msgid ""
|
||||||
|
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||||
|
"attributes {parameter} has no value."
|
||||||
|
msgstr ""
|
||||||
|
"Per tutti {elemcount} elementi {ifc_class} almeno uno di questa "
|
||||||
|
"classedegli attributi {parameter} non contiene alcun valore"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:85
|
||||||
|
msgid ""
|
||||||
|
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||||
|
" least one of these class attributes {parameter} has no value: "
|
||||||
|
"{falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"Per i seguenti {falsecount} di {elemcount} elementi {ifc_class}almeno uno"
|
||||||
|
" degli attributi {parameter} non contiene alcun valore"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:86
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:112
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:139
|
||||||
|
#: features/steps/attributes_psets_methods.py:33
|
||||||
|
#: features/steps/geometric_detail_methods.py:56
|
||||||
|
#: features/steps/geometric_detail_methods.py:134
|
||||||
|
msgid "There are no {ifc_class} elements in the IFC file."
|
||||||
|
msgstr "Il file IFC non contiene elementi della classe {ifc_class}."
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:110
|
||||||
|
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||||
|
msgstr "Il nome di {elemcount} {elemcount} elementi non è impostato"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:111
|
||||||
|
msgid ""
|
||||||
|
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||||
|
"set: {falseelems}"
|
||||||
|
msgstr "Il nome di {falsecount} su {elemcount} elementi {ifc_class} non èimpostato"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:137
|
||||||
|
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||||
|
msgstr "La descrizione di tutti {elemcount} {elemcount} elementi non èimpostato "
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:138
|
||||||
|
msgid ""
|
||||||
|
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||||
|
"is not set: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"la descrizione di {falsecount} su {elemcount} elementi {ifc_class} non "
|
||||||
|
"èimpostato"
|
||||||
|
|
||||||
|
#: features/steps/attributes_psets_methods.py:31
|
||||||
|
msgid ""
|
||||||
|
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||||
|
" in the pset."
|
||||||
|
msgstr ""
|
||||||
|
"A {elemcount} gli elementi {ifc_class} manca la proprietà "
|
||||||
|
"{parameter}nello pset."
|
||||||
|
|
||||||
|
#: features/steps/attributes_psets_methods.py:32
|
||||||
|
msgid ""
|
||||||
|
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||||
|
"missing the property {parameter} in the pset: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"Ai seguenti {falsecount} di {elemcount} elementi {ifc_class} manca la "
|
||||||
|
"proprietà{parameter} nello pset: {falseelems}"
|
||||||
|
|
||||||
|
#: features/steps/geometric_detail_methods.py:54
|
||||||
|
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||||
|
msgstr "Tutti {elemcount} elementi {ifc_class} non rappresentano {parameter}"
|
||||||
|
|
||||||
|
#: features/steps/geometric_detail_methods.py:55
|
||||||
|
msgid ""
|
||||||
|
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||||
|
"{parameter} representation: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"I seguenti {falsecount} di {elemcount} elementi {ifc_class} non "
|
||||||
|
"rappresentano{parameter}: {falseelems}"
|
||||||
|
|
||||||
|
#: features/steps/geometric_detail_methods.py:132
|
||||||
|
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||||
|
msgstr "La geometria di tutti {elemcount} elementi {ifc_class} contiene errori"
|
||||||
|
|
||||||
|
#: features/steps/geometric_detail_methods.py:133
|
||||||
|
msgid ""
|
||||||
|
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||||
|
"have errors: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"La geometria di {falsecount} su {elemcount} elementi {ifc_class} "
|
||||||
|
"contieneerrori: {falseelems}"
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:9
|
||||||
|
msgid "The IFC {} file could not be loaded"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:17
|
||||||
|
msgid "We expected a schema of {} but instead got {}"
|
||||||
|
msgstr "Ci aspettavamo uno schema di {} ma invece abbiamo ottenuto {}"
|
||||||
|
|
||||||
|
#~ msgid "The geometry of all {} {} elements have errors."
|
||||||
|
#~ msgstr "La geometria di tutti gli elementi {} {} contiene errori."
|
||||||
|
|
||||||
|
#~ msgid "The geometry of {} out of all {} {} elements have errors: {}"
|
||||||
|
#~ msgstr "La geometria di {} su {} {} elementi contiene errori."
|
||||||
|
|
||||||
|
#~ msgid "There are no {} elements in the IFC file."
|
||||||
|
#~ msgstr "Non ci sono {} elementi nel file IFC."
|
||||||
|
|
||||||
|
#~ msgid "All {} {} elements are not a IfcFacetedBrep representation."
|
||||||
|
#~ msgstr "Tutti gli elementi {} {} non sono una rappresentazioneIfcFacetedBrep."
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "The following {} of {} {} elements"
|
||||||
|
#~ " are not a IfcFacetedBrep representation:"
|
||||||
|
#~ " {}"
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "I seguenti {} di {} {} elementi"
|
||||||
|
#~ " non sono una rappresentazioneIfcFacetedBrep. "
|
||||||
|
#~ "{}"
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "All {elemcount} {ifc_class} elements are "
|
||||||
|
#~ "not a IfcFacetedBrep representation."
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "Tutti {elemcount} elementi {ifc_class} non"
|
||||||
|
#~ " sono una rappresentazioneIfcFacetedBrep."
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "The following {falsecount} of {elemcount} "
|
||||||
|
#~ "{ifc_class} elements are not a "
|
||||||
|
#~ "IfcFacetedBrep representation: {falseelems}"
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "I seguenti {falsecount} su {elemcount} "
|
||||||
|
#~ "elementi {ifc_class} non sonouna "
|
||||||
|
#~ "rappresentazione IfcFacetedBrep."
|
||||||
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
# Translations template for PROJECT.
|
# Translations template for PROJECT.
|
||||||
# Copyright (C) 2020 ORGANIZATION
|
# Copyright (C) 2021 ORGANIZATION
|
||||||
# This file is distributed under the same license as the PROJECT project.
|
# This file is distributed under the same license as the PROJECT project.
|
||||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2021.
|
||||||
#
|
#
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PROJECT VERSION\n"
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
"POT-Creation-Date: 2020-12-23 11:03+0100\n"
|
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -17,122 +17,126 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: Babel 2.6.0\n"
|
"Generated-By: Babel 2.6.0\n"
|
||||||
|
|
||||||
#: reports.py:153 reports.py:160
|
#: reports.py:158
|
||||||
msgid "OpenBIM auditing is a feature of"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: reports.py:155
|
|
||||||
msgid "en"
|
msgid "en"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: reports.py:156
|
#: reports.py:159
|
||||||
msgid "Success"
|
msgid "Success"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: reports.py:157
|
#: reports.py:160
|
||||||
msgid "Failure"
|
msgid "Failure"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: reports.py:158
|
#: reports.py:161
|
||||||
msgid "Tests passed"
|
msgid "Tests passed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: reports.py:159
|
#: reports.py:162
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: reports.py:161
|
#: reports.py:163
|
||||||
|
msgid "OpenBIM auditing is a feature of"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: reports.py:164
|
||||||
msgid "and"
|
msgid "and"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:24
|
#: features/steps/attributes_eleclasses_methods.py:26
|
||||||
msgid "All {elemcount} elements in the file are {ifc_class}."
|
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:32
|
#: features/steps/attributes_eleclasses_methods.py:34
|
||||||
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:41
|
#: features/steps/attributes_eleclasses_methods.py:43
|
||||||
#: features/steps/utils.py:158
|
#: features/steps/utils.py:158
|
||||||
msgid "Error in falsecount, something went wrong."
|
msgid "Error in falsecount, something went wrong."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:80
|
#: features/steps/attributes_eleclasses_methods.py:84
|
||||||
msgid ""
|
msgid ""
|
||||||
"For all {elemcount} {ifc_class} elements at least one of these class "
|
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||||
"attributes {parameter} has no value."
|
"attributes {parameter} has no value."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:81
|
#: features/steps/attributes_eleclasses_methods.py:85
|
||||||
msgid ""
|
msgid ""
|
||||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||||
" least one of these class attributes {parameter} has no value: "
|
" least one of these class attributes {parameter} has no value: "
|
||||||
"{falseelems}"
|
"{falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:82
|
#: features/steps/attributes_eleclasses_methods.py:86
|
||||||
#: features/steps/attributes_eleclasses_methods.py:108
|
#: features/steps/attributes_eleclasses_methods.py:112
|
||||||
#: features/steps/attributes_eleclasses_methods.py:133
|
#: features/steps/attributes_eleclasses_methods.py:139
|
||||||
#: features/steps/attributes_psets_methods.py:31
|
#: features/steps/attributes_psets_methods.py:33
|
||||||
#: features/steps/geometric_detail_methods.py:52
|
#: features/steps/geometric_detail_methods.py:56
|
||||||
#: features/steps/geometric_detail_methods.py:127
|
#: features/steps/geometric_detail_methods.py:134
|
||||||
msgid "There are no {ifc_class} elements in the IFC file."
|
msgid "There are no {ifc_class} elements in the IFC file."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:106
|
#: features/steps/attributes_eleclasses_methods.py:110
|
||||||
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:107
|
#: features/steps/attributes_eleclasses_methods.py:111
|
||||||
msgid ""
|
msgid ""
|
||||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||||
"set: {falseelems}"
|
"set: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:131
|
#: features/steps/attributes_eleclasses_methods.py:137
|
||||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_eleclasses_methods.py:132
|
#: features/steps/attributes_eleclasses_methods.py:138
|
||||||
msgid ""
|
msgid ""
|
||||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||||
"is not set: {falseelems}"
|
"is not set: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_psets_methods.py:29
|
#: features/steps/attributes_psets_methods.py:31
|
||||||
msgid ""
|
msgid ""
|
||||||
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||||
" in the pset."
|
" in the pset."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/attributes_psets_methods.py:30
|
#: features/steps/attributes_psets_methods.py:32
|
||||||
msgid ""
|
msgid ""
|
||||||
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||||
"missing the property {parameter} in the pset: {falseelems}"
|
"missing the property {parameter} in the pset: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:50
|
#: features/steps/geometric_detail_methods.py:54
|
||||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:51
|
#: features/steps/geometric_detail_methods.py:55
|
||||||
msgid ""
|
msgid ""
|
||||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||||
"{parameter} representation: {falseelems}"
|
"{parameter} representation: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:125
|
#: features/steps/geometric_detail_methods.py:132
|
||||||
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/geometric_detail_methods.py:126
|
#: features/steps/geometric_detail_methods.py:133
|
||||||
msgid ""
|
msgid ""
|
||||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||||
"have errors: {falseelems}"
|
"have errors: {falseelems}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: features/steps/ifcdata_methods.py:7
|
#: features/steps/ifcdata_methods.py:9
|
||||||
|
msgid "The IFC {} file could not be loaded"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:17
|
||||||
msgid "We expected a schema of {} but instead got {}"
|
msgid "We expected a schema of {} but instead got {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
# Dutch translations for Bimtester.
|
||||||
|
# Copyright (C) 2021 ORGANIZATION
|
||||||
|
# This file is distributed under the same license as the PROJECT project.
|
||||||
|
# Marcel Plomp , 2021.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PROJECT VERSIE\n"
|
||||||
|
"Report-Msgid-Bugs-To: EMAIL@ADDRES\n"
|
||||||
|
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||||
|
"PO-Revision-Date: 2020-12-18 11:41+0100\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language: nl\n"
|
||||||
|
"Language-Team: nl <LL@li.org>\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: Babel 2.6.0\n"
|
||||||
|
|
||||||
|
#: reports.py:158
|
||||||
|
msgid "en"
|
||||||
|
msgstr "nl"
|
||||||
|
|
||||||
|
#: reports.py:159
|
||||||
|
msgid "Success"
|
||||||
|
msgstr "Succes"
|
||||||
|
|
||||||
|
#: reports.py:160
|
||||||
|
msgid "Failure"
|
||||||
|
msgstr "Fout"
|
||||||
|
|
||||||
|
#: reports.py:161
|
||||||
|
msgid "Tests passed"
|
||||||
|
msgstr "Tests geslaagd"
|
||||||
|
|
||||||
|
#: reports.py:162
|
||||||
|
msgid "Duration"
|
||||||
|
msgstr "Tijdsduur"
|
||||||
|
|
||||||
|
#: reports.py:163
|
||||||
|
msgid "OpenBIM auditing is a feature of"
|
||||||
|
msgstr "OpenBIM-auditing is een kenmerk van"
|
||||||
|
|
||||||
|
#: reports.py:164
|
||||||
|
msgid "and"
|
||||||
|
msgstr "en"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:26
|
||||||
|
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||||
|
msgstr "Alle {elemcount} de elementen in het bestand zijn {ifc_class}"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:34
|
||||||
|
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"{falsecount} van de {elemcount} elementen zijn {ifc_class} elementen: "
|
||||||
|
"{falseelems}"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:43
|
||||||
|
#: features/steps/utils.py:158
|
||||||
|
msgid "Error in falsecount, something went wrong."
|
||||||
|
msgstr "Fout in de telling, er is iets misgegaan."
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:84
|
||||||
|
msgid ""
|
||||||
|
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||||
|
"attributes {parameter} has no value."
|
||||||
|
msgstr ""
|
||||||
|
"Voor alle {elemcount} {ifc_class} elementen heeft ten minste één van deze"
|
||||||
|
" class attributen {parameter} heeft geen waarde."
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:85
|
||||||
|
msgid ""
|
||||||
|
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||||
|
" least one of these class attributes {parameter} has no value: "
|
||||||
|
"{falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"Van de {falsecount} uit {elemcount} {ifc_class} elementen op ten minste "
|
||||||
|
"een van deze class-attributen {parameter} heeft geen waarde:{falseelems}"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:86
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:112
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:139
|
||||||
|
#: features/steps/attributes_psets_methods.py:33
|
||||||
|
#: features/steps/geometric_detail_methods.py:56
|
||||||
|
#: features/steps/geometric_detail_methods.py:134
|
||||||
|
msgid "There are no {ifc_class} elements in the IFC file."
|
||||||
|
msgstr "Er zijn geen {ifc_class} elementen in het IFC-bestand."
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:110
|
||||||
|
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||||
|
msgstr "De naam van alle {elemcount} {elemcount} elementen is niet ingesteld."
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:111
|
||||||
|
msgid ""
|
||||||
|
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||||
|
"set: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"De naam van {falsecount} van de {elemcount} {ifc_class} elementen is "
|
||||||
|
"nietset: {falseelems}"
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:137
|
||||||
|
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||||
|
msgstr ""
|
||||||
|
"De beschrijving van alle {elemcount} {elemcount} elementen is niet "
|
||||||
|
"ingesteld."
|
||||||
|
|
||||||
|
#: features/steps/attributes_eleclasses_methods.py:138
|
||||||
|
msgid ""
|
||||||
|
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||||
|
"is not set: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"De beschrijving van {falsecount} uit {elemcount} {ifc_class} elementen is"
|
||||||
|
" niet ingesteld: {falseelems}"
|
||||||
|
|
||||||
|
#: features/steps/attributes_psets_methods.py:31
|
||||||
|
msgid ""
|
||||||
|
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||||
|
" in the pset."
|
||||||
|
msgstr ""
|
||||||
|
"Alle {elemcount} {ifc_class} elementen missen de eigenschap {parameter} "
|
||||||
|
"in de pset."
|
||||||
|
|
||||||
|
#: features/steps/attributes_psets_methods.py:32
|
||||||
|
msgid ""
|
||||||
|
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||||
|
"missing the property {parameter} in the pset: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"De volgende {falsecount} van {elemcount} {ifc_class} elementen zijn "
|
||||||
|
"ontbreekt de eigenschap {parameter} in de pset: {falseelems}"
|
||||||
|
|
||||||
|
#: features/steps/geometric_detail_methods.py:54
|
||||||
|
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||||
|
msgstr ""
|
||||||
|
"Alle {elemcount} {ifc_class} elementen zijn geen {parameter} "
|
||||||
|
"representatie."
|
||||||
|
|
||||||
|
#: features/steps/geometric_detail_methods.py:55
|
||||||
|
msgid ""
|
||||||
|
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||||
|
"{parameter} representation: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"De volgende {falsecount} van {elemcount} {ifc_class} -elementen zijn geen"
|
||||||
|
" {parameter} representatie: {falseelems}"
|
||||||
|
|
||||||
|
#: features/steps/geometric_detail_methods.py:132
|
||||||
|
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||||
|
msgstr ""
|
||||||
|
"De geometrie van alle {elemcount} de {ifc_class} elementen bevatten "
|
||||||
|
"fouten."
|
||||||
|
|
||||||
|
#: features/steps/geometric_detail_methods.py:133
|
||||||
|
msgid ""
|
||||||
|
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||||
|
"have errors: {falseelems}"
|
||||||
|
msgstr ""
|
||||||
|
"De geometrie van {falsecount} van alle {elemcount} {ifc_class} elementen "
|
||||||
|
"hebben fouten: {falseelems}"
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:9
|
||||||
|
msgid "The IFC {} file could not be loaded"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:17
|
||||||
|
msgid "We expected a schema of {} but instead got {}"
|
||||||
|
msgstr "We verwachtten een schema van {} maar kregen in plaats daarvan {}"
|
||||||
|
|
||||||
|
#~ msgid "The geometry of all {} {} elements have errors."
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#~ msgid "The geometry of {} out of all {} {} elements have errors: {}"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#~ msgid "There are no {} elements in the IFC file."
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#~ msgid "All {} {} elements are not a IfcFacetedBrep representation."
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "The following {} of {} {} elements"
|
||||||
|
#~ " are not a IfcFacetedBrep representation:"
|
||||||
|
#~ " {}"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "All {elemcount} {ifc_class} elements are "
|
||||||
|
#~ "not a IfcFacetedBrep representation."
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "The following {falsecount} of {elemcount} "
|
||||||
|
#~ "{ifc_class} elements are not a "
|
||||||
|
#~ "IfcFacetedBrep representation: {falseelems}"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
@@ -8,12 +8,16 @@ from .features.steps.utils import switch_locale
|
|||||||
|
|
||||||
|
|
||||||
def generate_report(
|
def generate_report(
|
||||||
adir=".",
|
report_dir=".",
|
||||||
use_report_folder=True,
|
use_report_folder=True,
|
||||||
report_file_name="",
|
report_file_name="report.json",
|
||||||
html_template_file_path=""
|
html_template_file_path="",
|
||||||
|
report_file=""
|
||||||
):
|
):
|
||||||
|
|
||||||
|
# TODO use far less parameter
|
||||||
|
# to be discussed with other devs
|
||||||
|
|
||||||
print("# Generating HTML reports now.")
|
print("# Generating HTML reports now.")
|
||||||
|
|
||||||
# get locale path
|
# get locale path
|
||||||
@@ -32,23 +36,22 @@ def generate_report(
|
|||||||
if html_template_file_path:
|
if html_template_file_path:
|
||||||
report_template_path = html_template_file_path
|
report_template_path = html_template_file_path
|
||||||
|
|
||||||
# get report file
|
# get report file and report dir
|
||||||
report_dir = adir
|
if report_file:
|
||||||
if use_report_folder:
|
report_file = report_file
|
||||||
report_dir = os.path.join(adir, "report")
|
report_dir = os.path.dirname(report_file)
|
||||||
if not os.path.exists(report_dir):
|
|
||||||
return print("No report directory was found.")
|
|
||||||
|
|
||||||
if report_file_name:
|
|
||||||
report_path = os.path.join(report_dir, report_file_name)
|
|
||||||
else:
|
else:
|
||||||
report_path = os.path.join(report_dir, "report.json")
|
if use_report_folder:
|
||||||
# print(report_path)
|
report_dir = os.path.join(report_dir, "report")
|
||||||
if not os.path.exists(report_path):
|
report_file = os.path.join(report_dir, report_file_name)
|
||||||
return print("No report data was found.")
|
# print(report_file)
|
||||||
|
if not os.path.isdir(report_dir):
|
||||||
|
return print("Report directory does not exist.")
|
||||||
|
if not os.path.isfile(report_file):
|
||||||
|
return print("Report file does not exist.")
|
||||||
|
|
||||||
# read json report and create html report for each feature
|
# read json report and create html report for each feature
|
||||||
report = json.loads(open(report_path).read())
|
report = json.loads(open(report_file).read())
|
||||||
for feature in report:
|
for feature in report:
|
||||||
file_name = os.path.basename(feature["location"]).split(":")[0]
|
file_name = os.path.basename(feature["location"]).split(":")[0]
|
||||||
data = {
|
data = {
|
||||||
@@ -134,6 +137,10 @@ def generate_report(
|
|||||||
switch_locale(localedir, "de")
|
switch_locale(localedir, "de")
|
||||||
elif feature["keyword"] == "Fonctionnalité":
|
elif feature["keyword"] == "Fonctionnalité":
|
||||||
switch_locale(localedir, "fr")
|
switch_locale(localedir, "fr")
|
||||||
|
elif feature["keyword"] == "Funzionalità":
|
||||||
|
switch_locale(localedir, "it")
|
||||||
|
elif feature["keyword"] == "Functionaliteit":
|
||||||
|
switch_locale(localedir, "nl")
|
||||||
else:
|
else:
|
||||||
# standard English
|
# standard English
|
||||||
switch_locale(localedir, "en")
|
switch_locale(localedir, "en")
|
||||||
|
|||||||
+161
-147
@@ -1,11 +1,13 @@
|
|||||||
import behave.formatter.pretty # Needed for pyinstaller to package it
|
import behave.formatter.pretty # Needed for pyinstaller to package it
|
||||||
import fileinput
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from behave.__main__ import main as behave_main
|
|
||||||
|
|
||||||
|
# TODO: if the ifc file name or path contains special character
|
||||||
|
# like German Umlaute behave gives an error
|
||||||
|
|
||||||
|
|
||||||
# get bimtester source code module path
|
# get bimtester source code module path
|
||||||
@@ -26,29 +28,106 @@ def get_resource_path(relative_path):
|
|||||||
|
|
||||||
|
|
||||||
def run_tests(args):
|
def run_tests(args):
|
||||||
if not get_features(args):
|
|
||||||
print("No features could be found to check.")
|
print("# Run tests.")
|
||||||
|
|
||||||
|
report_file = os.path.join("report", "report.json")
|
||||||
|
|
||||||
|
if "copyintemprun" in args and args["copyintemprun"] is True:
|
||||||
|
print("copyintemprun")
|
||||||
|
is_copyintemprun = True
|
||||||
|
args, copy_base_path = copy_intmp_tests(args)
|
||||||
|
features_path = os.path.join(copy_base_path, "features")
|
||||||
|
report_file = os.path.join(copy_base_path, report_file)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("No copyintemprun")
|
||||||
|
is_copyintemprun = False
|
||||||
|
if not get_features(args):
|
||||||
|
print("No features could be found to check.")
|
||||||
|
return False
|
||||||
|
features_path = get_resource_path("features")
|
||||||
|
|
||||||
|
# get behave args
|
||||||
|
behave_args = get_behave_args(args, features_path, report_file)
|
||||||
|
|
||||||
|
# run tests
|
||||||
|
if behave_args != []:
|
||||||
|
run_behave(behave_args)
|
||||||
|
else:
|
||||||
|
print("Error, not able to run behave because of empty behave args.")
|
||||||
return False
|
return False
|
||||||
behave_args = [get_resource_path("features")]
|
|
||||||
|
if is_copyintemprun is True:
|
||||||
|
return report_file
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def get_behave_args(args, features_path, report_file):
|
||||||
|
|
||||||
|
if os.path.isdir(features_path):
|
||||||
|
behave_args = [features_path]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
if os.path.isdir(locale_path):
|
||||||
|
behave_args.extend([
|
||||||
|
# path for translation files
|
||||||
|
# next two lines are one arg
|
||||||
|
"--define",
|
||||||
|
"localedir={}".format(locale_path)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
"Error, translation locals path '{}' does not exist."
|
||||||
|
.format(locale_path)
|
||||||
|
)
|
||||||
|
|
||||||
if args["advanced_arguments"]:
|
if args["advanced_arguments"]:
|
||||||
behave_args.extend(args["advanced_arguments"].split())
|
behave_args.extend(args["advanced_arguments"].split())
|
||||||
elif not args["console"]:
|
|
||||||
|
if args["ifcfile"]:
|
||||||
behave_args.extend([
|
behave_args.extend([
|
||||||
|
# next two lines are one arg
|
||||||
|
"--define",
|
||||||
|
"ifcfile={}".format(args["ifcfile"])
|
||||||
|
])
|
||||||
|
|
||||||
|
if args["path"]:
|
||||||
|
behave_args.extend([
|
||||||
|
# next two lines are one arg
|
||||||
|
"--define",
|
||||||
|
"path={}".format(args["path"])
|
||||||
|
])
|
||||||
|
|
||||||
|
if not args["console"]:
|
||||||
|
behave_args.extend([
|
||||||
|
# redirect prints in step methods
|
||||||
|
# if step fails some output is catched, thus might not be printed
|
||||||
|
# https://github.com/behave/behave/issues/346
|
||||||
|
"--no-capture",
|
||||||
|
# next two lines are one arg
|
||||||
"--format",
|
"--format",
|
||||||
"json.pretty",
|
"json.pretty",
|
||||||
|
# report file, if relative, than relative to current shell path
|
||||||
|
# next two lines are one arg
|
||||||
"--outfile",
|
"--outfile",
|
||||||
"report/report.json"
|
report_file,
|
||||||
])
|
])
|
||||||
behave_args.extend([
|
|
||||||
"--define",
|
return behave_args
|
||||||
"localedir={}".format(locale_path)
|
|
||||||
])
|
|
||||||
if args["ifcfile"]:
|
def run_behave(behave_args):
|
||||||
behave_args.extend(["--define", "ifcfile={}".format(args["ifcfile"])])
|
|
||||||
if args["path"]:
|
from json import dumps
|
||||||
behave_args.extend(["--define", "path={}".format(args["path"])])
|
print(dumps(behave_args, indent=4))
|
||||||
|
|
||||||
|
from behave.__main__ import main as behave_main
|
||||||
behave_main(behave_args)
|
behave_main(behave_args)
|
||||||
print("# All tests are finished.")
|
print("# All tests are finished.")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -99,41 +178,9 @@ reset_runtime()
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# TODO: if the ifc file name or path contains special character
|
def copy_intmp_tests(args={}):
|
||||||
# like German Umlaute behave gives an error
|
|
||||||
|
|
||||||
|
print("# Copy features and steps to temp.")
|
||||||
def run_intmp_tests(args={}):
|
|
||||||
|
|
||||||
"""
|
|
||||||
run bimtester unit test in a temporary directory
|
|
||||||
features, steps and environment.py are copied to a temp directory
|
|
||||||
|
|
||||||
Keys of parameter args
|
|
||||||
----------------------
|
|
||||||
features: optional (ATM mandatory)
|
|
||||||
the path the features directory with feature files is in
|
|
||||||
ifcfile: optional (ATM mandatory)
|
|
||||||
the ifc file
|
|
||||||
advanced_arguments: optional
|
|
||||||
they will be directly passed to the behave call
|
|
||||||
|
|
||||||
features and ifcfile are given:
|
|
||||||
the ifcfile in feature files is replaced
|
|
||||||
|
|
||||||
features only is given (TODO):
|
|
||||||
the ifcfile provided in the feature files is used
|
|
||||||
|
|
||||||
ifcfile only is given (TODO):
|
|
||||||
features = ifcfile directory
|
|
||||||
the ifcfile in feature files is replaced
|
|
||||||
|
|
||||||
none of both is given (TODO):
|
|
||||||
the current directory = features
|
|
||||||
the ifcfile provided in the feature files is used
|
|
||||||
|
|
||||||
TODO: if the above is implemented adapt signature of run_all
|
|
||||||
"""
|
|
||||||
|
|
||||||
from behave import __version__ as behave_version
|
from behave import __version__ as behave_version
|
||||||
# https://github.com/behave/behave/issues/871
|
# https://github.com/behave/behave/issues/871
|
||||||
@@ -144,53 +191,82 @@ def run_intmp_tests(args={}):
|
|||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# print(args)
|
||||||
# get the features_path, the dir where the feature files to test are in
|
# get the features_path, the dir where the feature files to test are in
|
||||||
if "features" in args and args["features"] != "":
|
if ("featuresdir" in args and args["featuresdir"] != ""):
|
||||||
the_features_path = os.path.join(args["features"], "features")
|
is_features = True
|
||||||
|
the_features_path = os.path.join(args["featuresdir"], "features")
|
||||||
if not os.path.isdir(the_features_path):
|
if not os.path.isdir(the_features_path):
|
||||||
print(
|
print(
|
||||||
"The features directory does not exist: {}"
|
"Error, the features directory '{}' does not exist."
|
||||||
.format(the_features_path)
|
.format(the_features_path)
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
# TODO assume features beside ifc thus use ifc path
|
is_features = False
|
||||||
print("No features path was given.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# get ifc path and ifc filename
|
# get ifc path and ifc filename
|
||||||
if "ifcfile" in args and args["ifcfile"] != "":
|
if ("ifcfile" in args and args["ifcfile"] != ""):
|
||||||
|
is_ifcfile = True
|
||||||
ifcfile = args["ifcfile"]
|
ifcfile = args["ifcfile"]
|
||||||
|
if os.path.isfile(ifcfile) is not True:
|
||||||
|
print("Error, the ifc file '{}' does not exist.".format(ifcfile))
|
||||||
|
return False
|
||||||
ifc_path = os.path.dirname(os.path.realpath(ifcfile))
|
ifc_path = os.path.dirname(os.path.realpath(ifcfile))
|
||||||
if os.path.isdir(ifc_path) is False:
|
if os.path.isdir(ifc_path) is False:
|
||||||
print("ifc path does not exist.")
|
print("ifc path does not exist.")
|
||||||
return False
|
return False
|
||||||
if os.path.isfile(ifcfile) is True:
|
|
||||||
ifc_filename = os.path.basename(ifcfile)
|
|
||||||
else:
|
|
||||||
print("ifc file '{}' does not exist.".format(ifcfile))
|
|
||||||
return False
|
|
||||||
else:
|
else:
|
||||||
# TODO use ifc path from feature files
|
is_ifcfile = False
|
||||||
print("No ifc file was given.")
|
|
||||||
|
# print(is_features)
|
||||||
|
# print(is_ifcfile)
|
||||||
|
|
||||||
|
if is_features is True and is_ifcfile is True:
|
||||||
|
print("features given, ifcfile given.")
|
||||||
|
|
||||||
|
elif is_features is False and is_ifcfile is True:
|
||||||
|
print("features given, ifcfile NOT given.")
|
||||||
|
# features = ifcfile directory
|
||||||
|
the_features_path = os.path.join(ifc_path, "features")
|
||||||
|
|
||||||
|
elif is_features is True and is_ifcfile is False:
|
||||||
|
print("features given, ifcfile NOT given.")
|
||||||
|
# the ifcfile provided in the feature files is used
|
||||||
|
# TODO What will be passed as ifcfile arg?
|
||||||
|
print("Not yet implemented.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif is_features is False and is_ifcfile is False:
|
||||||
|
print("features NOT given, ifcfile NOT given.")
|
||||||
|
# the current directory = features
|
||||||
|
# the ifcfile provided in the feature files is used
|
||||||
|
# TODO What will be passed as ifcfile arg?
|
||||||
|
print("Not yet implemented.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Error: this should never happen, please debug.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# set up paths
|
# set up paths
|
||||||
# a unique temp path should not be used
|
# a unique temp path should not be used
|
||||||
# behave raises an ambiguous step exception
|
# behave raises an ambiguous step exception
|
||||||
# run_path = tempfile.mkdtemp()
|
# copy_base_path = tempfile.mkdtemp()
|
||||||
# thus use the same path on every run
|
# thus use the same path on every run
|
||||||
# but delete it if exists
|
# but delete it if exists
|
||||||
run_path = os.path.join(tempfile.gettempdir(), "bimtesterfc")
|
copy_base_path = os.path.join(tempfile.gettempdir(), "bimtesterfc")
|
||||||
if os.path.isdir(run_path):
|
if os.path.isdir(copy_base_path):
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
rmtree(run_path) # fails on read only files
|
rmtree(copy_base_path) # fails on read only files
|
||||||
if os.path.isdir(run_path):
|
if os.path.isdir(copy_base_path):
|
||||||
print("Delete former beimtester run dir {} failed".format(run_path))
|
print(
|
||||||
|
"Delete former beimtester run dir '{}' failed"
|
||||||
|
.format(copy_base_path)
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
os.mkdir(run_path)
|
os.mkdir(copy_base_path)
|
||||||
report_path = os.path.join(run_path, "report")
|
copy_features_path = os.path.join(copy_base_path, "features")
|
||||||
copy_features_path = os.path.join(run_path, "features")
|
|
||||||
|
|
||||||
# copy features path from bimtester source code
|
# copy features path from bimtester source code
|
||||||
srccode_features_path = os.path.join(
|
srccode_features_path = os.path.join(
|
||||||
@@ -218,8 +294,7 @@ def run_intmp_tests(args={}):
|
|||||||
# dirs_exist_ok=True
|
# dirs_exist_ok=True
|
||||||
# )
|
# )
|
||||||
|
|
||||||
# copy feature files and replace ifcpath in feature files
|
# copy feature files
|
||||||
# replaceing is IMHO better than copy the ifc file which could be 500 MB
|
|
||||||
feature_files = os.listdir(the_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:
|
||||||
@@ -230,99 +305,38 @@ def run_intmp_tests(args={}):
|
|||||||
os.path.join(the_features_path, feature_file),
|
os.path.join(the_features_path, feature_file),
|
||||||
cp_feature_file
|
cp_feature_file
|
||||||
)
|
)
|
||||||
# search the line
|
|
||||||
ff = open(cp_feature_file, "r")
|
|
||||||
lines = ff.readlines()
|
|
||||||
ff.close()
|
|
||||||
theline = ""
|
|
||||||
for line in lines:
|
|
||||||
if "* The IFC file" in line and "must be provided" in line:
|
|
||||||
theline = line
|
|
||||||
if ifc_filename is None:
|
|
||||||
ifc_filename = os.path.basename(theline.split('"')[1])
|
|
||||||
newifcline = (
|
|
||||||
' * The IFC file "{}" must be provided\n'
|
|
||||||
.format(os.path.join(ifc_path, ifc_filename))
|
|
||||||
)
|
|
||||||
# print(newifcline)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print("The line which sets the ifc file to test was not found.")
|
|
||||||
newifcline = ""
|
|
||||||
# replace the line
|
|
||||||
if newifcline != "":
|
|
||||||
# https://stackoverflow.com/a/290494
|
|
||||||
for line in fileinput.input(cp_feature_file, inplace=True):
|
|
||||||
# the print replaces the line in the file
|
|
||||||
print(line.replace(theline, newifcline), end="")
|
|
||||||
|
|
||||||
# get advanced args
|
return args, copy_base_path
|
||||||
# print to console from inside step files, add "--no-capture" flag
|
|
||||||
# https://github.com/behave/behave/issues/346
|
|
||||||
behave_args = [copy_features_path]
|
|
||||||
if "advanced_arguments" in args:
|
|
||||||
behave_args.extend(args["advanced_arguments"].split())
|
|
||||||
elif "console" not in args:
|
|
||||||
behave_args.extend([
|
|
||||||
# redirect prints in step methods
|
|
||||||
# if step fails some output is catched, thus might not be printed
|
|
||||||
"--no-capture",
|
|
||||||
# next two lines are one arg
|
|
||||||
"--format",
|
|
||||||
"json.pretty",
|
|
||||||
# next two lines are one arg
|
|
||||||
"--outfile",
|
|
||||||
os.path.join(report_path, "report.json"),
|
|
||||||
# next two lines are one arg
|
|
||||||
"--define",
|
|
||||||
"ifcbasename={}".format(os.path.splitext(ifc_filename)[0]),
|
|
||||||
# next two lines are one arg
|
|
||||||
"--define",
|
|
||||||
"localedir={}".format(locale_path)
|
|
||||||
])
|
|
||||||
print(behave_args)
|
|
||||||
|
|
||||||
# run tests
|
|
||||||
from behave.__main__ import main as behave_main
|
|
||||||
behave_main(behave_args)
|
|
||||||
print("All tests are finished.")
|
|
||||||
|
|
||||||
# delete steps
|
|
||||||
# shutil.rmtree(steps_path)
|
|
||||||
|
|
||||||
return run_path
|
|
||||||
|
|
||||||
|
|
||||||
def run_all(the_features_path, the_ifcfile):
|
def run_all(args):
|
||||||
|
|
||||||
|
print("# Run all.")
|
||||||
|
|
||||||
# run bimtester
|
# run bimtester
|
||||||
runpath = run_intmp_tests({
|
report_file = run_tests(args)
|
||||||
"features": the_features_path,
|
print(report_file)
|
||||||
"ifcfile": the_ifcfile
|
|
||||||
})
|
|
||||||
print(runpath)
|
|
||||||
|
|
||||||
# check if it worked out well
|
# check if it worked out well
|
||||||
if runpath is False:
|
if report_file is False:
|
||||||
print("BIMTester behave tests returned False.")
|
print("BIMTester behave tests returned False.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not os.path.isdir(runpath):
|
if not os.path.isfile(report_file):
|
||||||
print("runpath does not exist. This should not happen. Debug")
|
print("Report directory does not exist. This should not happen. Debug")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# create html report and open in webbrowser
|
# create html report and open in webbrowser
|
||||||
from .reports import generate_report
|
from .reports import generate_report
|
||||||
generate_report(runpath)
|
generate_report(report_file=report_file)
|
||||||
# get the feature files
|
# get the feature files
|
||||||
feature_files = os.listdir(
|
feature_files = os.listdir(
|
||||||
os.path.join(the_features_path, "features")
|
os.path.join(args["featuresdir"], "features")
|
||||||
)
|
)
|
||||||
# print(feature_files)
|
# print(feature_files)
|
||||||
for ff in feature_files:
|
for ff in feature_files:
|
||||||
webbrowser.open(os.path.join(
|
webbrowser.open(os.path.join(
|
||||||
runpath,
|
os.path.dirname(report_file),
|
||||||
"report",
|
|
||||||
ff + ".html"
|
ff + ".html"
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
ISO-10303-21;
|
||||||
|
HEADER;
|
||||||
|
FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
|
||||||
|
FILE_NAME('col.ifc','2021-01-11T05:16:43',('',''),(''),'IfcOpenShell 0.6.0b0','IfcOpenShell 0.6.0b0','');
|
||||||
|
FILE_SCHEMA(('IFC2X3'));
|
||||||
|
ENDSEC;
|
||||||
|
DATA;
|
||||||
|
#1=IFCPERSON($,$,'',$,$,$,$,$);
|
||||||
|
#2=IFCORGANIZATION($,'',$,$,$);
|
||||||
|
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||||
|
#4=IFCAPPLICATION(#2,'0.19 build 23652 (Git)','FreeCAD','118df2cf_ed21_438e_a41');
|
||||||
|
#5=IFCOWNERHISTORY(#3,#4,$,.ADDED.,1610342203,#3,#4,1610342203);
|
||||||
|
#6=IFCDIRECTION((1.,0.,0.));
|
||||||
|
#7=IFCDIRECTION((0.,0.,1.));
|
||||||
|
#8=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
|
#9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
|
||||||
|
#10=IFCDIRECTION((0.,1.,0.));
|
||||||
|
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
|
||||||
|
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||||
|
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||||
|
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||||
|
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||||
|
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
|
||||||
|
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
|
||||||
|
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
|
||||||
|
#20=IFCDIRECTION((0.,1.));
|
||||||
|
#21=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#20);
|
||||||
|
#22=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#21,$,.MODEL_VIEW.,$);
|
||||||
|
#23=IFCPROJECT('2iAYrakL9FABNNwZfj$CbO',#5,'BIMTester Example 1 - IFC2X3',$,$,$,$,(#21),#19);
|
||||||
|
#24=IFCDIRECTION((1.,0.));
|
||||||
|
#25=IFCCARTESIANPOINT((0.,0.));
|
||||||
|
#26=IFCAXIS2PLACEMENT2D(#25,#24);
|
||||||
|
#27=IFCCIRCLEPROFILEDEF(.AREA.,$,#26,0.2);
|
||||||
|
#28=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
|
#29=IFCAXIS2PLACEMENT3D(#28,#7,#6);
|
||||||
|
#30=IFCEXTRUDEDAREASOLID(#27,#29,#7,5.);
|
||||||
|
#31=IFCCOLOURRGB($,1.,0.5,1.);
|
||||||
|
#32=IFCSURFACESTYLERENDERING(#31,$,$,$,$,$,$,$,.FLAT.);
|
||||||
|
#33=IFCSURFACESTYLE($,.BOTH.,(#32));
|
||||||
|
#34=IFCPRESENTATIONSTYLEASSIGNMENT((#33));
|
||||||
|
#35=IFCSTYLEDITEM(#30,(#34),$);
|
||||||
|
#36=IFCLOCALPLACEMENT($,#9);
|
||||||
|
#37=IFCSHAPEREPRESENTATION(#22,'Body','SweptSolid',(#30));
|
||||||
|
#38=IFCPRODUCTDEFINITIONSHAPE($,$,(#37));
|
||||||
|
#39=IFCBUILDINGELEMENTPROXY('3JNmm1CUH9H9P6lVsx1y3W',#5,'Structure','',$,#36,#38,$,.ELEMENT.);
|
||||||
|
#40=IFCSITE('2PJ1ax1HL4SgHFFReEEwE$',#5,'Default Site','',$,$,$,$,.ELEMENT.,$,$,$,$,$);
|
||||||
|
#41=IFCRELAGGREGATES('1J6GQExT511x6QRu5FmkD2',#5,'ProjectLink','',#23,(#40));
|
||||||
|
#42=IFCBUILDING('1tIoXRzCXF3vuIMrF6RVcd',#5,'Default Building','',$,$,$,$,.ELEMENT.,$,$,$);
|
||||||
|
#43=IFCRELAGGREGATES('2GkPanCgnAzQY_0xv8dnHH',#5,'SiteLink','',#40,(#42));
|
||||||
|
#44=IFCBUILDINGSTOREY('1L8$GCIw116uw35vpyjSsO',#5,'Default Storey','',$,$,$,$,.ELEMENT.,$);
|
||||||
|
#45=IFCRELAGGREGATES('1lB$$h00nFaPQb2gvlhRX$',#5,'DefaultStoreyLink','',#42,(#44));
|
||||||
|
#46=IFCRELCONTAINEDINSPATIALSTRUCTURE('3dnpjDLD5DvuyUGcyHqRvU',#5,'UnassignedObjectsLink','',(#39),#44);
|
||||||
|
ENDSEC;
|
||||||
|
END-ISO-10303-21;
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
ISO-10303-21;
|
||||||
|
HEADER;
|
||||||
|
FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
|
||||||
|
FILE_NAME('col.ifc','2021-01-11T05:12:19',('',''),(''),'IfcOpenShell 0.6.0b0','IfcOpenShell 0.6.0b0','');
|
||||||
|
FILE_SCHEMA(('IFC4'));
|
||||||
|
ENDSEC;
|
||||||
|
DATA;
|
||||||
|
#1=IFCPERSON($,$,'',$,$,$,$,$);
|
||||||
|
#2=IFCORGANIZATION($,'',$,$,$);
|
||||||
|
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||||
|
#4=IFCAPPLICATION(#2,'0.19 build 23652 (Git)','FreeCAD','118df2cf_ed21_438e_a41');
|
||||||
|
#5=IFCOWNERHISTORY(#3,#4,$,.ADDED.,1610341939,#3,#4,1610341939);
|
||||||
|
#6=IFCDIRECTION((1.,0.,0.));
|
||||||
|
#7=IFCDIRECTION((0.,0.,1.));
|
||||||
|
#8=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
|
#9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
|
||||||
|
#10=IFCDIRECTION((0.,1.,0.));
|
||||||
|
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
|
||||||
|
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||||
|
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||||
|
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||||
|
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||||
|
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
|
||||||
|
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
|
||||||
|
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
|
||||||
|
#20=IFCDIRECTION((0.,1.));
|
||||||
|
#21=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#20);
|
||||||
|
#22=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#21,$,.MODEL_VIEW.,$);
|
||||||
|
#23=IFCPROJECT('2iAYrakL9FABNNwZfj$CbO',#5,'BIMTester Example 1 - IFC4',$,$,$,$,(#21),#19);
|
||||||
|
#24=IFCDIRECTION((1.,0.));
|
||||||
|
#25=IFCCARTESIANPOINT((0.,0.));
|
||||||
|
#26=IFCAXIS2PLACEMENT2D(#25,#24);
|
||||||
|
#27=IFCCIRCLEPROFILEDEF(.AREA.,$,#26,0.2);
|
||||||
|
#28=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
|
#29=IFCAXIS2PLACEMENT3D(#28,#7,#6);
|
||||||
|
#30=IFCEXTRUDEDAREASOLID(#27,#29,#7,5.);
|
||||||
|
#31=IFCCOLOURRGB($,1.,0.5,1.);
|
||||||
|
#32=IFCSURFACESTYLERENDERING(#31,$,$,$,$,$,$,$,.FLAT.);
|
||||||
|
#33=IFCSURFACESTYLE($,.BOTH.,(#32));
|
||||||
|
#34=IFCPRESENTATIONSTYLEASSIGNMENT((#33));
|
||||||
|
#35=IFCSTYLEDITEM(#30,(#34),$);
|
||||||
|
#36=IFCLOCALPLACEMENT($,#9);
|
||||||
|
#37=IFCSHAPEREPRESENTATION(#22,'Body','SweptSolid',(#30));
|
||||||
|
#38=IFCPRODUCTDEFINITIONSHAPE($,$,(#37));
|
||||||
|
#39=IFCBUILDINGELEMENTPROXY('3JNmm1CUH9H9P6lVsx1y3W',#5,'Structure','',$,#36,#38,$,.COMPLEX.);
|
||||||
|
#40=IFCSITE('2PJ1ax1HL4SgHFFReEEwE$',#5,'Default Site','',$,$,$,$,.ELEMENT.,$,$,$,$,$);
|
||||||
|
#41=IFCRELAGGREGATES('1J6GQExT511x6QRu5FmkD2',#5,'ProjectLink','',#23,(#40));
|
||||||
|
#42=IFCBUILDING('1tIoXRzCXF3vuIMrF6RVcd',#5,'Default Building','',$,$,$,$,.ELEMENT.,$,$,$);
|
||||||
|
#43=IFCRELAGGREGATES('2GkPanCgnAzQY_0xv8dnHH',#5,'SiteLink','',#40,(#42));
|
||||||
|
#44=IFCBUILDINGSTOREY('1L8$GCIw116uw35vpyjSsO',#5,'Default Storey','',$,$,$,$,.ELEMENT.,$);
|
||||||
|
#45=IFCRELAGGREGATES('1lB$$h00nFaPQb2gvlhRX$',#5,'DefaultStoreyLink','',#42,(#44));
|
||||||
|
#46=IFCRELCONTAINEDINSPATIALSTRUCTURE('3dnpjDLD5DvuyUGcyHqRvU',#5,'UnassignedObjectsLink','',(#39),#44);
|
||||||
|
ENDSEC;
|
||||||
|
END-ISO-10303-21;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# language: de
|
||||||
|
|
||||||
|
Funktionalität: Basisdaten
|
||||||
|
|
||||||
|
Um BIM-Daten anzusehen
|
||||||
|
Für alle beteiligten Akteure
|
||||||
|
Wir brauchen eine IFC-Datei
|
||||||
|
|
||||||
|
|
||||||
|
Szenario: Bereitstellen von IFC-Daten
|
||||||
|
|
||||||
|
* Die IFC-Datei wurde durch einen Startparameter zur Verfügung gestellt
|
||||||
|
* Die IFC-Daten müssen das IFC2X3 Schema benutzen
|
||||||
|
|
||||||
|
|
||||||
|
Szenario: Projektinformationen
|
||||||
|
|
||||||
|
* Der Name, die Abkürzung oder die Kurzkennung des Projektes ist "BIMTester Example 1 - IFC2X3"
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# language: en
|
||||||
|
|
||||||
|
Feature: Base setup
|
||||||
|
|
||||||
|
In order to view the BIM data
|
||||||
|
As any interested stakeholder
|
||||||
|
We need an IFC file
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Receiving a file
|
||||||
|
|
||||||
|
* The IFC file has been provided through an argument
|
||||||
|
* IFC data must use the IFC2X3 schema
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Project information
|
||||||
|
|
||||||
|
* The project name, code, or short identifier must be "BIMTester Example 1 - IFC2X3"
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# language: fr
|
||||||
|
|
||||||
|
Fonctionnalité: Base setup
|
||||||
|
|
||||||
|
In order to view the BIM data
|
||||||
|
As any interested stakeholder
|
||||||
|
We need an IFC file
|
||||||
|
|
||||||
|
|
||||||
|
Scénario: Recevoir e fichier
|
||||||
|
|
||||||
|
* The IFC file has been provided through an argument
|
||||||
|
* Les données IFC doivent utiliser le schéma IFC2X3
|
||||||
|
|
||||||
|
|
||||||
|
Scénario: Project information
|
||||||
|
|
||||||
|
* The project name, code, or short identifier must be "BIMTester Example 1 - IFC2X3"
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# language: it
|
||||||
|
|
||||||
|
Funzionalità: Dati di base
|
||||||
|
|
||||||
|
Per poter consultare dati BIM
|
||||||
|
a tutti gli attori partecipanti
|
||||||
|
serve un file IFC
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Preparare Dati IFC
|
||||||
|
|
||||||
|
* Il file IFC è stato fornito attraverso un argumento
|
||||||
|
* I dati IFC devono seguire lo schema IFC2X3
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Project information
|
||||||
|
|
||||||
|
* Il nome del progetto, codice o identificatore breve deve essere "BIMTester Example 1 - IFC2X3"
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# language: nl
|
||||||
|
|
||||||
|
Functionaliteit: Basisgegevens
|
||||||
|
|
||||||
|
Om BIM-gegevens te bekijken
|
||||||
|
Zoals elke geïnteresseerde stakeholder
|
||||||
|
We hebben een IFC-bestand nodig
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Bestand ontvangen
|
||||||
|
|
||||||
|
* The IFC file has been provided through an argument
|
||||||
|
* IFC-gegevens moeten het IFC2X3 -schema gebruiken
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Project informatie
|
||||||
|
|
||||||
|
* De projectnaam, code of korte ID moet "BIMTester Example 1 - IFC2X3"
|
||||||
@@ -1,13 +1,19 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import os
|
||||||
|
|
||||||
from bimtester import clean
|
from bimtester import clean
|
||||||
from bimtester import reports
|
from bimtester import reports
|
||||||
from bimtester import run
|
from bimtester import run
|
||||||
|
|
||||||
|
|
||||||
def show_widget(features="", ifcfile=""):
|
def show_widget(
|
||||||
|
features="",
|
||||||
|
ifcfile="",
|
||||||
|
get_featurepath_from_ifcpath=False,
|
||||||
|
args=[]
|
||||||
|
):
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from PySide2 import QtWidgets
|
from PySide2 import QtWidgets
|
||||||
@@ -18,7 +24,12 @@ def show_widget(features="", ifcfile=""):
|
|||||||
app = QtWidgets.QApplication(sys.argv)
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
|
||||||
# Create and show the form
|
# Create and show the form
|
||||||
form = GuiWidgetBimTester(features, ifcfile)
|
form = GuiWidgetBimTester(
|
||||||
|
features,
|
||||||
|
ifcfile,
|
||||||
|
get_featurepath_from_ifcpath,
|
||||||
|
args
|
||||||
|
)
|
||||||
form.show()
|
form.show()
|
||||||
|
|
||||||
# Run the main Qt loop
|
# Run the main Qt loop
|
||||||
@@ -27,15 +38,76 @@ def show_widget(features="", ifcfile=""):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
# TODO make similar to bash commands
|
||||||
|
# use - not _ in named args
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Runs unit tests for BIM data"
|
description="Runs unit tests for BIM data"
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-a",
|
||||||
|
"--advanced-arguments",
|
||||||
|
type=str,
|
||||||
|
help="Specify your own arguments to Python's Behave",
|
||||||
|
default=""
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--console",
|
||||||
|
action="store_true",
|
||||||
|
help="Show results in the console"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-d",
|
||||||
|
"--featuresdir",
|
||||||
|
type=str,
|
||||||
|
help=(
|
||||||
|
"Specify a features directory. This should contain "
|
||||||
|
"a directory named 'features' which contains all the "
|
||||||
|
"feature files."
|
||||||
|
),
|
||||||
|
default=""
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-f",
|
||||||
|
"--feature",
|
||||||
|
type=str,
|
||||||
|
help="Specify a feature file to test",
|
||||||
|
default=""
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-g",
|
||||||
|
"--gui",
|
||||||
|
action="store_true",
|
||||||
|
help=(
|
||||||
|
"Start the gui. The option t (copyintemprun) "
|
||||||
|
"is triggered automaticly."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-i",
|
||||||
|
"--ifcfile",
|
||||||
|
type=str,
|
||||||
|
help=(
|
||||||
|
"Specify a ifc file."
|
||||||
|
),
|
||||||
|
default=""
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-p",
|
"-p",
|
||||||
"--purge",
|
"--purge",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Purge tests of deleted elements"
|
help="Purge tests of deleted elements"
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-path",
|
||||||
|
"--path",
|
||||||
|
type=str,
|
||||||
|
help=(
|
||||||
|
"Specify a path to prepend to feature and ifc file"
|
||||||
|
),
|
||||||
|
default=""
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-r",
|
"-r",
|
||||||
"--report",
|
"--report",
|
||||||
@@ -49,66 +121,18 @@ if __name__ == "__main__":
|
|||||||
help="Generate a HTML report after running the tests"
|
help="Generate a HTML report after running the tests"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-path",
|
"-t",
|
||||||
"--path",
|
"--copyintemprun",
|
||||||
type=str,
|
|
||||||
help=(
|
|
||||||
"Specify a path to prepend to feature and ifc file"
|
|
||||||
),
|
|
||||||
default=""
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-c",
|
|
||||||
"--console",
|
|
||||||
action="store_true",
|
|
||||||
help="Show results in the console"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-f",
|
|
||||||
"--feature",
|
|
||||||
type=str,
|
|
||||||
help="Specify a feature file to test",
|
|
||||||
default=""
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-a",
|
|
||||||
"--advanced-arguments",
|
|
||||||
type=str,
|
|
||||||
help="Specify your own arguments to Python's Behave",
|
|
||||||
default=""
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-g",
|
|
||||||
"--gui",
|
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help=(
|
help=(
|
||||||
"Start the gui. The option t (run in temp directory) "
|
"Copy steps and feature files into a temporary directory "
|
||||||
"is triggered automaticly."
|
"and run bimtester with them."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
"featuresdir",
|
|
||||||
type=str,
|
|
||||||
nargs="?",
|
|
||||||
help=(
|
|
||||||
"Specify a features directory. This should contain "
|
|
||||||
" a directory named features which contains all the "
|
|
||||||
"feature files."
|
|
||||||
),
|
|
||||||
default=""
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"ifcfile",
|
|
||||||
type=str,
|
|
||||||
nargs="?",
|
|
||||||
help=(
|
|
||||||
"Specify a ifc file."
|
|
||||||
),
|
|
||||||
default=""
|
|
||||||
)
|
|
||||||
|
|
||||||
args = vars(parser.parse_args())
|
args = vars(parser.parse_args())
|
||||||
print(args)
|
from json import dumps
|
||||||
|
print(dumps(args, indent=4))
|
||||||
|
|
||||||
if args["path"]:
|
if args["path"]:
|
||||||
if args["feature"]:
|
if args["feature"]:
|
||||||
@@ -121,9 +145,19 @@ if __name__ == "__main__":
|
|||||||
elif args["report"]:
|
elif args["report"]:
|
||||||
reports.generate_report()
|
reports.generate_report()
|
||||||
elif args["gui"]:
|
elif args["gui"]:
|
||||||
show_widget(args["featuresdir"], args["ifcfile"])
|
args["copyintemprun"] = True
|
||||||
|
fea = args["featuresdir"]
|
||||||
|
ifc = args["ifcfile"]
|
||||||
|
if fea != "" and ifc != "":
|
||||||
|
show_widget(fea, ifc, False, args)
|
||||||
|
elif fea == "" and ifc != "":
|
||||||
|
show_widget(fea, ifc, True, args)
|
||||||
|
elif args["copyintemprun"]:
|
||||||
|
run.run_tests(args)
|
||||||
|
# TODO merge with else, but do not forget the tmp dir is not known
|
||||||
else:
|
else:
|
||||||
run.run_tests(args)
|
run.run_tests(args)
|
||||||
if args["report_after_run"]:
|
if args["report_after_run"]:
|
||||||
reports.generate_report()
|
reports.generate_report()
|
||||||
|
|
||||||
print("# All tasks are complete :-)")
|
print("# All tasks are complete :-)")
|
||||||
|
|||||||
@@ -266,21 +266,7 @@ endif
|
|||||||
rm -rf dist/working
|
rm -rf dist/working
|
||||||
|
|
||||||
# Provides IFCPatch functionality
|
# Provides IFCPatch functionality
|
||||||
cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/ifcpatch.py
|
cd dist/blenderbim/libs/site/packages/ && svn export https://github.com/IfcOpenShell/IfcOpenShell/trunk/src/ifcpatch
|
||||||
cd dist/blenderbim/libs/site/packages/ && mkdir recipes
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/__init__.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/ExtractElements.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/MergeProject.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/Migrate.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/OffsetObjectPlacements.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/OffsetStoreyElevations.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/Optimise.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/RecycleNonRootedElements.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/RemoveSiteRepresentation.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/ResetAbsoluteCoordinates.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/ResetSpatialElementLocations.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/SetRefElevation.py
|
|
||||||
cd dist/blenderbim/libs/site/packages/recipes && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcpatch/recipes/SplitByBuildingStorey.py
|
|
||||||
|
|
||||||
cd dist/blenderbim && sed -i "s/999999/$(VERSION)/" __init__.py
|
cd dist/blenderbim && sed -i "s/999999/$(VERSION)/" __init__.py
|
||||||
cd dist && zip -r blender28-bim-$(VERSION)-$(PLATFORM).zip ./*
|
cd dist && zip -r blender28-bim-$(VERSION)-$(PLATFORM).zip ./*
|
||||||
|
|||||||
@@ -5,46 +5,49 @@ bpy = sys.modules.get("bpy")
|
|||||||
|
|
||||||
if bpy is not None:
|
if bpy is not None:
|
||||||
import bpy
|
import bpy
|
||||||
import blenderbim.bim.module.root as module_root
|
import importlib
|
||||||
import blenderbim.bim.module.aggregate as module_aggregate
|
|
||||||
import blenderbim.bim.module.attribute as module_attribute
|
|
||||||
import blenderbim.bim.module.bcf as module_bcf
|
|
||||||
import blenderbim.bim.module.context as module_context
|
|
||||||
import blenderbim.bim.module.covetool as module_covetool
|
|
||||||
import blenderbim.bim.module.debug as module_debug
|
|
||||||
import blenderbim.bim.module.geometry as module_geometry
|
|
||||||
import blenderbim.bim.module.model as module_model
|
|
||||||
import blenderbim.bim.module.owner as module_owner
|
|
||||||
import blenderbim.bim.module.project as module_project
|
|
||||||
import blenderbim.bim.module.pset as module_pset
|
|
||||||
import blenderbim.bim.module.spatial as module_spatial
|
|
||||||
import blenderbim.bim.module.style as module_style
|
|
||||||
import blenderbim.bim.module.type as module_type
|
|
||||||
import blenderbim.bim.module.unit as module_unit
|
|
||||||
from . import ui, prop, operator
|
from . import ui, prop, operator
|
||||||
|
|
||||||
|
modules = {
|
||||||
|
"root": None,
|
||||||
|
"aggregate": None,
|
||||||
|
"attribute": None,
|
||||||
|
"bcf": None,
|
||||||
|
"cobie": None,
|
||||||
|
"context": None,
|
||||||
|
"covetool": None,
|
||||||
|
"csv": None,
|
||||||
|
"diff": None,
|
||||||
|
"bimtester": None,
|
||||||
|
"debug": None,
|
||||||
|
"geometry": None,
|
||||||
|
"georeference": None,
|
||||||
|
"material": None,
|
||||||
|
"model": None,
|
||||||
|
"owner": None,
|
||||||
|
"project": None,
|
||||||
|
"pset": None,
|
||||||
|
"spatial": None,
|
||||||
|
"style": None,
|
||||||
|
"type": None,
|
||||||
|
"unit": None,
|
||||||
|
"void": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
for name in modules.keys():
|
||||||
|
modules[name] = importlib.import_module(f"blenderbim.bim.module.{name}")
|
||||||
|
|
||||||
classes = [
|
classes = [
|
||||||
operator.SelectClass,
|
operator.SelectClass,
|
||||||
operator.SelectType,
|
operator.SelectType,
|
||||||
operator.OpenUri,
|
operator.OpenUri,
|
||||||
operator.SelectFeaturesDir,
|
|
||||||
operator.SelectDiffJsonFile,
|
|
||||||
operator.SelectDiffNewFile,
|
|
||||||
operator.SelectDiffOldFile,
|
|
||||||
operator.SelectDataDir,
|
operator.SelectDataDir,
|
||||||
operator.SelectSchemaDir,
|
operator.SelectSchemaDir,
|
||||||
operator.SelectIfcFile,
|
operator.SelectIfcFile,
|
||||||
operator.ValidateIfcFile,
|
|
||||||
operator.ExportIFC,
|
operator.ExportIFC,
|
||||||
operator.ImportIFC,
|
operator.ImportIFC,
|
||||||
operator.ColourByClass,
|
|
||||||
operator.ColourByAttribute,
|
operator.ColourByAttribute,
|
||||||
operator.ColourByPset,
|
operator.ColourByPset,
|
||||||
operator.ResetObjectColours,
|
|
||||||
operator.ApproveClass,
|
|
||||||
operator.RejectClass,
|
|
||||||
operator.SelectAudited,
|
|
||||||
operator.RejectElement,
|
|
||||||
operator.SelectExternalMaterialDir,
|
operator.SelectExternalMaterialDir,
|
||||||
operator.AddSweptSolid,
|
operator.AddSweptSolid,
|
||||||
operator.RemoveSweptSolid,
|
operator.RemoveSweptSolid,
|
||||||
@@ -54,8 +57,6 @@ if bpy is not None:
|
|||||||
operator.SelectSweptSolidInnerCurves,
|
operator.SelectSweptSolidInnerCurves,
|
||||||
operator.AssignSweptSolidExtrusion,
|
operator.AssignSweptSolidExtrusion,
|
||||||
operator.SelectSweptSolidExtrusion,
|
operator.SelectSweptSolidExtrusion,
|
||||||
operator.AddQto,
|
|
||||||
operator.RemoveQto,
|
|
||||||
operator.AddMaterialPset,
|
operator.AddMaterialPset,
|
||||||
operator.RemoveMaterialPset,
|
operator.RemoveMaterialPset,
|
||||||
operator.AddMaterialLayer,
|
operator.AddMaterialLayer,
|
||||||
@@ -86,10 +87,6 @@ if bpy is not None:
|
|||||||
operator.SelectGlobalId,
|
operator.SelectGlobalId,
|
||||||
operator.SelectAttribute,
|
operator.SelectAttribute,
|
||||||
operator.SelectPset,
|
operator.SelectPset,
|
||||||
operator.CreateAggregate,
|
|
||||||
operator.EditAggregate,
|
|
||||||
operator.SaveAggregate,
|
|
||||||
operator.ExplodeAggregate,
|
|
||||||
operator.LoadClassification,
|
operator.LoadClassification,
|
||||||
operator.AddClassification,
|
operator.AddClassification,
|
||||||
operator.RemoveClassification,
|
operator.RemoveClassification,
|
||||||
@@ -107,8 +104,6 @@ if bpy is not None:
|
|||||||
operator.OpenView,
|
operator.OpenView,
|
||||||
operator.OpenViewCamera,
|
operator.OpenViewCamera,
|
||||||
operator.ActivateView,
|
operator.ActivateView,
|
||||||
operator.ExecuteIfcDiff,
|
|
||||||
operator.VisualiseDiff,
|
|
||||||
operator.ExportClashSets,
|
operator.ExportClashSets,
|
||||||
operator.ImportClashSets,
|
operator.ImportClashSets,
|
||||||
operator.AddClashSet,
|
operator.AddClashSet,
|
||||||
@@ -133,11 +128,6 @@ if bpy is not None:
|
|||||||
operator.RemovePropertyTemplate,
|
operator.RemovePropertyTemplate,
|
||||||
operator.AddSectionPlane,
|
operator.AddSectionPlane,
|
||||||
operator.RemoveSectionPlane,
|
operator.RemoveSectionPlane,
|
||||||
operator.AddCsvAttribute,
|
|
||||||
operator.RemoveCsvAttribute,
|
|
||||||
operator.ExportIfcCsv,
|
|
||||||
operator.ImportIfcCsv,
|
|
||||||
operator.EyedropIfcCsv,
|
|
||||||
operator.ReloadIfcFile,
|
operator.ReloadIfcFile,
|
||||||
operator.AddIfcFile,
|
operator.AddIfcFile,
|
||||||
operator.RemoveIfcFile,
|
operator.RemoveIfcFile,
|
||||||
@@ -148,14 +138,6 @@ if bpy is not None:
|
|||||||
operator.AddVariable,
|
operator.AddVariable,
|
||||||
operator.RemoveVariable,
|
operator.RemoveVariable,
|
||||||
operator.PropagateTextData,
|
operator.PropagateTextData,
|
||||||
operator.ConvertLocalToGlobal,
|
|
||||||
operator.ConvertGlobalToLocal,
|
|
||||||
operator.GuessQuantity,
|
|
||||||
operator.ExecuteBIMTester,
|
|
||||||
operator.BIMTesterPurge,
|
|
||||||
operator.SelectCobieIfcFile,
|
|
||||||
operator.SelectCobieJsonFile,
|
|
||||||
operator.ExecuteIfcCobie,
|
|
||||||
operator.SelectIfcPatchInput,
|
operator.SelectIfcPatchInput,
|
||||||
operator.SelectIfcPatchOutput,
|
operator.SelectIfcPatchOutput,
|
||||||
operator.ExecuteIfcPatch,
|
operator.ExecuteIfcPatch,
|
||||||
@@ -178,8 +160,6 @@ if bpy is not None:
|
|||||||
operator.BuildSchedule,
|
operator.BuildSchedule,
|
||||||
operator.AddScheduleToSheet,
|
operator.AddScheduleToSheet,
|
||||||
operator.SetViewportShadowFromSun,
|
operator.SetViewportShadowFromSun,
|
||||||
operator.SetNorthOffset,
|
|
||||||
operator.GetNorthOffset,
|
|
||||||
operator.AddPresentationLayer,
|
operator.AddPresentationLayer,
|
||||||
operator.AssignPresentationLayer,
|
operator.AssignPresentationLayer,
|
||||||
operator.UnassignPresentationLayer,
|
operator.UnassignPresentationLayer,
|
||||||
@@ -197,17 +177,10 @@ if bpy is not None:
|
|||||||
operator.LinkIfc,
|
operator.LinkIfc,
|
||||||
operator.SnapSpacesTogether,
|
operator.SnapSpacesTogether,
|
||||||
operator.CopyGrid,
|
operator.CopyGrid,
|
||||||
|
operator.AddSectionsAnnotations,
|
||||||
prop.StrProperty,
|
prop.StrProperty,
|
||||||
prop.Attribute,
|
prop.Attribute,
|
||||||
prop.MaterialLayer,
|
|
||||||
prop.MaterialConstituent,
|
|
||||||
prop.MaterialProfile,
|
|
||||||
prop.MaterialSet,
|
|
||||||
prop.Variable,
|
prop.Variable,
|
||||||
prop.Role,
|
|
||||||
prop.Address,
|
|
||||||
prop.Person,
|
|
||||||
prop.Organisation,
|
|
||||||
prop.Classification,
|
prop.Classification,
|
||||||
prop.ClassificationReference,
|
prop.ClassificationReference,
|
||||||
prop.ClassificationView,
|
prop.ClassificationView,
|
||||||
@@ -227,8 +200,6 @@ if bpy is not None:
|
|||||||
prop.BIMProperties,
|
prop.BIMProperties,
|
||||||
prop.DocProperties,
|
prop.DocProperties,
|
||||||
prop.BIMLibrary,
|
prop.BIMLibrary,
|
||||||
prop.MapConversion,
|
|
||||||
prop.TargetCRS,
|
|
||||||
prop.IfcParameter,
|
prop.IfcParameter,
|
||||||
prop.BoundaryCondition,
|
prop.BoundaryCondition,
|
||||||
prop.PsetQto,
|
prop.PsetQto,
|
||||||
@@ -245,26 +216,17 @@ if bpy is not None:
|
|||||||
ui.BIM_PT_drawings,
|
ui.BIM_PT_drawings,
|
||||||
ui.BIM_PT_schedules,
|
ui.BIM_PT_schedules,
|
||||||
ui.BIM_PT_sheets,
|
ui.BIM_PT_sheets,
|
||||||
ui.BIM_PT_bim,
|
|
||||||
ui.BIM_PT_psets,
|
ui.BIM_PT_psets,
|
||||||
ui.BIM_PT_classifications,
|
ui.BIM_PT_classifications,
|
||||||
ui.BIM_PT_document_information,
|
ui.BIM_PT_document_information,
|
||||||
ui.BIM_PT_constraints,
|
ui.BIM_PT_constraints,
|
||||||
ui.BIM_PT_search,
|
ui.BIM_PT_search,
|
||||||
ui.BIM_PT_ifccsv,
|
|
||||||
ui.BIM_PT_ifcclash,
|
ui.BIM_PT_ifcclash,
|
||||||
ui.BIM_PT_qa,
|
|
||||||
ui.BIM_PT_library,
|
ui.BIM_PT_library,
|
||||||
ui.BIM_PT_gis,
|
|
||||||
ui.BIM_PT_presentation_layers,
|
ui.BIM_PT_presentation_layers,
|
||||||
ui.BIM_PT_diff,
|
|
||||||
ui.BIM_PT_cobie,
|
|
||||||
ui.BIM_PT_patch,
|
ui.BIM_PT_patch,
|
||||||
ui.BIM_PT_mvd,
|
ui.BIM_PT_mvd,
|
||||||
ui.BIM_PT_material,
|
|
||||||
ui.BIM_PT_presentation_layer_data,
|
ui.BIM_PT_presentation_layer_data,
|
||||||
ui.BIM_PT_object_material,
|
|
||||||
ui.BIM_PT_object_qto,
|
|
||||||
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,
|
||||||
@@ -288,22 +250,8 @@ if bpy is not None:
|
|||||||
ui.BIM_ADDON_preferences,
|
ui.BIM_ADDON_preferences,
|
||||||
]
|
]
|
||||||
|
|
||||||
classes.extend(module_root.classes)
|
for module in modules.values():
|
||||||
classes.extend(module_aggregate.classes)
|
classes.extend(module.classes)
|
||||||
classes.extend(module_attribute.classes)
|
|
||||||
classes.extend(module_bcf.classes)
|
|
||||||
classes.extend(module_context.classes)
|
|
||||||
classes.extend(module_covetool.classes)
|
|
||||||
classes.extend(module_debug.classes)
|
|
||||||
classes.extend(module_geometry.classes)
|
|
||||||
classes.extend(module_model.classes)
|
|
||||||
classes.extend(module_owner.classes)
|
|
||||||
classes.extend(module_project.classes)
|
|
||||||
classes.extend(module_pset.classes)
|
|
||||||
classes.extend(module_spatial.classes)
|
|
||||||
classes.extend(module_style.classes)
|
|
||||||
classes.extend(module_type.classes)
|
|
||||||
classes.extend(module_unit.classes)
|
|
||||||
|
|
||||||
def menu_func_export(self, context):
|
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)")
|
||||||
@@ -325,31 +273,18 @@ if bpy is not None:
|
|||||||
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
||||||
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.TargetCRS = bpy.props.PointerProperty(type=prop.TargetCRS)
|
|
||||||
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||||
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||||
|
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) # Check if we need this
|
||||||
bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
|
bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
|
||||||
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
||||||
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()
|
for module in modules.values():
|
||||||
module_attribute.register()
|
module.register()
|
||||||
module_bcf.register()
|
|
||||||
module_context.register()
|
|
||||||
module_covetool.register()
|
|
||||||
module_debug.register()
|
|
||||||
module_geometry.register()
|
|
||||||
module_model.register()
|
|
||||||
module_owner.register()
|
|
||||||
module_project.register()
|
|
||||||
module_pset.register()
|
|
||||||
module_spatial.register()
|
|
||||||
module_style.register()
|
|
||||||
module_type.register()
|
|
||||||
module_unit.register()
|
|
||||||
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
|
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
|
||||||
bpy.app.handlers.load_post.append(prop.toggleDecorationsOnLoad)
|
bpy.app.handlers.load_post.append(prop.toggleDecorationsOnLoad)
|
||||||
|
|
||||||
@@ -361,29 +296,16 @@ if bpy is not None:
|
|||||||
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.DocProperties
|
del bpy.types.Scene.DocProperties
|
||||||
del bpy.types.Scene.MapConversion
|
|
||||||
del bpy.types.Scene.TargetCRS
|
|
||||||
del bpy.types.Object.BIMObjectProperties
|
del bpy.types.Object.BIMObjectProperties
|
||||||
del bpy.types.Collection.BIMObjectProperties
|
del bpy.types.Material.BIMObjectProperties
|
||||||
|
del bpy.types.Collection.BIMObjectProperties # Check if we need this
|
||||||
del bpy.types.Material.BIMMaterialProperties
|
del bpy.types.Material.BIMMaterialProperties
|
||||||
del bpy.types.Mesh.BIMMeshProperties
|
del bpy.types.Mesh.BIMMeshProperties
|
||||||
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()
|
for module in reversed(list(modules.values())):
|
||||||
module_style.unregister()
|
module.unregister()
|
||||||
module_spatial.unregister()
|
|
||||||
module_pset.unregister()
|
|
||||||
module_project.unregister()
|
|
||||||
module_owner.unregister()
|
|
||||||
module_model.unregister()
|
|
||||||
module_geometry.unregister()
|
|
||||||
module_debug.unregister()
|
|
||||||
module_covetool.unregister()
|
|
||||||
module_context.unregister()
|
|
||||||
module_bcf.unregister()
|
|
||||||
module_attribute.register()
|
|
||||||
module_aggregate.register()
|
|
||||||
module_root.unregister()
|
|
||||||
bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler)
|
bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import gpu
|
|||||||
import bgl
|
import bgl
|
||||||
from gpu.types import GPUShader, GPUBatch, GPUIndexBuf, GPUVertBuf, GPUVertFormat
|
from gpu.types import GPUShader, GPUBatch, GPUIndexBuf, GPUVertBuf, GPUVertFormat
|
||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
from . import helper
|
||||||
|
|
||||||
|
|
||||||
class BaseDecorator():
|
class BaseDecorator():
|
||||||
@@ -641,7 +642,7 @@ class LevelDecorator(BaseDecorator):
|
|||||||
self.draw_labels(context, obj, splines)
|
self.draw_labels(context, obj, splines)
|
||||||
|
|
||||||
|
|
||||||
class PlanDecorator(LevelDecorator):
|
class PlanLevelDecorator(LevelDecorator):
|
||||||
basename = "IfcAnnotation/Plan Level"
|
basename = "IfcAnnotation/Plan Level"
|
||||||
|
|
||||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||||
@@ -723,7 +724,7 @@ class PlanDecorator(LevelDecorator):
|
|||||||
self.draw_label(context, text, p0, dir, gap=8, center=False)
|
self.draw_label(context, text, p0, dir, gap=8, center=False)
|
||||||
|
|
||||||
|
|
||||||
class SectionDecorator(LevelDecorator):
|
class SectionLevelDecorator(LevelDecorator):
|
||||||
basename = "IfcAnnotation/Section Level"
|
basename = "IfcAnnotation/Section Level"
|
||||||
|
|
||||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||||
@@ -996,6 +997,108 @@ class GridDecorator(BaseDecorator):
|
|||||||
self.draw_label(context, text, p1, dir, vcenter=True, gap=0)
|
self.draw_label(context, text, p1, dir, vcenter=True, gap=0)
|
||||||
|
|
||||||
|
|
||||||
|
class SectionViewDecorator(LevelDecorator):
|
||||||
|
basename = "IfcAnnotation/Section"
|
||||||
|
|
||||||
|
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||||
|
#define CIRCLE_SIZE 8.0
|
||||||
|
#define TRIANGLE_L 32.0
|
||||||
|
#define TRIANGLE_W 16.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
GEOM_GLSL = """
|
||||||
|
uniform vec2 winsize;
|
||||||
|
|
||||||
|
layout(lines) in;
|
||||||
|
layout(line_strip, max_vertices=MAX_POINTS) out;
|
||||||
|
|
||||||
|
void triangle_head(in vec4 dir, in vec4 side, in float length, in float width, out vec4 head[3]) {
|
||||||
|
vec4 nose = dir * length;
|
||||||
|
vec4 ear = side * width;
|
||||||
|
head[0] = vec4(0);
|
||||||
|
head[1] = nose * .5 + ear;
|
||||||
|
head[2] = nose;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 clip2win = matCLIP2WIN();
|
||||||
|
vec4 win2clip = matWIN2CLIP();
|
||||||
|
|
||||||
|
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
||||||
|
|
||||||
|
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
|
||||||
|
vec4 edge = p1w - p0w, dir = normalize(edge);
|
||||||
|
vec4 gap = dir * TRIANGLE_L * .5;
|
||||||
|
vec4 side = vec4(cross(vec3(dir.xy, 0), vec3(0, 0, 1)).xy, 0, 0);
|
||||||
|
vec4 p;
|
||||||
|
|
||||||
|
vec4 head[CIRCLE_SEGS];
|
||||||
|
circle_head(CIRCLE_SIZE, head);
|
||||||
|
|
||||||
|
vec4 head3[3];
|
||||||
|
|
||||||
|
// start edge circle
|
||||||
|
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||||
|
p = p0w + gap + head[i];
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
|
EmitVertex();
|
||||||
|
}
|
||||||
|
p = p0w + gap + head[0];
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
// start edge triangle
|
||||||
|
triangle_head(dir, -side, TRIANGLE_L, TRIANGLE_W, head3);
|
||||||
|
p = p0w + head3[0];
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
|
EmitVertex();
|
||||||
|
p = p0w + head3[1];
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
|
EmitVertex();
|
||||||
|
p = p0w + head3[2];
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
// end edge circle
|
||||||
|
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||||
|
p = p1w - gap + head[i];
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
|
EmitVertex();
|
||||||
|
}
|
||||||
|
p = p1w - gap + head[0];
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
// end edge triangle
|
||||||
|
triangle_head(-dir, -side, TRIANGLE_L, TRIANGLE_W, head3);
|
||||||
|
p = p1w + head3[0];
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
|
EmitVertex();
|
||||||
|
p = p1w + head3[1];
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
|
EmitVertex();
|
||||||
|
p = p1w + head3[2];
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
// stem
|
||||||
|
gl_Position = p0;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p1;
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def decorate(self, context, obj):
|
||||||
|
verts, _, _ = self.get_path_geom(obj, topo=False)
|
||||||
|
self.draw_lines(context, obj, verts, [(0, 1)])
|
||||||
|
|
||||||
|
|
||||||
class DecorationsHandler():
|
class DecorationsHandler():
|
||||||
decorators_classes = [
|
decorators_classes = [
|
||||||
DimensionDecorator,
|
DimensionDecorator,
|
||||||
@@ -1004,10 +1107,11 @@ class DecorationsHandler():
|
|||||||
HiddenDecorator,
|
HiddenDecorator,
|
||||||
LeaderDecorator,
|
LeaderDecorator,
|
||||||
MiscDecorator,
|
MiscDecorator,
|
||||||
PlanDecorator,
|
PlanLevelDecorator,
|
||||||
SectionDecorator,
|
SectionLevelDecorator,
|
||||||
StairDecorator,
|
StairDecorator,
|
||||||
BreakDecorator
|
BreakDecorator,
|
||||||
|
SectionViewDecorator
|
||||||
]
|
]
|
||||||
|
|
||||||
installed = None
|
installed = None
|
||||||
@@ -1031,11 +1135,9 @@ class DecorationsHandler():
|
|||||||
self.decorators = [cls() for cls in self.decorators_classes]
|
self.decorators = [cls() for cls in self.decorators_classes]
|
||||||
|
|
||||||
def __call__(self, context):
|
def __call__(self, context):
|
||||||
props = context.scene.DocProperties
|
collection, _ = helper.get_active_drawing(context.scene)
|
||||||
if props.active_drawing_index is None or len(props.drawings) == 0:
|
if collection is None:
|
||||||
return
|
return
|
||||||
drawing = props.drawings[props.active_drawing_index]
|
|
||||||
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
|
|
||||||
|
|
||||||
for decorator in self.decorators:
|
for decorator in self.decorators:
|
||||||
for obj in decorator.get_objects(collection):
|
for obj in decorator.get_objects(collection):
|
||||||
|
|||||||
@@ -299,6 +299,27 @@ def parse_diagram_scale(camera):
|
|||||||
return float(numerator) / float(denominator)
|
return float(numerator) / float(denominator)
|
||||||
|
|
||||||
|
|
||||||
|
def get_project_collection(scene):
|
||||||
|
"""Get main project collection"""
|
||||||
|
|
||||||
|
colls = [c for c in scene.collection.children if c.name.startswith('IfcProject')]
|
||||||
|
if len(colls) != 1:
|
||||||
|
raise RuntimeError("project collection missing or not unique")
|
||||||
|
return colls[0]
|
||||||
|
|
||||||
|
|
||||||
|
def get_active_drawing(scene):
|
||||||
|
"""Get active drawing collection and camera"""
|
||||||
|
props = scene.DocProperties
|
||||||
|
if props.active_drawing_index is None or len(props.drawings) == 0:
|
||||||
|
return None, None
|
||||||
|
try:
|
||||||
|
drawing = props.drawings[props.active_drawing_index]
|
||||||
|
return scene.collection.children['Views'].children[f"IfcGroup/{drawing.name}"], drawing.camera
|
||||||
|
except (KeyError, IndexError):
|
||||||
|
raise RuntimeError("missing drawing collection")
|
||||||
|
|
||||||
|
|
||||||
def ortho_view_frame(camera, margin=0.015):
|
def ortho_view_frame(camera, margin=0.015):
|
||||||
"""Calculates 2d bounding box of camera view area.
|
"""Calculates 2d bounding box of camera view area.
|
||||||
|
|
||||||
@@ -308,7 +329,7 @@ def ortho_view_frame(camera, margin=0.015):
|
|||||||
:type camera: bpy.types.Camera + BIMCameraProperties
|
:type camera: bpy.types.Camera + BIMCameraProperties
|
||||||
:arg margin: margins, in scene units
|
:arg margin: margins, in scene units
|
||||||
:type margin: float
|
:type margin: float
|
||||||
:return: (xmin, xmax, ymin, ymax) in local camera coordinates
|
:return: (xmin, xmax, ymin, ymax, zmin, zmax) in local camera coordinates
|
||||||
"""
|
"""
|
||||||
aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x
|
aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x
|
||||||
size = camera.ortho_scale
|
size = camera.ortho_scale
|
||||||
@@ -317,7 +338,11 @@ def ortho_view_frame(camera, margin=0.015):
|
|||||||
scale = parse_diagram_scale(camera)
|
scale = parse_diagram_scale(camera)
|
||||||
xmarg = margin * scale
|
xmarg = margin * scale
|
||||||
ymarg = margin * scale * aspect
|
ymarg = margin * scale * aspect
|
||||||
return (-hwidth + xmarg, hwidth - xmarg, -hheight + ymarg, hheight - ymarg)
|
return (-hwidth + xmarg, hwidth - xmarg, -hheight + ymarg, hheight - ymarg, -camera.clip_start, -camera.clip_end)
|
||||||
|
|
||||||
|
|
||||||
|
def almost_zero(v):
|
||||||
|
return abs(v) < 1e-5
|
||||||
|
|
||||||
|
|
||||||
def clip_segment(bounds, segm):
|
def clip_segment(bounds, segm):
|
||||||
@@ -329,11 +354,11 @@ def clip_segment(bounds, segm):
|
|||||||
"""
|
"""
|
||||||
# Liang–Barsky algorithm
|
# Liang–Barsky algorithm
|
||||||
|
|
||||||
xmin, xmax, ymin, ymax = bounds
|
xmin, xmax, ymin, ymax, _, _ = bounds
|
||||||
p1, p2 = segm
|
p1, p2 = segm
|
||||||
|
|
||||||
def clip_side(p, q):
|
def clip_side(p, q):
|
||||||
if abs(p) < 1e-10: # ~= 0, parallel to the side
|
if almost_zero(p): # ~= 0, parallel to the side
|
||||||
if q < 0:
|
if q < 0:
|
||||||
return None # outside
|
return None # outside
|
||||||
else:
|
else:
|
||||||
@@ -368,3 +393,19 @@ def clip_segment(bounds, segm):
|
|||||||
p2c = p1 + dlt * t2
|
p2c = p1 + dlt * t2
|
||||||
|
|
||||||
return p1c, p2c
|
return p1c, p2c
|
||||||
|
|
||||||
|
|
||||||
|
def elevate_segment(bounds, segm):
|
||||||
|
"""Elevate line xy-perpendicular segment vertically
|
||||||
|
|
||||||
|
: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
|
||||||
|
"""
|
||||||
|
_, _, ymin, ymax, zmin, _ = bounds
|
||||||
|
p1, p2 = segm
|
||||||
|
dlt = p2 - p1
|
||||||
|
if not (almost_zero(dlt.x) and almost_zero(dlt.y)):
|
||||||
|
return None
|
||||||
|
x = p1.x
|
||||||
|
return [Vector((x, ymin, zmin)), Vector((x, ymax, zmin))]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import math
|
|||||||
import multiprocessing
|
import multiprocessing
|
||||||
import zipfile
|
import zipfile
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import numpy as np
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -85,7 +86,7 @@ class MaterialCreator:
|
|||||||
item_id = self.mesh.BIMMeshProperties.ifc_item_ids.add()
|
item_id = self.mesh.BIMMeshProperties.ifc_item_ids.add()
|
||||||
item_id.name = str(item.id())
|
item_id.name = str(item.id())
|
||||||
|
|
||||||
styled_item = item.StyledByItem[0] # Cardinality is S[0:1]
|
styled_item = item.StyledByItem[0] # Cardinality is S[0:1]
|
||||||
style_name = self.get_surface_style_name(styled_item)
|
style_name = self.get_surface_style_name(styled_item)
|
||||||
|
|
||||||
if not style_name:
|
if not style_name:
|
||||||
@@ -130,7 +131,9 @@ class MaterialCreator:
|
|||||||
material_to_slot[i] = slot_index
|
material_to_slot[i] = slot_index
|
||||||
|
|
||||||
if len(self.mesh.polygons) == len(self.mesh["ios_material_ids"]):
|
if len(self.mesh.polygons) == len(self.mesh["ios_material_ids"]):
|
||||||
material_index = [(material_to_slot[mat_id] if mat_id != -1 else 0) for mat_id in self.mesh["ios_material_ids"]]
|
material_index = [
|
||||||
|
(material_to_slot[mat_id] if mat_id != -1 else 0) for mat_id in self.mesh["ios_material_ids"]
|
||||||
|
]
|
||||||
self.mesh.polygons.foreach_set("material_index", material_index)
|
self.mesh.polygons.foreach_set("material_index", material_index)
|
||||||
|
|
||||||
def canonicalise_material_name(self, name):
|
def canonicalise_material_name(self, name):
|
||||||
@@ -180,86 +183,31 @@ class MaterialCreator:
|
|||||||
def create_single(self, material):
|
def create_single(self, material):
|
||||||
if material.Name not in self.materials:
|
if material.Name not in self.materials:
|
||||||
self.create_new_single(material)
|
self.create_new_single(material)
|
||||||
self.obj.BIMObjectProperties.material_type = "IfcMaterial"
|
|
||||||
self.obj.BIMObjectProperties.material = self.materials[material.Name]
|
|
||||||
|
|
||||||
def create_layer_set(self, layer_set):
|
def create_layer_set(self, layer_set):
|
||||||
props = self.obj.BIMObjectProperties
|
|
||||||
props.material_type = "IfcMaterialLayerSet"
|
|
||||||
props.material_set.name = layer_set.LayerSetName or ""
|
|
||||||
if hasattr(layer_set, "Description"): # IFC2X3 support
|
|
||||||
props.material_set.description = layer_set.Description or ""
|
|
||||||
for layer in layer_set.MaterialLayers:
|
for layer in layer_set.MaterialLayers:
|
||||||
new = props.material_set.material_layers.add()
|
|
||||||
if layer.Material:
|
if layer.Material:
|
||||||
if layer.Material.Name not in self.materials:
|
if layer.Material.Name not in self.materials:
|
||||||
self.create_new_single(layer.Material)
|
self.create_new_single(layer.Material)
|
||||||
new.material = self.materials[layer.Material.Name]
|
|
||||||
new.layer_thickness = layer.LayerThickness
|
|
||||||
new.is_ventilated = "TRUE" if layer.IsVentilated else "FALSE"
|
|
||||||
if not hasattr(layer, "Name"):
|
|
||||||
continue # IFC2X3 support
|
|
||||||
new.name = layer.Name or ""
|
|
||||||
new.description = layer.Description or ""
|
|
||||||
try:
|
|
||||||
new.category = layer.Category if layer.Category else "None"
|
|
||||||
except:
|
|
||||||
new.custom_category = layer.Category or ""
|
|
||||||
new.priority = layer.Priority or 0
|
|
||||||
|
|
||||||
def create_constituent_set(self, constituent_set):
|
def create_constituent_set(self, constituent_set):
|
||||||
props = self.obj.BIMObjectProperties
|
|
||||||
props.material_type = "IfcMaterialConstituentSet"
|
|
||||||
props.material_set.name = constituent_set.Name or ""
|
|
||||||
props.material_set.description = constituent_set.Description or ""
|
|
||||||
for constituent in constituent_set.MaterialConstituents:
|
for constituent in constituent_set.MaterialConstituents:
|
||||||
new = props.material_set.material_constituents.add()
|
|
||||||
new.name = constituent.Name or ""
|
|
||||||
new.description = constituent.Description or ""
|
|
||||||
if constituent.Material.Name not in self.materials:
|
if constituent.Material.Name not in self.materials:
|
||||||
self.create_new_single(constituent.Material)
|
self.create_new_single(constituent.Material)
|
||||||
new.material = self.materials[constituent.Material.Name]
|
|
||||||
new.fraction = constituent.Fraction or 0.0
|
|
||||||
new.category = constituent.Category or ""
|
|
||||||
|
|
||||||
def create_profile_set(self, profile_set):
|
def create_profile_set(self, profile_set):
|
||||||
props = self.obj.BIMObjectProperties
|
|
||||||
props.material_type = "IfcMaterialProfileSet"
|
|
||||||
props.material_set.name = profile_set.Name or ""
|
|
||||||
props.material_set.description = profile_set.Description or ""
|
|
||||||
for profile in profile_set.MaterialProfiles:
|
for profile in profile_set.MaterialProfiles:
|
||||||
new = props.material_set.material_profiles.add()
|
|
||||||
new.name = profile.Name or ""
|
|
||||||
new.description = profile.Description or ""
|
|
||||||
if profile.Material.Name not in self.materials:
|
if profile.Material.Name not in self.materials:
|
||||||
self.create_new_single(profile.Material)
|
self.create_new_single(profile.Material)
|
||||||
new.material = self.materials[profile.Material.Name]
|
|
||||||
try:
|
|
||||||
new.profile = profile.Profile.is_a()
|
|
||||||
for i, attribute in enumerate(profile.Profile):
|
|
||||||
newa = new.profile_attributes.add()
|
|
||||||
newa.name = profile.Profile.attribute_name(i)
|
|
||||||
newa.string_value = str(attribute)
|
|
||||||
except:
|
|
||||||
pass # TODO: currently, only parametric profile sets are supported
|
|
||||||
new.priority = profile.Priority or 0
|
|
||||||
new.category = profile.Category or ""
|
|
||||||
|
|
||||||
def create_material_list(self, material_list):
|
def create_material_list(self, material_list):
|
||||||
props = self.obj.BIMObjectProperties
|
|
||||||
props.material_type = "IfcMaterialConstituentSet" # Constituent sets are the recommended upgrade path
|
|
||||||
for material in material_list.Materials:
|
for material in material_list.Materials:
|
||||||
new = props.material_set.material_constituents.add()
|
|
||||||
if material.Name not in self.materials:
|
if material.Name not in self.materials:
|
||||||
self.create_new_single(material)
|
self.create_new_single(material)
|
||||||
new.material = self.materials[material.Name]
|
|
||||||
|
|
||||||
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())
|
obj.BIMObjectProperties.ifc_definition_id = int(material.id())
|
||||||
self.ifc_importer.add_element_attributes(material, obj.BIMMaterialProperties)
|
|
||||||
for pset in getattr(material, "HasProperties", ()):
|
|
||||||
self.ifc_importer.add_pset(pset, obj.BIMMaterialProperties)
|
|
||||||
if not material.HasRepresentation or not material.HasRepresentation[0].Representations:
|
if not material.HasRepresentation or not material.HasRepresentation[0].Representations:
|
||||||
return
|
return
|
||||||
for representation in material.HasRepresentation[0].Representations:
|
for representation in material.HasRepresentation[0].Representations:
|
||||||
@@ -280,7 +228,7 @@ class MaterialCreator:
|
|||||||
if style.Name:
|
if style.Name:
|
||||||
return style.Name
|
return style.Name
|
||||||
return str(style.id())
|
return str(style.id())
|
||||||
return None # We only support surface styles right now
|
return None # We only support surface styles right now
|
||||||
|
|
||||||
def parse_styled_item(self, styled_item, material):
|
def parse_styled_item(self, styled_item, material):
|
||||||
styles = self.get_styled_item_styles(styled_item)
|
styles = self.get_styled_item_styles(styled_item)
|
||||||
@@ -288,7 +236,6 @@ class MaterialCreator:
|
|||||||
if not style.is_a("IfcSurfaceStyle"):
|
if not style.is_a("IfcSurfaceStyle"):
|
||||||
continue
|
continue
|
||||||
material.BIMMaterialProperties.ifc_style_id = int(style.id())
|
material.BIMMaterialProperties.ifc_style_id = int(style.id())
|
||||||
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"):
|
||||||
alpha = 1.0
|
alpha = 1.0
|
||||||
@@ -301,13 +248,6 @@ class MaterialCreator:
|
|||||||
surface_style.SurfaceColour.Blue,
|
surface_style.SurfaceColour.Blue,
|
||||||
alpha,
|
alpha,
|
||||||
)
|
)
|
||||||
elif surface_style.is_a("IfcExternallyDefinedSurfaceStyle"):
|
|
||||||
external_style = surface_style
|
|
||||||
if external_style:
|
|
||||||
material.BIMMaterialProperties.is_external = True
|
|
||||||
material.BIMMaterialProperties.location = external_style.Location
|
|
||||||
material.BIMMaterialProperties.identification = external_style.Identification
|
|
||||||
material.BIMMaterialProperties.name = external_style.Name
|
|
||||||
|
|
||||||
# IfcPresentationStyleAssignment is deprecated as of IFC4
|
# IfcPresentationStyleAssignment is deprecated as of IFC4
|
||||||
# However it is still widely used thanks to Revit :(
|
# However it is still widely used thanks to Revit :(
|
||||||
@@ -395,8 +335,6 @@ class IfcImporter:
|
|||||||
self.profile_code("Purge diffs")
|
self.profile_code("Purge diffs")
|
||||||
self.load_existing_rooted_elements()
|
self.load_existing_rooted_elements()
|
||||||
self.profile_code("Load existing rooted elements")
|
self.profile_code("Load existing rooted elements")
|
||||||
self.cache_file()
|
|
||||||
self.profile_code("Caching file")
|
|
||||||
self.load_file()
|
self.load_file()
|
||||||
self.profile_code("Loading file")
|
self.profile_code("Loading file")
|
||||||
self.set_ifc_file()
|
self.set_ifc_file()
|
||||||
@@ -406,8 +344,8 @@ class IfcImporter:
|
|||||||
self.profile_code("Set vendor worksarounds")
|
self.profile_code("Set vendor worksarounds")
|
||||||
self.calculate_unit_scale()
|
self.calculate_unit_scale()
|
||||||
self.profile_code("Calculate unit scale")
|
self.profile_code("Calculate unit scale")
|
||||||
self.patch_ifc()
|
self.calculate_model_offset()
|
||||||
self.profile_code("Patching ifc")
|
self.profile_code("Calculate model offset")
|
||||||
self.set_units()
|
self.set_units()
|
||||||
self.profile_code("Set units")
|
self.profile_code("Set units")
|
||||||
self.create_project()
|
self.create_project()
|
||||||
@@ -434,8 +372,6 @@ class IfcImporter:
|
|||||||
if self.ifc_import_settings.should_import_native:
|
if self.ifc_import_settings.should_import_native:
|
||||||
self.parse_native_elements()
|
self.parse_native_elements()
|
||||||
self.profile_code("Parsing native elements")
|
self.profile_code("Parsing native elements")
|
||||||
self.create_georeferencing()
|
|
||||||
self.profile_code("Georeferencing ifc")
|
|
||||||
self.create_groups()
|
self.create_groups()
|
||||||
self.profile_code("Creating groups")
|
self.profile_code("Creating groups")
|
||||||
self.create_grids()
|
self.create_grids()
|
||||||
@@ -443,12 +379,8 @@ class IfcImporter:
|
|||||||
if self.ifc_import_settings.should_import_native:
|
if self.ifc_import_settings.should_import_native:
|
||||||
self.create_native_products()
|
self.create_native_products()
|
||||||
self.profile_code("Creating native products")
|
self.profile_code("Creating native products")
|
||||||
# TODO: Deprecate after bug #682 is fixed and the new importer is stable
|
self.create_products()
|
||||||
if self.ifc_import_settings.should_use_legacy:
|
self.profile_code("Creating meshified products")
|
||||||
self.create_products_legacy()
|
|
||||||
else:
|
|
||||||
self.create_products()
|
|
||||||
self.profile_code("Creating meshified products")
|
|
||||||
self.relate_openings()
|
self.relate_openings()
|
||||||
self.profile_code("Relating openings")
|
self.profile_code("Relating openings")
|
||||||
self.place_objects_in_spatial_tree()
|
self.place_objects_in_spatial_tree()
|
||||||
@@ -471,46 +403,22 @@ class IfcImporter:
|
|||||||
self.profile_code("Create presentation layers")
|
self.profile_code("Create presentation layers")
|
||||||
self.add_project_to_scene()
|
self.add_project_to_scene()
|
||||||
self.profile_code("Add project to scene")
|
self.profile_code("Add project to scene")
|
||||||
if self.ifc_import_settings.should_clean_mesh and len(self.file.by_type("IfcElement")) < 10000:
|
if self.ifc_import_settings.should_clean_mesh and len(self.file.by_type("IfcElement")) < 1000:
|
||||||
self.clean_mesh()
|
self.clean_mesh()
|
||||||
self.profile_code("Mesh cleaning")
|
self.profile_code("Mesh cleaning")
|
||||||
|
|
||||||
def auto_set_workarounds(self):
|
def auto_set_workarounds(self):
|
||||||
if (
|
|
||||||
"DDS-CAD" in self.file.wrapped_data.header.file_name.originating_system
|
|
||||||
or "DDS" in self.file.wrapped_data.header.file_name.preprocessor_version
|
|
||||||
):
|
|
||||||
self.ifc_import_settings.should_reset_absolute_coordinates = True
|
|
||||||
applications = self.file.by_type("IfcApplication")
|
applications = self.file.by_type("IfcApplication")
|
||||||
if not applications:
|
if not applications:
|
||||||
return
|
return
|
||||||
if applications[0].ApplicationIdentifier == "Revit":
|
if "prostructures" in applications[0].ApplicationFullName.lower():
|
||||||
if self.is_ifc_class_far_away("IfcSite"):
|
|
||||||
self.ifc_import_settings.should_ignore_site_coordinates = True
|
|
||||||
self.ifc_import_settings.should_guess_georeferencing = True
|
|
||||||
if self.is_ifc_class_far_away("IfcBuilding"):
|
|
||||||
self.ifc_import_settings.should_ignore_building_coordinates = True
|
|
||||||
self.ifc_import_settings.should_guess_georeferencing = True
|
|
||||||
elif "prostructures" in applications[0].ApplicationFullName.lower():
|
|
||||||
self.ifc_import_settings.should_allow_non_element_aggregates = True
|
self.ifc_import_settings.should_allow_non_element_aggregates = True
|
||||||
elif applications[0].ApplicationFullName.lower() == "12d model":
|
|
||||||
self.ifc_import_settings.should_reset_absolute_coordinates = True
|
|
||||||
elif "Civil 3D" in applications[0].ApplicationFullName:
|
|
||||||
self.ifc_import_settings.should_reset_absolute_coordinates = True
|
|
||||||
elif applications[0].ApplicationFullName == "Tekla Structures":
|
|
||||||
if self.is_ifc_class_far_away("IfcSite"):
|
|
||||||
self.ifc_import_settings.should_ignore_site_coordinates = True
|
|
||||||
|
|
||||||
def is_ifc_class_far_away(self, ifc_class):
|
def is_element_far_away(self, element):
|
||||||
for site in self.file.by_type(ifc_class):
|
try:
|
||||||
if (
|
return self.is_point_far_away(element.ObjectPlacement.RelativePlacement.Location)
|
||||||
not site.ObjectPlacement
|
except:
|
||||||
or not site.ObjectPlacement.RelativePlacement
|
pass
|
||||||
or not site.ObjectPlacement.RelativePlacement.Location
|
|
||||||
):
|
|
||||||
continue
|
|
||||||
if self.is_point_far_away(site.ObjectPlacement.RelativePlacement.Location):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_point_far_away(self, point):
|
def is_point_far_away(self, point):
|
||||||
# Arbitrary threshold based on experience
|
# Arbitrary threshold based on experience
|
||||||
@@ -600,94 +508,94 @@ class IfcImporter:
|
|||||||
products.extend(self.get_products_from_shape_representation(inverse_element))
|
products.extend(self.get_products_from_shape_representation(inverse_element))
|
||||||
return products
|
return products
|
||||||
|
|
||||||
def patch_ifc(self):
|
def calculate_model_offset(self):
|
||||||
project = self.file.by_type("IfcProject")[0]
|
project = self.file.by_type("IfcProject")[0]
|
||||||
if self.ifc_import_settings.should_ignore_site_coordinates:
|
site = self.find_decomposed_ifc_class(project, "IfcSite")
|
||||||
sites = self.find_decomposed_ifc_class(project, "IfcSite")
|
if site and self.is_element_far_away(site[0]):
|
||||||
if self.ifc_import_settings.should_guess_georeferencing and sites:
|
return self.guess_georeferencing(site[0])
|
||||||
self.guess_georeferencing(sites[0])
|
building = self.find_decomposed_ifc_class(project, "IfcBuilding")
|
||||||
for site in sites:
|
if building and self.is_element_far_away(building[0]):
|
||||||
self.patch_placement_to_origin(site)
|
return self.guess_georeferencing(building[0])
|
||||||
if self.ifc_import_settings.should_ignore_building_coordinates:
|
return self.guess_absolute_coordinate()
|
||||||
buildings = self.find_decomposed_ifc_class(project, "IfcBuilding")
|
|
||||||
if self.ifc_import_settings.should_guess_georeferencing and buildings:
|
|
||||||
self.guess_georeferencing(buildings[0])
|
|
||||||
for building in buildings:
|
|
||||||
self.patch_placement_to_origin(building)
|
|
||||||
if self.ifc_import_settings.should_reset_absolute_coordinates:
|
|
||||||
self.reset_absolute_coordinates()
|
|
||||||
|
|
||||||
def guess_georeferencing(self, element):
|
def guess_georeferencing(self, element):
|
||||||
if not element.ObjectPlacement.is_a("IfcLocalPlacement"):
|
if not element.ObjectPlacement.is_a("IfcLocalPlacement"):
|
||||||
return
|
return
|
||||||
placement = element.ObjectPlacement.RelativePlacement
|
placement = element.ObjectPlacement.RelativePlacement
|
||||||
bpy.context.scene.MapConversion.eastings = str(placement.Location.Coordinates[0])
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
bpy.context.scene.MapConversion.northings = str(placement.Location.Coordinates[1])
|
props.blender_eastings = str(placement.Location.Coordinates[0])
|
||||||
bpy.context.scene.MapConversion.orthogonal_height = str(placement.Location.Coordinates[2])
|
props.blender_northings = str(placement.Location.Coordinates[1])
|
||||||
|
props.blender_orthogonal_height = str(placement.Location.Coordinates[2])
|
||||||
if placement.RefDirection:
|
if placement.RefDirection:
|
||||||
bpy.context.scene.MapConversion.x_axis_abscissa = str(placement.RefDirection.DirectionRatios[0])
|
props.blender_x_axis_abscissa = str(placement.RefDirection.DirectionRatios[0])
|
||||||
bpy.context.scene.MapConversion.x_axis_ordinate = str(placement.RefDirection.DirectionRatios[1] * -1)
|
props.blender_x_axis_ordinate = str(placement.RefDirection.DirectionRatios[1])
|
||||||
bpy.context.scene.MapConversion.scale = "1"
|
props.has_blender_offset = True
|
||||||
|
props.blender_offset_type = "OBJECT_PLACEMENT"
|
||||||
|
|
||||||
def reset_absolute_coordinates(self):
|
def guess_absolute_coordinate(self):
|
||||||
# 12D can have some funky coordinates out of any sensible range. This
|
# Civil BIM applications like to work in absolute coordinates, where the ObjectPlacement is 0,0,0 but each
|
||||||
# method will not work all the time, but will catch most issues.
|
# individual coordinate of the shape representation is in absolute values.
|
||||||
|
offset_point = self.get_offset_point()
|
||||||
|
if not offset_point:
|
||||||
|
return
|
||||||
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
|
props.blender_eastings = str(offset_point[0])
|
||||||
|
props.blender_northings = str(offset_point[1])
|
||||||
|
props.blender_orthogonal_height = str(offset_point[2])
|
||||||
|
props.has_blender_offset = True
|
||||||
|
props.blender_offset_type = "CARTESIAN_POINT"
|
||||||
|
|
||||||
|
def get_offset_point(self):
|
||||||
offset_point = None
|
offset_point = None
|
||||||
|
elements_checked = 0
|
||||||
|
# If more than these points aren't far away, the file probably isn't absolutely positioned
|
||||||
|
element_checking_threshold = 100
|
||||||
try:
|
try:
|
||||||
point_lists = self.file.by_type("IfcCartesianPointList3D")
|
point_lists = self.file.by_type("IfcCartesianPointList3D")
|
||||||
except:
|
except:
|
||||||
# IFC2X3 does not have IfcCartesianPointList3D
|
# IFC2X3 does not have IfcCartesianPointList3D
|
||||||
point_lists = []
|
point_lists = []
|
||||||
for point_list in point_lists:
|
for point_list in point_lists:
|
||||||
coord_list = [None] * len(point_list.CoordList)
|
elements_checked += 1
|
||||||
|
if elements_checked > element_checking_threshold:
|
||||||
|
return
|
||||||
for i, point in enumerate(point_list.CoordList):
|
for i, point in enumerate(point_list.CoordList):
|
||||||
if len(point) == 2 or not self.is_point_far_away(point):
|
if len(point) == 3 and self.is_point_far_away(point):
|
||||||
coord_list[i] = point
|
return point[0]
|
||||||
continue
|
# offset_point = (point[0], point[1], point[2])
|
||||||
if not offset_point:
|
|
||||||
offset_point = (point[0], point[1], point[2])
|
|
||||||
self.ifc_import_settings.logger.info("Resetting absolute coordinates by %s", point)
|
|
||||||
point = (point[0] - offset_point[0], point[1] - offset_point[1], point[2] - offset_point[2])
|
|
||||||
coord_list[i] = point
|
|
||||||
point_list.CoordList = coord_list
|
|
||||||
for point in self.file.by_type("IfcCartesianPoint"):
|
for point in self.file.by_type("IfcCartesianPoint"):
|
||||||
if len(point.Coordinates) == 2 or not self.is_point_far_away(point):
|
elements_checked += 1
|
||||||
continue
|
if elements_checked > element_checking_threshold:
|
||||||
if not offset_point:
|
return
|
||||||
offset_point = (point.Coordinates[0], point.Coordinates[1], point.Coordinates[2])
|
if len(point.Coordinates) == 3 and self.is_point_far_away(point):
|
||||||
self.ifc_import_settings.logger.info("Resetting absolute coordinates by %s", point)
|
return point[0]
|
||||||
point.Coordinates = (
|
|
||||||
point.Coordinates[0] - offset_point[0],
|
def apply_blender_offset_to_matrix(self, matrix):
|
||||||
point.Coordinates[1] - offset_point[1],
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
point.Coordinates[2] - offset_point[2],
|
if props.has_blender_offset and props.blender_offset_type == "OBJECT_PLACEMENT":
|
||||||
|
test = mathutils.Matrix(
|
||||||
|
ifcopenshell.util.geolocation.global2local(
|
||||||
|
matrix,
|
||||||
|
float(props.blender_eastings) * self.unit_scale,
|
||||||
|
float(props.blender_northings) * self.unit_scale,
|
||||||
|
float(props.blender_orthogonal_height) * self.unit_scale,
|
||||||
|
float(props.blender_x_axis_abscissa),
|
||||||
|
float(props.blender_x_axis_ordinate),
|
||||||
|
).tolist()
|
||||||
)
|
)
|
||||||
if not offset_point:
|
|
||||||
return
|
return mathutils.Matrix(
|
||||||
if self.file.wrapped_data.schema == "IFC2X3":
|
ifcopenshell.util.geolocation.global2local(
|
||||||
properties = [
|
matrix,
|
||||||
self.file.createIfcPropertySingleValue(
|
float(props.blender_eastings) * self.unit_scale,
|
||||||
"Eastings", None, self.file.createIfcLengthMeasure(offset_point[0])
|
float(props.blender_northings) * self.unit_scale,
|
||||||
),
|
float(props.blender_orthogonal_height) * self.unit_scale,
|
||||||
self.file.createIfcPropertySingleValue(
|
float(props.blender_x_axis_abscissa),
|
||||||
"Northings", None, self.file.createIfcLengthMeasure(offset_point[1])
|
float(props.blender_x_axis_ordinate),
|
||||||
),
|
).tolist()
|
||||||
self.file.createIfcPropertySingleValue(
|
|
||||||
"OrthogonalHeight", None, self.file.createIfcLengthMeasure(offset_point[2])
|
|
||||||
),
|
|
||||||
]
|
|
||||||
history = self.file.createIfcOwnerHistory()
|
|
||||||
pset = self.file.createIfcPropertySet(
|
|
||||||
ifcopenshell.guid.new(), history, "EPset_MapConversion", None, properties
|
|
||||||
)
|
)
|
||||||
self.file.createIfcRelDefinesByProperties(
|
return mathutils.Matrix(matrix.tolist())
|
||||||
ifcopenshell.guid.new(), history, None, None, self.file.by_type("IfcSite"), pset
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# We don't have the full geolocation information, so we'll add what we can
|
|
||||||
scene = bpy.context.scene
|
|
||||||
scene.MapConversion.eastings = str(offset_point[0])
|
|
||||||
scene.MapConversion.northings = str(offset_point[1])
|
|
||||||
scene.MapConversion.orthogonal_height = str(offset_point[2])
|
|
||||||
|
|
||||||
def find_decomposed_ifc_class(self, element, ifc_class):
|
def find_decomposed_ifc_class(self, element, ifc_class):
|
||||||
results = []
|
results = []
|
||||||
@@ -701,60 +609,6 @@ class IfcImporter:
|
|||||||
results.extend(self.find_decomposed_ifc_class(part, ifc_class))
|
results.extend(self.find_decomposed_ifc_class(part, ifc_class))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def patch_placement_to_origin(self, element):
|
|
||||||
element.ObjectPlacement.RelativePlacement.Location.Coordinates = (0.0, 0.0, 0.0)
|
|
||||||
if element.ObjectPlacement.RelativePlacement.Axis:
|
|
||||||
element.ObjectPlacement.RelativePlacement.Axis.DirectionRatios = (0.0, 0.0, 1.0)
|
|
||||||
if element.ObjectPlacement.RelativePlacement.RefDirection:
|
|
||||||
element.ObjectPlacement.RelativePlacement.RefDirection.DirectionRatios = (1.0, 0.0, 0.0)
|
|
||||||
|
|
||||||
def create_georeferencing(self):
|
|
||||||
try:
|
|
||||||
map_conversion = self.file.by_type("IfcMapConversion")
|
|
||||||
projected_crs = self.file.by_type("IfcProjectedCRS")
|
|
||||||
if not map_conversion or not projected_crs:
|
|
||||||
return
|
|
||||||
except:
|
|
||||||
return # For example, in IFC2X3
|
|
||||||
map_conversion = map_conversion[0]
|
|
||||||
projected_crs = projected_crs[0]
|
|
||||||
scene = bpy.context.scene
|
|
||||||
scene.BIMProperties.has_georeferencing = True
|
|
||||||
map_conversion_map = {
|
|
||||||
"Eastings": "eastings",
|
|
||||||
"Northings": "northings",
|
|
||||||
"OrthogonalHeight": "orthogonal_height",
|
|
||||||
"XAxisAbscissa": "x_axis_abscissa",
|
|
||||||
"XAxisOrdinate": "x_axis_ordinate",
|
|
||||||
"Scale": "scale",
|
|
||||||
}
|
|
||||||
target_crs_map = {
|
|
||||||
"Name": "name",
|
|
||||||
"Description": "description",
|
|
||||||
"GeodeticDatum": "geodetic_datum",
|
|
||||||
"VerticalDatum": "vertical_datum",
|
|
||||||
"MapProjection": "map_projection",
|
|
||||||
"MapZone": "map_zone",
|
|
||||||
"MapUnit": "map_unit",
|
|
||||||
}
|
|
||||||
for keyA, keyB in map_conversion_map.items():
|
|
||||||
value = getattr(map_conversion, keyA)
|
|
||||||
if value is not None:
|
|
||||||
setattr(scene.MapConversion, keyB, str(value))
|
|
||||||
for keyA, keyB in target_crs_map.items():
|
|
||||||
value = getattr(projected_crs, keyA)
|
|
||||||
if value is not None:
|
|
||||||
if keyA == "MapUnit":
|
|
||||||
value = self.get_unit_name(value)
|
|
||||||
setattr(scene.TargetCRS, keyB, str(value))
|
|
||||||
|
|
||||||
def get_unit_name(self, named_unit):
|
|
||||||
name = ""
|
|
||||||
if hasattr(named_unit, "Prefix") and named_unit.Prefix:
|
|
||||||
name += named_unit.Prefix
|
|
||||||
name += named_unit.Name
|
|
||||||
return name
|
|
||||||
|
|
||||||
def create_groups(self):
|
def create_groups(self):
|
||||||
group_collection = None
|
group_collection = None
|
||||||
for collection in self.project["blender"].children:
|
for collection in self.project["blender"].children:
|
||||||
@@ -773,7 +627,6 @@ class IfcImporter:
|
|||||||
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()
|
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||||
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}
|
||||||
|
|
||||||
@@ -811,7 +664,6 @@ class IfcImporter:
|
|||||||
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.BIMObjectProperties.ifc_definition_id = element.id()
|
||||||
obj.matrix_world = matrix_world
|
obj.matrix_world = matrix_world
|
||||||
self.add_element_attributes(axis, obj.BIMObjectProperties)
|
|
||||||
grid.objects.link(obj)
|
grid.objects.link(obj)
|
||||||
|
|
||||||
def create_type_products(self):
|
def create_type_products(self):
|
||||||
@@ -863,11 +715,6 @@ class IfcImporter:
|
|||||||
):
|
):
|
||||||
return representation_map
|
return representation_map
|
||||||
|
|
||||||
def create_products_legacy(self):
|
|
||||||
elements = self.file.by_type("IfcElement") + self.file.by_type("IfcSpace")
|
|
||||||
for element in elements:
|
|
||||||
self.create_product_legacy(element)
|
|
||||||
|
|
||||||
def create_native_products(self):
|
def create_native_products(self):
|
||||||
if not self.native_elements:
|
if not self.native_elements:
|
||||||
return
|
return
|
||||||
@@ -962,20 +809,19 @@ class IfcImporter:
|
|||||||
|
|
||||||
if shape:
|
if shape:
|
||||||
m = shape.transformation.matrix.data
|
m = shape.transformation.matrix.data
|
||||||
mat = mathutils.Matrix(
|
# We use numpy here because Blender mathutils.Matrix is not accurate enough
|
||||||
([m[0], m[1], m[2], 0], [m[3], m[4], m[5], 0], [m[6], m[7], m[8], 0], [m[9], m[10], m[11], 1])
|
mat = np.matrix(
|
||||||
|
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
|
||||||
)
|
)
|
||||||
mat.transpose()
|
obj.matrix_world = self.apply_blender_offset_to_matrix(mat)
|
||||||
obj.matrix_world = mat
|
|
||||||
self.material_creator.create(element, obj, mesh)
|
self.material_creator.create(element, obj, mesh)
|
||||||
elif hasattr(element, "ObjectPlacement"):
|
elif hasattr(element, "ObjectPlacement"):
|
||||||
obj.matrix_world = self.get_element_matrix(element)
|
obj.matrix_world = self.apply_blender_offset_to_matrix(self.get_element_matrix(element))
|
||||||
|
|
||||||
self.add_element_representation_items(element, obj)
|
self.add_element_representation_items(element, 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_opening_relation(element, obj)
|
self.add_opening_relation(element, obj)
|
||||||
self.add_product_definitions(element, obj)
|
|
||||||
self.added_data[element.GlobalId] = obj
|
self.added_data[element.GlobalId] = obj
|
||||||
|
|
||||||
if element.is_a("IfcOpeningElement"):
|
if element.is_a("IfcOpeningElement"):
|
||||||
@@ -1336,7 +1182,7 @@ class IfcImporter:
|
|||||||
obj = self.added_data[product.GlobalId]
|
obj = self.added_data[product.GlobalId]
|
||||||
obj.data.BIMMeshProperties.presentation_layer_index = layer_index
|
obj.data.BIMMeshProperties.presentation_layer_index = layer_index
|
||||||
except:
|
except:
|
||||||
pass # Occurs for example in opening elements or exclusions
|
pass # Occurs for example in opening elements or exclusions
|
||||||
|
|
||||||
def clean_mesh(self):
|
def clean_mesh(self):
|
||||||
obj = None
|
obj = None
|
||||||
@@ -1355,64 +1201,6 @@ 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_definitions(self, element, obj):
|
|
||||||
if not hasattr(element, "IsDefinedBy") or not element.IsDefinedBy:
|
|
||||||
return
|
|
||||||
for definition in element.IsDefinedBy:
|
|
||||||
if not definition.is_a("IfcRelDefinesByProperties"):
|
|
||||||
continue
|
|
||||||
if definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"):
|
|
||||||
self.add_qto(definition.RelatingPropertyDefinition, obj)
|
|
||||||
|
|
||||||
def add_pset(self, pset, props):
|
|
||||||
new_pset = props.psets.add()
|
|
||||||
new_pset.name = pset.Name
|
|
||||||
pset_template = schema.ifc.psetqto.get_by_name(new_pset.name)
|
|
||||||
if pset_template:
|
|
||||||
for prop_name in (p.Name for p in pset_template.HasPropertyTemplates):
|
|
||||||
prop = new_pset.properties.add()
|
|
||||||
prop.name = prop_name
|
|
||||||
try:
|
|
||||||
if hasattr(pset, "HasProperties"):
|
|
||||||
props = pset.HasProperties
|
|
||||||
elif hasattr(pset, "Properties"):
|
|
||||||
props = pset.Properties
|
|
||||||
except:
|
|
||||||
return # I've seen ArchiCAD produce invalid IFCs with empty data
|
|
||||||
# Invalid IFC, but some vendors like Solidworks do this so we accomodate it
|
|
||||||
if not props:
|
|
||||||
return
|
|
||||||
for prop in props:
|
|
||||||
if prop.is_a("IfcPropertySingleValue") and prop.NominalValue:
|
|
||||||
index = new_pset.properties.find(prop.Name)
|
|
||||||
if index >= 0:
|
|
||||||
new_pset.properties[index].string_value = str(prop.NominalValue.wrappedValue)
|
|
||||||
else:
|
|
||||||
new_prop = new_pset.properties.add()
|
|
||||||
new_prop.name = prop.Name
|
|
||||||
new_prop.string_value = str(prop.NominalValue.wrappedValue)
|
|
||||||
|
|
||||||
def add_qto(self, qto, obj):
|
|
||||||
new_qto = obj.BIMObjectProperties.qtos.add()
|
|
||||||
new_qto.name = str(qto.Name)
|
|
||||||
qto_template = schema.ifc.psetqto.get_by_name(new_qto.name)
|
|
||||||
if qto_template:
|
|
||||||
for prop_name in (p.Name for p in qto_template.HasPropertyTemplates):
|
|
||||||
prop = new_qto.properties.add()
|
|
||||||
prop.name = prop_name
|
|
||||||
for prop in qto.Quantities:
|
|
||||||
if prop.is_a("IfcPhysicalSimpleQuantity"):
|
|
||||||
value = getattr(prop, "{}Value".format(prop.is_a()[len("IfcQuantity") :]))
|
|
||||||
if not value:
|
|
||||||
continue
|
|
||||||
index = new_qto.properties.find(prop.Name)
|
|
||||||
if index >= 0:
|
|
||||||
new_qto.properties[index].string_value = str(value)
|
|
||||||
else:
|
|
||||||
new_prop = new_qto.properties.add()
|
|
||||||
new_prop.name = prop.Name
|
|
||||||
new_prop.string_value = str(value)
|
|
||||||
|
|
||||||
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
|
||||||
@@ -1429,15 +1217,6 @@ class IfcImporter:
|
|||||||
with open(self.ifc_import_settings.diff_file, "r") as file:
|
with open(self.ifc_import_settings.diff_file, "r") as file:
|
||||||
self.diff = json.load(file)
|
self.diff = json.load(file)
|
||||||
|
|
||||||
def cache_file(self):
|
|
||||||
destination = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", "ifc")
|
|
||||||
copythread = FileCopy(self.ifc_import_settings.input_file, destination)
|
|
||||||
bpy.context.scene.BIMProperties.ifc_cache = os.path.join(
|
|
||||||
destination, os.path.basename(self.ifc_import_settings.input_file)
|
|
||||||
)
|
|
||||||
copythread.start()
|
|
||||||
copythread.join()
|
|
||||||
|
|
||||||
def load_file(self):
|
def load_file(self):
|
||||||
self.ifc_import_settings.logger.info("loading file %s", self.ifc_import_settings.input_file)
|
self.ifc_import_settings.logger.info("loading file %s", self.ifc_import_settings.input_file)
|
||||||
extension = self.ifc_import_settings.input_file.split(".")[-1]
|
extension = self.ifc_import_settings.input_file.split(".")[-1]
|
||||||
@@ -1708,7 +1487,6 @@ class IfcImporter:
|
|||||||
|
|
||||||
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_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
|
||||||
|
|
||||||
@@ -1731,47 +1509,6 @@ class IfcImporter:
|
|||||||
objects_to_purge.append(obj)
|
objects_to_purge.append(obj)
|
||||||
bpy.ops.object.delete({"selected_objects": objects_to_purge})
|
bpy.ops.object.delete({"selected_objects": objects_to_purge})
|
||||||
|
|
||||||
def create_product_legacy(self, element):
|
|
||||||
if (
|
|
||||||
self.diff
|
|
||||||
and element.GlobalId not in self.diff["added"]
|
|
||||||
and element.GlobalId not in self.diff["changed"].keys()
|
|
||||||
):
|
|
||||||
return
|
|
||||||
|
|
||||||
self.ifc_import_settings.logger.info("Creating object %s", element)
|
|
||||||
self.time = time.time()
|
|
||||||
if element.is_a("IfcOpeningElement"):
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
representation_id = self.get_representation_id(element)
|
|
||||||
|
|
||||||
mesh_name = "mesh-{}".format(representation_id)
|
|
||||||
mesh = self.meshes.get(mesh_name)
|
|
||||||
if mesh is None or representation_id is None:
|
|
||||||
shape = ifcopenshell.geom.create_shape(self.settings, element)
|
|
||||||
self.ifc_import_settings.logger.info("Shape was generated in %.2f", time.time() - self.time)
|
|
||||||
self.time = time.time()
|
|
||||||
|
|
||||||
mesh = self.create_mesh(element, shape)
|
|
||||||
self.meshes[mesh_name] = mesh
|
|
||||||
self.mesh_shapes[mesh_name] = shape
|
|
||||||
else:
|
|
||||||
self.ifc_import_settings.logger.info("Mesh reused.")
|
|
||||||
except:
|
|
||||||
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
|
|
||||||
return
|
|
||||||
|
|
||||||
obj = bpy.data.objects.new(self.get_name(element), mesh)
|
|
||||||
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
|
||||||
self.material_creator.create(element, obj, mesh)
|
|
||||||
obj.matrix_world = self.get_element_matrix(element, mesh_name)
|
|
||||||
self.add_element_classifications(element, obj)
|
|
||||||
self.add_element_document_relations(element, obj)
|
|
||||||
self.add_product_definitions(element, obj)
|
|
||||||
self.added_data[element.GlobalId] = obj
|
|
||||||
|
|
||||||
def add_element_document_relations(self, element, obj):
|
def add_element_document_relations(self, element, obj):
|
||||||
for association in element.HasAssociations:
|
for association in element.HasAssociations:
|
||||||
if association.is_a("IfcRelAssociatesDocument"):
|
if association.is_a("IfcRelAssociatesDocument"):
|
||||||
@@ -1812,7 +1549,7 @@ class IfcImporter:
|
|||||||
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)
|
||||||
if grid_collection: # Just in case we ran into invalid grids from Revit
|
if grid_collection: # Just in case we ran into invalid grids from Revit
|
||||||
self.spatial_structure_elements[container.GlobalId]["blender"].children.link(grid_collection)
|
self.spatial_structure_elements[container.GlobalId]["blender"].children.link(grid_collection)
|
||||||
grid_collection.objects.link(obj)
|
grid_collection.objects.link(obj)
|
||||||
else:
|
else:
|
||||||
@@ -1852,21 +1589,6 @@ class IfcImporter:
|
|||||||
self.ifc_import_settings.logger.warning("Warning: this object is outside the spatial hierarchy %s", element)
|
self.ifc_import_settings.logger.warning("Warning: this object is outside the spatial hierarchy %s", element)
|
||||||
bpy.context.scene.collection.objects.link(obj)
|
bpy.context.scene.collection.objects.link(obj)
|
||||||
|
|
||||||
def add_element_attributes(self, element, props):
|
|
||||||
attributes = element.get_info()
|
|
||||||
for key, value in attributes.items():
|
|
||||||
if (
|
|
||||||
value is None
|
|
||||||
or isinstance(value, (tuple, ifcopenshell.entity_instance))
|
|
||||||
or key == "id"
|
|
||||||
or key == "type"
|
|
||||||
):
|
|
||||||
continue
|
|
||||||
attribute = props.attributes.add()
|
|
||||||
attribute.name = key
|
|
||||||
attribute.data_type = "string"
|
|
||||||
attribute.string_value = str(self.cast_edge_case_attribute(element.is_a(), key, value))
|
|
||||||
|
|
||||||
def cast_edge_case_attribute(self, ifc_class, key, value):
|
def cast_edge_case_attribute(self, ifc_class, key, value):
|
||||||
if key == "RefLatitude" or key == "RefLongitude":
|
if key == "RefLatitude" or key == "RefLongitude":
|
||||||
return ifcopenshell.util.geolocation.dms2dd(*value)
|
return ifcopenshell.util.geolocation.dms2dd(*value)
|
||||||
@@ -1904,38 +1626,11 @@ class IfcImporter:
|
|||||||
return self.get_referenced_source_name(element.ReferencedSource)
|
return self.get_referenced_source_name(element.ReferencedSource)
|
||||||
|
|
||||||
def get_element_matrix(self, element, mesh_name=None):
|
def get_element_matrix(self, element, mesh_name=None):
|
||||||
element_matrix = self.get_local_placement(element.ObjectPlacement)
|
result = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||||
|
result[0][3] *= self.unit_scale
|
||||||
if mesh_name:
|
result[1][3] *= self.unit_scale
|
||||||
# Blender supports reusing a mesh with a different transformation
|
result[2][3] *= self.unit_scale
|
||||||
# applied at the object level. In contrast, IFC supports reusing a mesh
|
return result
|
||||||
# with a different transformation applied at the mesh level _as well as_
|
|
||||||
# the object level. For this reason, if the end-goal is to re-use mesh
|
|
||||||
# data, we must combine IFC's mesh-level transformation into Blender's
|
|
||||||
# object level transformation.
|
|
||||||
|
|
||||||
# The first step to do this is to _undo_ the mesh-level transformation
|
|
||||||
# from whatever shared mesh we are using, as it is not necessarily the
|
|
||||||
# same as the current mesh.
|
|
||||||
shared_shape_transformation = self.get_representation_cartesian_transformation(
|
|
||||||
self.file.by_id(self.mesh_shapes[mesh_name].product.id())
|
|
||||||
)
|
|
||||||
if shared_shape_transformation:
|
|
||||||
shared_transform = self.get_cartesiantransformationoperator(shared_shape_transformation)
|
|
||||||
shared_transform.invert()
|
|
||||||
element_matrix = element_matrix @ shared_transform
|
|
||||||
|
|
||||||
# The next step is to apply the current element's mesh level
|
|
||||||
# transformation to our current element's object transformation
|
|
||||||
transformation = self.get_representation_cartesian_transformation(element)
|
|
||||||
if transformation:
|
|
||||||
element_matrix = self.get_cartesiantransformationoperator(transformation) @ element_matrix
|
|
||||||
|
|
||||||
element_matrix[0][3] *= self.unit_scale
|
|
||||||
element_matrix[1][3] *= self.unit_scale
|
|
||||||
element_matrix[2][3] *= self.unit_scale
|
|
||||||
|
|
||||||
return element_matrix
|
|
||||||
|
|
||||||
def get_body_representations(self, representations, matrix=None):
|
def get_body_representations(self, representations, matrix=None):
|
||||||
if matrix is None:
|
if matrix is None:
|
||||||
@@ -2005,11 +1700,29 @@ class IfcImporter:
|
|||||||
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
|
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
|
||||||
else:
|
else:
|
||||||
representation_id = int(re.sub(r"\D", "", representation_id))
|
representation_id = int(re.sub(r"\D", "", representation_id))
|
||||||
mesh = bpy.data.meshes.new("{}/{}".format(
|
mesh = bpy.data.meshes.new(
|
||||||
self.file.by_id(representation_id).ContextOfItems.id(), geometry.id))
|
"{}/{}".format(self.file.by_id(representation_id).ContextOfItems.id(), geometry.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
|
if props.has_blender_offset and props.blender_offset_type == "CARTESIAN_POINT":
|
||||||
|
ordinate_index = 0
|
||||||
|
verts = [None] * len(geometry.verts)
|
||||||
|
offset_point = (
|
||||||
|
float(props.blender_eastings),
|
||||||
|
float(props.blender_northings),
|
||||||
|
float(props.blender_orthogonal_height),
|
||||||
|
)
|
||||||
|
for i, vert in enumerate(geometry.verts):
|
||||||
|
if ordinate_index > 2:
|
||||||
|
ordinate_index = 0
|
||||||
|
verts[i] = vert - offset_point[ordinate_index]
|
||||||
|
ordinate_index += 1
|
||||||
|
else:
|
||||||
|
verts = geometry.verts
|
||||||
|
|
||||||
if geometry.faces:
|
if geometry.faces:
|
||||||
num_vertices = len(geometry.verts) // 3
|
num_vertices = len(verts) // 3
|
||||||
total_faces = len(geometry.faces)
|
total_faces = len(geometry.faces)
|
||||||
loop_start = range(0, total_faces, 3)
|
loop_start = range(0, total_faces, 3)
|
||||||
num_loops = total_faces // 3
|
num_loops = total_faces // 3
|
||||||
@@ -2021,11 +1734,11 @@ class IfcImporter:
|
|||||||
# Potentially, there is a smarter way to do this. See #1047
|
# Potentially, there is a smarter way to do this. See #1047
|
||||||
v_index = cycle((0, 1, 2))
|
v_index = cycle((0, 1, 2))
|
||||||
verts = [
|
verts = [
|
||||||
v + self.ifc_import_settings.model_offset_coordinates[next(v_index)] for v in geometry.verts
|
v + self.ifc_import_settings.model_offset_coordinates[next(v_index)] for v in verts
|
||||||
]
|
]
|
||||||
mesh.vertices.foreach_set("co", verts)
|
mesh.vertices.foreach_set("co", verts)
|
||||||
else:
|
else:
|
||||||
mesh.vertices.foreach_set("co", geometry.verts)
|
mesh.vertices.foreach_set("co", verts)
|
||||||
mesh.loops.add(num_vertex_indices)
|
mesh.loops.add(num_vertex_indices)
|
||||||
mesh.loops.foreach_set("vertex_index", geometry.faces)
|
mesh.loops.foreach_set("vertex_index", geometry.faces)
|
||||||
mesh.polygons.add(num_loops)
|
mesh.polygons.add(num_loops)
|
||||||
@@ -2034,7 +1747,7 @@ class IfcImporter:
|
|||||||
mesh.update()
|
mesh.update()
|
||||||
else:
|
else:
|
||||||
e = geometry.edges
|
e = geometry.edges
|
||||||
v = geometry.verts
|
v = verts
|
||||||
vertices = [[v[i], v[i + 1], v[i + 2]] for i in range(0, len(v), 3)]
|
vertices = [[v[i], v[i + 1], v[i + 2]] for i in range(0, len(v), 3)]
|
||||||
edges = [[e[i], e[i + 1]] for i in range(0, len(e), 2)]
|
edges = [[e[i], e[i + 1]] for i in range(0, len(e), 2)]
|
||||||
mesh.from_pydata(vertices, edges, [])
|
mesh.from_pydata(vertices, edges, [])
|
||||||
@@ -2051,6 +1764,7 @@ class IfcImporter:
|
|||||||
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)
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
print(traceback.format_exc())
|
print(traceback.format_exc())
|
||||||
|
|
||||||
def create_curve(self, geometry):
|
def create_curve(self, geometry):
|
||||||
@@ -2155,7 +1869,6 @@ class IfcImportSettings:
|
|||||||
self.should_auto_set_workarounds = True
|
self.should_auto_set_workarounds = True
|
||||||
self.should_ignore_site_coordinates = False
|
self.should_ignore_site_coordinates = False
|
||||||
self.should_ignore_building_coordinates = False
|
self.should_ignore_building_coordinates = False
|
||||||
self.should_reset_absolute_coordinates = False
|
|
||||||
self.should_merge_materials_by_colour = False
|
self.should_merge_materials_by_colour = False
|
||||||
self.should_import_type_representations = False
|
self.should_import_type_representations = False
|
||||||
self.should_import_curves = False
|
self.should_import_curves = False
|
||||||
@@ -2164,7 +1877,6 @@ class IfcImportSettings:
|
|||||||
self.should_use_cpu_multiprocessing = False
|
self.should_use_cpu_multiprocessing = False
|
||||||
self.should_import_with_profiling = False
|
self.should_import_with_profiling = False
|
||||||
self.should_import_native = False
|
self.should_import_native = False
|
||||||
self.should_use_legacy = False
|
|
||||||
self.should_merge_aggregates = False
|
self.should_merge_aggregates = False
|
||||||
self.should_merge_by_class = False
|
self.should_merge_by_class = False
|
||||||
self.should_merge_by_material = False
|
self.should_merge_by_material = False
|
||||||
@@ -2181,10 +1893,6 @@ class IfcImportSettings:
|
|||||||
settings.diff_file = scene_bim.diff_json_file
|
settings.diff_file = scene_bim.diff_json_file
|
||||||
settings.ifc_import_filter = scene_bim.ifc_import_filter
|
settings.ifc_import_filter = scene_bim.ifc_import_filter
|
||||||
settings.ifc_selector = scene_bim.ifc_selector
|
settings.ifc_selector = scene_bim.ifc_selector
|
||||||
settings.should_ignore_site_coordinates = scene_bim.import_should_ignore_site_coordinates
|
|
||||||
settings.should_ignore_building_coordinates = scene_bim.import_should_ignore_building_coordinates
|
|
||||||
settings.should_reset_absolute_coordinates = scene_bim.import_should_reset_absolute_coordinates
|
|
||||||
settings.should_guess_georeferencing = scene_bim.import_should_guess_georeferencing
|
|
||||||
settings.should_import_type_representations = scene_bim.import_should_import_type_representations
|
settings.should_import_type_representations = scene_bim.import_should_import_type_representations
|
||||||
settings.should_import_curves = scene_bim.import_should_import_curves
|
settings.should_import_curves = scene_bim.import_should_import_curves
|
||||||
settings.should_import_opening_elements = scene_bim.import_should_import_opening_elements
|
settings.should_import_opening_elements = scene_bim.import_should_import_opening_elements
|
||||||
@@ -2194,7 +1902,6 @@ class IfcImportSettings:
|
|||||||
settings.should_import_with_profiling = scene_bim.import_should_import_with_profiling
|
settings.should_import_with_profiling = scene_bim.import_should_import_with_profiling
|
||||||
settings.should_import_native = scene_bim.import_should_import_native
|
settings.should_import_native = scene_bim.import_should_import_native
|
||||||
settings.should_roundtrip_native = scene_bim.import_export_should_roundtrip_native
|
settings.should_roundtrip_native = scene_bim.import_export_should_roundtrip_native
|
||||||
settings.should_use_legacy = scene_bim.import_should_use_legacy
|
|
||||||
settings.should_import_aggregates = scene_bim.import_should_import_aggregates
|
settings.should_import_aggregates = scene_bim.import_should_import_aggregates
|
||||||
settings.should_merge_aggregates = scene_bim.import_should_merge_aggregates
|
settings.should_merge_aggregates = scene_bim.import_should_merge_aggregates
|
||||||
settings.should_merge_by_class = scene_bim.import_should_merge_by_class
|
settings.should_merge_by_class = scene_bim.import_should_merge_by_class
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.AssignObject,
|
||||||
|
operator.EnableEditingAggregate,
|
||||||
|
operator.DisableEditingAggregate,
|
||||||
|
operator.AddAggregate,
|
||||||
|
ui.BIM_PT_aggregate,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
pass
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import ifcopenshell
|
||||||
|
|
||||||
|
|
||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {
|
||||||
|
"product": None,
|
||||||
|
"relating_object": None,
|
||||||
|
}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
decomposes = None
|
||||||
|
if self.settings["product"].Decomposes:
|
||||||
|
decomposes = self.settings["product"].Decomposes[0]
|
||||||
|
|
||||||
|
is_decomposed_by = None
|
||||||
|
for rel in self.settings["relating_object"].IsDecomposedBy:
|
||||||
|
if rel.is_a("IfcRelAggregates"):
|
||||||
|
is_decomposed_by = rel
|
||||||
|
break
|
||||||
|
|
||||||
|
if decomposes and decomposes == is_decomposed_by:
|
||||||
|
return
|
||||||
|
|
||||||
|
if decomposes:
|
||||||
|
related_objects = list(decomposes.RelatedObjects)
|
||||||
|
related_objects.remove(self.settings["product"])
|
||||||
|
if related_objects:
|
||||||
|
decomposes.RelatedObjects = related_objects
|
||||||
|
else:
|
||||||
|
self.file.remove(decomposes)
|
||||||
|
|
||||||
|
if is_decomposed_by:
|
||||||
|
related_objects = list(is_decomposed_by.RelatedObjects)
|
||||||
|
related_objects.append(self.settings["product"])
|
||||||
|
is_decomposed_by.RelatedObjects = related_objects
|
||||||
|
else:
|
||||||
|
is_decomposed_by = self.file.create_entity(
|
||||||
|
"IfcRelAggregates",
|
||||||
|
**{
|
||||||
|
"GlobalId": ifcopenshell.guid.new(),
|
||||||
|
# TODO "OwnerHistory": None
|
||||||
|
"RelatedObjects": [self.settings["product"]],
|
||||||
|
"RelatingObject": self.settings["relating_object"],
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class Data:
|
||||||
|
products = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls, product_id):
|
||||||
|
file = IfcStore.get_file()
|
||||||
|
if not file:
|
||||||
|
return
|
||||||
|
product = file.by_id(product_id)
|
||||||
|
if product.Decomposes and product.Decomposes[0].is_a("IfcRelAggregates"):
|
||||||
|
obj = product.Decomposes[0].RelatingObject
|
||||||
|
cls.products[product_id] = {"type": obj.is_a(), "Name": obj.Name, "id": int(obj.id())}
|
||||||
|
else:
|
||||||
|
cls.products[product_id] = {"type": None, "Name": None, "id": None}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import bpy
|
||||||
|
import blenderbim.bim.module.aggregate.assign_object as assign_object
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.module.aggregate.data import Data
|
||||||
|
|
||||||
|
|
||||||
|
class AssignObject(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.assign_object"
|
||||||
|
bl_label = "Assign Object"
|
||||||
|
relating_object: bpy.props.StringProperty()
|
||||||
|
related_object: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
related_object = bpy.data.objects.get(self.related_object) if self.related_object else bpy.context.active_object
|
||||||
|
props = related_object.BIMObjectProperties
|
||||||
|
relating_object = bpy.data.objects.get(self.relating_object) if self.relating_object else props.relating_object
|
||||||
|
if not relating_object or not relating_object.BIMObjectProperties.ifc_definition_id:
|
||||||
|
return {"FINISHED"}
|
||||||
|
product = self.file.by_id(props.ifc_definition_id)
|
||||||
|
assign_object.Usecase(
|
||||||
|
self.file,
|
||||||
|
{
|
||||||
|
"product": product,
|
||||||
|
"relating_object": self.file.by_id(relating_object.BIMObjectProperties.ifc_definition_id),
|
||||||
|
},
|
||||||
|
).execute()
|
||||||
|
bpy.ops.bim.edit_object_placement(obj=related_object.name)
|
||||||
|
Data.load(props.ifc_definition_id)
|
||||||
|
bpy.ops.bim.disable_editing_aggregate(obj=related_object.name)
|
||||||
|
|
||||||
|
spatial_collection = bpy.data.collections.get(related_object.name)
|
||||||
|
relating_collection = bpy.data.collections.get(relating_object.name)
|
||||||
|
if spatial_collection:
|
||||||
|
self.remove_collection(bpy.context.scene.collection, spatial_collection)
|
||||||
|
for collection in bpy.data.collections:
|
||||||
|
if collection == relating_collection:
|
||||||
|
collection.children.link(spatial_collection)
|
||||||
|
continue
|
||||||
|
self.remove_collection(collection, spatial_collection)
|
||||||
|
else:
|
||||||
|
for collection in related_object.users_collection:
|
||||||
|
collection.objects.unlink(related_object)
|
||||||
|
relating_collection.objects.link(related_object)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def remove_collection(self, parent, child):
|
||||||
|
try:
|
||||||
|
parent.children.unlink(child)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class EnableEditingAggregate(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.enable_editing_aggregate"
|
||||||
|
bl_label = "Enable Editing Aggregate"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.context.active_object.BIMObjectProperties.relating_object = None
|
||||||
|
bpy.context.active_object.BIMObjectProperties.is_editing_aggregate = True
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class DisableEditingAggregate(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.disable_editing_aggregate"
|
||||||
|
bl_label = "Disable Editing Aggregate"
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||||
|
obj.BIMObjectProperties.is_editing_aggregate = False
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class AddAggregate(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.add_aggregate"
|
||||||
|
bl_label = "Add Aggregate"
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||||
|
aggregate_collection = bpy.data.collections.new("IfcElementAssembly/Assembly")
|
||||||
|
bpy.context.scene.collection.children.link(aggregate_collection)
|
||||||
|
aggregate = bpy.data.objects.new("Assembly", None)
|
||||||
|
aggregate_collection.objects.link(aggregate)
|
||||||
|
bpy.ops.bim.assign_class(obj=aggregate.name, ifc_class="IfcElementAssembly")
|
||||||
|
bpy.ops.bim.assign_object(related_object=obj.name, relating_object=aggregate.name)
|
||||||
|
return {"FINISHED"}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
from bpy.types import Panel
|
||||||
|
from blenderbim.bim.module.aggregate.data import Data
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_aggregate(Panel):
|
||||||
|
bl_label = "IFC Aggregation"
|
||||||
|
bl_idname = "BIM_PT_aggregate"
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "object"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
props = context.active_object.BIMObjectProperties
|
||||||
|
if not props.ifc_definition_id:
|
||||||
|
return False
|
||||||
|
if props.ifc_definition_id not in Data.products:
|
||||||
|
Data.load(props.ifc_definition_id)
|
||||||
|
if not Data.products[props.ifc_definition_id]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
props = context.active_object.BIMObjectProperties
|
||||||
|
if not props.ifc_definition_id:
|
||||||
|
return
|
||||||
|
if props.ifc_definition_id not in Data.products:
|
||||||
|
Data.load(props.ifc_definition_id)
|
||||||
|
|
||||||
|
if props.is_editing_aggregate:
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "relating_object", text="")
|
||||||
|
row.operator("bim.assign_object", icon="CHECKMARK", text="")
|
||||||
|
row.operator("bim.disable_editing_aggregate", icon="X", text="")
|
||||||
|
else:
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
name = "{}/{}".format(
|
||||||
|
Data.products[props.ifc_definition_id]["type"], Data.products[props.ifc_definition_id]["Name"]
|
||||||
|
)
|
||||||
|
if name == "None/None":
|
||||||
|
name = "This object is not part of an aggregation"
|
||||||
|
row.label(text=name)
|
||||||
|
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
|
||||||
|
row.operator("bim.add_aggregate", icon="ADD", text="")
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui, prop, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.EnableEditingAttributes,
|
||||||
|
operator.DisableEditingAttributes,
|
||||||
|
operator.EditAttributes,
|
||||||
|
prop.BIMAttributeProperties,
|
||||||
|
ui.BIM_PT_object_attributes,
|
||||||
|
ui.BIM_PT_material_attributes,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.Object.BIMAttributeProperties = bpy.props.PointerProperty(type=prop.BIMAttributeProperties)
|
||||||
|
bpy.types.Material.BIMAttributeProperties = bpy.props.PointerProperty(type=prop.BIMAttributeProperties)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
del bpy.types.Object.BIMAttributeProperties
|
||||||
|
del bpy.types.Material.BIMAttributeProperties
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import ifcopenshell
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class Data:
|
||||||
|
products = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls, product_id):
|
||||||
|
file = IfcStore.get_file()
|
||||||
|
if not file:
|
||||||
|
return
|
||||||
|
product = file.by_id(product_id)
|
||||||
|
cls.products[product_id] = []
|
||||||
|
declaration = IfcStore.get_schema().declaration_by_name(product.is_a())
|
||||||
|
for attribute in declaration.all_attributes():
|
||||||
|
data_type = str(attribute.type_of_attribute())
|
||||||
|
value = getattr(product, attribute.name())
|
||||||
|
list_type = None
|
||||||
|
enum_items = ()
|
||||||
|
if "<entity" in data_type:
|
||||||
|
data_type = "entity"
|
||||||
|
value = None if value is None else str(value)
|
||||||
|
elif "<list" in data_type:
|
||||||
|
if "<entity" in data_type:
|
||||||
|
list_type = "entity"
|
||||||
|
elif "<string>" in data_type:
|
||||||
|
list_type = "string"
|
||||||
|
elif "<real>" in data_type:
|
||||||
|
list_type = "float"
|
||||||
|
elif "<integer>" in data_type:
|
||||||
|
list_type = "integer"
|
||||||
|
data_type = "list"
|
||||||
|
value = None if value is None else str(value)
|
||||||
|
elif "<string>" in data_type:
|
||||||
|
data_type = "string"
|
||||||
|
value = None if value is None else str(value)
|
||||||
|
elif "<real>" in data_type:
|
||||||
|
data_type = "float"
|
||||||
|
value = None if value is None else float(value)
|
||||||
|
elif "<integer>" in data_type:
|
||||||
|
data_type = "integer"
|
||||||
|
value = None if value is None else int(value)
|
||||||
|
elif "<enumeration" in data_type:
|
||||||
|
data_type = "enum"
|
||||||
|
value = None if value is None else str(value)
|
||||||
|
enum_items = attribute.type_of_attribute().declared_type().enumeration_items()
|
||||||
|
cls.products[product_id].append({
|
||||||
|
"name": attribute.name(),
|
||||||
|
"value": value,
|
||||||
|
"type": data_type,
|
||||||
|
"enum_items": enum_items,
|
||||||
|
"list_type": list_type,
|
||||||
|
"is_optional": attribute.optional(),
|
||||||
|
"is_null": getattr(product, attribute.name()) is None
|
||||||
|
})
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
class Usecase():
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {
|
||||||
|
"product": None,
|
||||||
|
"attributes": {}
|
||||||
|
}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
for name, value in self.settings["attributes"].items():
|
||||||
|
setattr(self.settings["product"], name, value)
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import bpy
|
||||||
|
import json
|
||||||
|
import blenderbim.bim.module.attribute.edit_attributes as edit_attributes
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.module.attribute.data import Data
|
||||||
|
|
||||||
|
|
||||||
|
class EnableEditingAttributes(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.enable_editing_attributes"
|
||||||
|
bl_label = "Enable Editing Attributes"
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
|
obj_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
if self.obj_type == "Object":
|
||||||
|
obj = bpy.data.objects.get(self.obj)
|
||||||
|
elif self.obj_type == "Material":
|
||||||
|
obj = bpy.data.materials.get(self.obj)
|
||||||
|
oprops = obj.BIMObjectProperties
|
||||||
|
props = obj.BIMAttributeProperties
|
||||||
|
while len(props.attributes) > 0:
|
||||||
|
props.attributes.remove(0)
|
||||||
|
for attribute in Data.products[oprops.ifc_definition_id]:
|
||||||
|
new = props.attributes.add()
|
||||||
|
if attribute["type"] == "entity":
|
||||||
|
continue
|
||||||
|
new.name = attribute["name"]
|
||||||
|
new.is_null = attribute["is_null"]
|
||||||
|
if attribute["type"] == "string" or attribute["type"] == "list":
|
||||||
|
new.string_value = attribute["value"] or ""
|
||||||
|
elif attribute["type"] == "integer":
|
||||||
|
new.int_value = attribute["value"] or 0
|
||||||
|
elif attribute["type"] == "float":
|
||||||
|
new.float_value = attribute["value"] or 0.
|
||||||
|
elif attribute["type"] == "enum":
|
||||||
|
new.enum_items = json.dumps(attribute["enum_items"])
|
||||||
|
if attribute["value"]:
|
||||||
|
new.enum_value = attribute["value"]
|
||||||
|
props.is_editing_attributes = True
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class DisableEditingAttributes(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.disable_editing_attributes"
|
||||||
|
bl_label = "Disable Editing Attributes"
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
|
obj_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if self.obj_type == "Object":
|
||||||
|
obj = bpy.data.objects.get(self.obj)
|
||||||
|
elif self.obj_type == "Material":
|
||||||
|
obj = bpy.data.materials.get(self.obj)
|
||||||
|
props = obj.BIMAttributeProperties
|
||||||
|
props.is_editing_attributes = False
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class EditAttributes(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.edit_attributes"
|
||||||
|
bl_label = "Edit Attributes"
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
|
obj_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
if self.obj_type == "Object":
|
||||||
|
obj = bpy.data.objects.get(self.obj)
|
||||||
|
elif self.obj_type == "Material":
|
||||||
|
obj = bpy.data.materials.get(self.obj)
|
||||||
|
oprops = obj.BIMObjectProperties
|
||||||
|
props = obj.BIMAttributeProperties
|
||||||
|
attributes = {}
|
||||||
|
for attribute in Data.products[oprops.ifc_definition_id]:
|
||||||
|
blender_attribute = props.attributes.get(attribute["name"])
|
||||||
|
if not blender_attribute:
|
||||||
|
continue
|
||||||
|
if attribute["is_optional"] and blender_attribute.is_null:
|
||||||
|
attributes[attribute["name"]] = None
|
||||||
|
elif attribute["type"] == "string":
|
||||||
|
attributes[attribute["name"]] = blender_attribute.string_value
|
||||||
|
elif attribute["type"] == "list":
|
||||||
|
values = blender_attribute.string_value[1:-1].split(", ")
|
||||||
|
if attribute["list_type"] == "float":
|
||||||
|
values = [float(v) for v in values]
|
||||||
|
elif attribute["list_type"] == "integer":
|
||||||
|
values = [int(v) for v in values]
|
||||||
|
attributes[attribute["name"]] = values
|
||||||
|
elif attribute["type"] == "integer":
|
||||||
|
attributes[attribute["name"]] = blender_attribute.int_value
|
||||||
|
elif attribute["type"] == "float":
|
||||||
|
attributes[attribute["name"]] = blender_attribute.float_value
|
||||||
|
elif attribute["type"] == "enum":
|
||||||
|
attributes[attribute["name"]] = blender_attribute.enum_value
|
||||||
|
edit_attributes.Usecase(self.file, {
|
||||||
|
"product": self.file.by_id(oprops.ifc_definition_id),
|
||||||
|
"attributes": attributes
|
||||||
|
}).execute()
|
||||||
|
Data.load(oprops.ifc_definition_id)
|
||||||
|
bpy.ops.bim.disable_editing_attributes(obj=self.obj, obj_type=self.obj_type)
|
||||||
|
return {"FINISHED"}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import bpy
|
||||||
|
import blenderbim.bim.schema # refactor
|
||||||
|
from blenderbim.bim.module.material.data import Data
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
|
from bpy.types import PropertyGroup
|
||||||
|
from bpy.props import (
|
||||||
|
PointerProperty,
|
||||||
|
StringProperty,
|
||||||
|
EnumProperty,
|
||||||
|
BoolProperty,
|
||||||
|
IntProperty,
|
||||||
|
FloatProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
class BIMAttributeProperties(PropertyGroup):
|
||||||
|
attributes: CollectionProperty(name="Attributes", type=Attribute)
|
||||||
|
is_editing_attributes: BoolProperty(name="Is Editing Attributes")
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
from bpy.types import Panel
|
||||||
|
from blenderbim.bim.module.attribute.data import Data
|
||||||
|
|
||||||
|
|
||||||
|
def draw_ui(context, layout, obj_type):
|
||||||
|
obj = context.active_object if obj_type == "Object" else context.active_object.active_material
|
||||||
|
oprops = obj.BIMObjectProperties
|
||||||
|
props = obj.BIMAttributeProperties
|
||||||
|
if oprops.ifc_definition_id not in Data.products:
|
||||||
|
Data.load(oprops.ifc_definition_id)
|
||||||
|
|
||||||
|
if props.is_editing_attributes:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
op = row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes")
|
||||||
|
op.obj_type = obj_type
|
||||||
|
op.obj = obj.name
|
||||||
|
op = row.operator("bim.disable_editing_attributes", icon="X", text="")
|
||||||
|
op.obj_type = obj_type
|
||||||
|
op.obj = obj.name
|
||||||
|
|
||||||
|
for attribute in Data.products[oprops.ifc_definition_id]:
|
||||||
|
if attribute["type"] == "entity":
|
||||||
|
continue
|
||||||
|
row = layout.row(align=True)
|
||||||
|
blender_attribute = props.attributes.get(attribute["name"])
|
||||||
|
if attribute["type"] == "string" or attribute["type"] == "list":
|
||||||
|
row.prop(blender_attribute, "string_value", text=attribute["name"])
|
||||||
|
elif attribute["type"] == "integer":
|
||||||
|
row.prop(blender_attribute, "int_value", text=attribute["name"])
|
||||||
|
elif attribute["type"] == "float":
|
||||||
|
row.prop(blender_attribute, "float_value", text=attribute["name"])
|
||||||
|
elif attribute["type"] == "enum":
|
||||||
|
row.prop(blender_attribute, "enum_value", text=attribute["name"])
|
||||||
|
|
||||||
|
if attribute["name"] == "GlobalId":
|
||||||
|
row.operator("bim.generate_global_id", icon="FILE_REFRESH", text="")
|
||||||
|
if attribute["is_optional"]:
|
||||||
|
row.prop(
|
||||||
|
blender_attribute,
|
||||||
|
"is_null",
|
||||||
|
icon="RADIOBUT_OFF" if blender_attribute.is_null else "RADIOBUT_ON",
|
||||||
|
text="",
|
||||||
|
)
|
||||||
|
# TODO: reimplement, see #1222
|
||||||
|
# op = row.operator("bim.copy_attribute_to_selection", icon="COPYDOWN", text="")
|
||||||
|
# op.attribute_name = attribute.name
|
||||||
|
# op.attribute_value = attribute.string_value
|
||||||
|
else:
|
||||||
|
row = layout.row()
|
||||||
|
op = row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit")
|
||||||
|
op.obj_type = obj_type
|
||||||
|
op.obj = obj.name
|
||||||
|
for attribute in Data.products[oprops.ifc_definition_id]:
|
||||||
|
if attribute["value"] is None or attribute["type"] == "entity":
|
||||||
|
continue
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text=attribute["name"])
|
||||||
|
row.label(text=str(attribute["value"]))
|
||||||
|
|
||||||
|
# TODO: reimplement, see #1222
|
||||||
|
# if "IfcSite/" in context.active_object.name or "IfcBuilding/" in context.active_object.name:
|
||||||
|
# self.draw_addresses_ui()
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_object_attributes(Panel):
|
||||||
|
bl_label = "IFC Attributes"
|
||||||
|
bl_idname = "BIM_PT_object_attributes"
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "object"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
draw_ui(context, self.layout, "Object")
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_material_attributes(Panel):
|
||||||
|
bl_label = "IFC Attributes"
|
||||||
|
bl_idname = "BIM_PT_material_attributes"
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "material"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
try:
|
||||||
|
return bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
draw_ui(context, self.layout, "Material")
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui, prop, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.ExecuteBIMTester,
|
||||||
|
operator.BIMTesterPurge,
|
||||||
|
operator.SelectFeaturesDir,
|
||||||
|
operator.RejectElement,
|
||||||
|
operator.ColourByClass,
|
||||||
|
operator.ResetObjectColours,
|
||||||
|
operator.ApproveClass,
|
||||||
|
operator.RejectClass,
|
||||||
|
operator.SelectAudited,
|
||||||
|
prop.BimTesterProperties,
|
||||||
|
ui.BIM_PT_qa,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.Scene.BimTesterProperties = bpy.props.PointerProperty(type=prop.BimTesterProperties)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
del bpy.types.Scene.BimTesterProperties
|
||||||
|
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
import bpy
|
||||||
|
import ifcopenshell
|
||||||
|
import bimtester
|
||||||
|
import os
|
||||||
|
import webbrowser
|
||||||
|
from pathlib import Path
|
||||||
|
from itertools import cycle
|
||||||
|
|
||||||
|
|
||||||
|
class ExecuteBIMTester(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.execute_bim_tester"
|
||||||
|
bl_label = "Execute BIMTester"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
|
||||||
|
filename = os.path.join(
|
||||||
|
bpy.context.scene.BimTesterProperties.features_dir, bpy.context.scene.BimTesterProperties.features_file + ".feature"
|
||||||
|
)
|
||||||
|
cwd = os.getcwd()
|
||||||
|
os.chdir(bpy.context.scene.BimTesterProperties.features_dir)
|
||||||
|
bimtester.run.run_tests({"feature": filename, "advanced_arguments": None, "console": False})
|
||||||
|
bimtester.reports.generate_report()
|
||||||
|
webbrowser.open(
|
||||||
|
"file://"
|
||||||
|
+ os.path.join(
|
||||||
|
bpy.context.scene.BimTesterProperties.features_dir,
|
||||||
|
"report",
|
||||||
|
bpy.context.scene.BimTesterProperties.features_file + ".feature.html",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
os.chdir(cwd)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class BIMTesterPurge(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.bim_tester_purge"
|
||||||
|
bl_label = "Purge Tests"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
|
||||||
|
filename = os.path.join(
|
||||||
|
bpy.context.scene.BimTesterProperties.features_dir, bpy.context.scene.BimTesterProperties.features_file + ".feature"
|
||||||
|
)
|
||||||
|
cwd = os.getcwd()
|
||||||
|
os.chdir(bpy.context.scene.BimTesterProperties.features_dir)
|
||||||
|
bimtester.clean.TestPurger().purge()
|
||||||
|
os.chdir(cwd)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class SelectFeaturesDir(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.select_features_dir"
|
||||||
|
bl_label = "Select Features Directory"
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.context.scene.BimTesterProperties.features_dir = (
|
||||||
|
os.path.dirname(os.path.abspath(self.filepath)) if "." in self.filepath else self.filepath
|
||||||
|
)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
context.window_manager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
class RejectElement(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.reject_element"
|
||||||
|
bl_label = "Reject Element"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
lines = []
|
||||||
|
for object in bpy.context.selected_objects:
|
||||||
|
lines.append(
|
||||||
|
" * The element {} should not exist because {}".format(
|
||||||
|
object.BIMObjectProperties.attributes[
|
||||||
|
object.BIMObjectProperties.attributes.find("GlobalId")
|
||||||
|
].string_value,
|
||||||
|
bpy.context.scene.BimTesterProperties.qa_reject_element_reason,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
QAHelper.append_to_scenario(lines)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class ColourByClass(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.colour_by_class"
|
||||||
|
bl_label = "Colour by Class"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
colours = cycle(colour_list)
|
||||||
|
ifc_classes = {}
|
||||||
|
for obj in bpy.context.visible_objects:
|
||||||
|
if "/" not in obj.name:
|
||||||
|
continue
|
||||||
|
ifc_class = obj.name.split("/")[0]
|
||||||
|
if ifc_class not in ifc_classes:
|
||||||
|
ifc_classes[ifc_class] = next(colours)
|
||||||
|
obj.color = ifc_classes[ifc_class]
|
||||||
|
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
||||||
|
area.spaces[0].shading.color_type = "OBJECT"
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class ResetObjectColours(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.reset_object_colours"
|
||||||
|
bl_label = "Reset Colours"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
for object in bpy.context.selected_objects:
|
||||||
|
object.color = (1, 1, 1, 1)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class ApproveClass(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.approve_class"
|
||||||
|
bl_label = "Approve Class"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
lines = []
|
||||||
|
for object in bpy.context.selected_objects:
|
||||||
|
index = object.BIMObjectProperties.attributes.find("GlobalId")
|
||||||
|
if index != -1:
|
||||||
|
lines.append(
|
||||||
|
" * The element {} is an {}".format(
|
||||||
|
object.BIMObjectProperties.attributes[index].string_value, object.name.split("/")[0]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
QAHelper.append_to_scenario(lines)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class RejectClass(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.reject_class"
|
||||||
|
bl_label = "Reject Class"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
lines = []
|
||||||
|
for object in bpy.context.selected_objects:
|
||||||
|
lines.append(
|
||||||
|
" * The element {} is an {}".format(
|
||||||
|
object.BIMObjectProperties.attributes[
|
||||||
|
object.BIMObjectProperties.attributes.find("GlobalId")
|
||||||
|
].string_value,
|
||||||
|
bpy.context.scene.BimTesterProperties.audit_ifc_class,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
QAHelper.append_to_scenario(lines)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class SelectAudited(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.select_audited"
|
||||||
|
bl_label = "Select Audited"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
audited_global_ids = []
|
||||||
|
for filename in Path(bpy.context.scene.BimTesterProperties.features_dir).glob("*.feature"):
|
||||||
|
with open(filename, "r") as feature_file:
|
||||||
|
lines = feature_file.readlines()
|
||||||
|
for line in lines:
|
||||||
|
words = line.strip().split()
|
||||||
|
for word in words:
|
||||||
|
if self.is_a_global_id(word):
|
||||||
|
audited_global_ids.append(word)
|
||||||
|
for object in bpy.context.visible_objects:
|
||||||
|
index = object.BIMObjectProperties.attributes.find("GlobalId")
|
||||||
|
if index != -1 and object.BIMObjectProperties.attributes[index].string_value in audited_global_ids:
|
||||||
|
object.select_set(True)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def is_a_global_id(self, word):
|
||||||
|
return word[0] in ["0", "1", "2", "3"] and len(word) == 22
|
||||||
|
|
||||||
|
class QAHelper:
|
||||||
|
@classmethod
|
||||||
|
def append_to_scenario(cls, lines):
|
||||||
|
filename = os.path.join(
|
||||||
|
bpy.context.scene.BimTesterProperties.features_dir, bpy.context.scene.BimTesterProperties.features_file + ".feature"
|
||||||
|
)
|
||||||
|
if os.path.exists(filename + "~"):
|
||||||
|
os.remove(filename + "~")
|
||||||
|
os.rename(filename, filename + "~")
|
||||||
|
with open(filename, "w") as destination:
|
||||||
|
with open(filename + "~", "r") as source:
|
||||||
|
is_in_scenario = False
|
||||||
|
for source_line in source:
|
||||||
|
if (
|
||||||
|
"Scenario: " in source_line
|
||||||
|
and bpy.context.scene.BimTesterProperties.scenario == source_line.strip()[len("Scenario: ") :]
|
||||||
|
):
|
||||||
|
is_in_scenario = True
|
||||||
|
elif is_in_scenario:
|
||||||
|
for line in lines:
|
||||||
|
destination.write(line + "\n")
|
||||||
|
is_in_scenario = False
|
||||||
|
destination.write(source_line)
|
||||||
|
os.remove(filename + "~")
|
||||||
|
|
||||||
|
colour_list = [
|
||||||
|
(0.651, 0.81, 0.892, 1),
|
||||||
|
(0.121, 0.471, 0.706, 1),
|
||||||
|
(0.699, 0.876, 0.54, 1),
|
||||||
|
(0.199, 0.629, 0.174, 1),
|
||||||
|
(0.983, 0.605, 0.602, 1),
|
||||||
|
(0.89, 0.101, 0.112, 1),
|
||||||
|
(0.989, 0.751, 0.427, 1),
|
||||||
|
(0.986, 0.497, 0.1, 1),
|
||||||
|
(0.792, 0.699, 0.839, 1),
|
||||||
|
(0.414, 0.239, 0.603, 1),
|
||||||
|
(0.993, 0.999, 0.6, 1),
|
||||||
|
(0.693, 0.349, 0.157, 1),
|
||||||
|
]
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.prop import StrProperty
|
||||||
|
from bpy.types import PropertyGroup
|
||||||
|
from bpy.props import (
|
||||||
|
PointerProperty,
|
||||||
|
StringProperty,
|
||||||
|
EnumProperty,
|
||||||
|
BoolProperty,
|
||||||
|
IntProperty,
|
||||||
|
FloatProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
scenarios_enum = []
|
||||||
|
featuresfiles_enum = []
|
||||||
|
classes_enum = []
|
||||||
|
|
||||||
|
|
||||||
|
def getIfcClasses(self, context): # This is a copy of the one in bim.prop (as it is used in other modules, can be refactored later)
|
||||||
|
global classes_enum
|
||||||
|
file = IfcStore.get_file()
|
||||||
|
if len(classes_enum) < 1 and file:
|
||||||
|
declaration = IfcStore.get_schema().declaration_by_name(self.ifc_product)
|
||||||
|
def get_classes(declaration):
|
||||||
|
results = []
|
||||||
|
if not declaration.is_abstract():
|
||||||
|
results.append(declaration.name())
|
||||||
|
for subtype in declaration.subtypes():
|
||||||
|
results.extend(get_classes(subtype))
|
||||||
|
return results
|
||||||
|
classes = get_classes(declaration)
|
||||||
|
classes_enum.extend([(c, c, "") for c in sorted(classes)])
|
||||||
|
return classes_enum
|
||||||
|
|
||||||
|
|
||||||
|
def getFeaturesFiles(self, context):
|
||||||
|
global featuresfiles_enum
|
||||||
|
if len(featuresfiles_enum) < 1:
|
||||||
|
featuresfiles_enum.clear()
|
||||||
|
for filename in Path(context.scene.BimTesterProperties.features_dir).glob("*.feature"):
|
||||||
|
f = str(filename.stem)
|
||||||
|
featuresfiles_enum.append((f, f, ""))
|
||||||
|
return featuresfiles_enum
|
||||||
|
|
||||||
|
|
||||||
|
def refreshFeaturesFiles(self, context):
|
||||||
|
global featuresfiles_enum
|
||||||
|
featuresfiles_enum.clear()
|
||||||
|
getFeaturesFiles(self, context)
|
||||||
|
|
||||||
|
|
||||||
|
def getScenarios(self, context):
|
||||||
|
global scenarios_enum
|
||||||
|
if len(scenarios_enum) < 1:
|
||||||
|
scenarios_enum.clear()
|
||||||
|
|
||||||
|
if context.scene.BimTesterProperties.features_file != '': # To handle the error when no .feature file exists in the folder
|
||||||
|
filename = os.path.join(
|
||||||
|
context.scene.BimTesterProperties.features_dir, context.scene.BimTesterProperties.features_file + ".feature"
|
||||||
|
)
|
||||||
|
with open(filename, "r") as feature_file:
|
||||||
|
lines = feature_file.readlines()
|
||||||
|
for line in lines:
|
||||||
|
if "Scenario:" in line:
|
||||||
|
s = line.strip()[len("Scenario: ") :]
|
||||||
|
scenarios_enum.append((s, s, ""))
|
||||||
|
return scenarios_enum
|
||||||
|
|
||||||
|
|
||||||
|
def refreshScenarios(self, context):
|
||||||
|
global scenarios_enum
|
||||||
|
scenarios_enum.clear()
|
||||||
|
getScenarios(self, context)
|
||||||
|
|
||||||
|
|
||||||
|
class BimTesterProperties(PropertyGroup):
|
||||||
|
features_dir: StringProperty(default="", name="Features Directory", update=refreshFeaturesFiles)
|
||||||
|
features_file: EnumProperty(items=getFeaturesFiles, name="Features File", update=refreshScenarios)
|
||||||
|
audit_ifc_class: EnumProperty(items=getIfcClasses, name="Audit Class")
|
||||||
|
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
|
||||||
|
scenario: EnumProperty(items=getScenarios, name="Scenario")
|
||||||
|
# should_load_from_memory: BoolProperty(default=False, name="Load from Memory") # can be added later to mimic the functionality in the CSV Module
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import bpy
|
||||||
|
from bpy.types import Panel
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_qa(Panel):
|
||||||
|
bl_label = "BIMTester Quality Auditing"
|
||||||
|
bl_idname = "BIM_PT_qa"
|
||||||
|
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
|
||||||
|
bimtester_properties = bpy.context.scene.BimTesterProperties
|
||||||
|
|
||||||
|
layout.label(text="Gherkin Setup:")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(bimtester_properties, "features_dir")
|
||||||
|
row.operator("bim.select_features_dir", icon="FILE_FOLDER", text="")
|
||||||
|
|
||||||
|
if bimtester_properties.features_dir:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(bimtester_properties, "features_file")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(bimtester_properties, "scenario")
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
if str(context.scene.BimTesterProperties.features_file) != '': # To handle the error when no .feature file exists in the folder
|
||||||
|
row = layout.row()
|
||||||
|
row.operator("bim.execute_bim_tester")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.operator("bim.bim_tester_purge")
|
||||||
|
|
||||||
|
layout.label(text="Quality Auditing:")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(bimtester_properties, "qa_reject_element_reason")
|
||||||
|
row = layout.row()
|
||||||
|
row.operator("bim.reject_element")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.operator("bim.colour_by_class")
|
||||||
|
row.operator("bim.reset_object_colours")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(bimtester_properties, "audit_ifc_class")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.operator("bim.approve_class")
|
||||||
|
row.operator("bim.reject_class")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.operator("bim.select_audited")
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui, prop, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.SelectCobieIfcFile,
|
||||||
|
operator.SelectCobieJsonFile,
|
||||||
|
operator.ExecuteIfcCobie,
|
||||||
|
prop.COBieProperties,
|
||||||
|
ui.BIM_PT_cobie,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.Scene.COBieProperties = bpy.props.PointerProperty(type=prop.COBieProperties)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
del bpy.types.Scene.COBieProperties
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import bpy
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import ifcopenshell
|
||||||
|
import json
|
||||||
|
import webbrowser
|
||||||
|
import tempfile
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class SelectCobieIfcFile(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.select_cobie_ifc_file"
|
||||||
|
bl_label = "Select COBie IFC File"
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.context.scene.COBieProperties.cobie_ifc_file = self.filepath
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
context.window_manager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
|
||||||
|
class SelectCobieJsonFile(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.select_cobie_json_file"
|
||||||
|
bl_label = "Select COBie JSON File"
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.context.scene.COBieProperties.cobie_json_file = self.filepath
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
context.window_manager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
|
||||||
|
class ExecuteIfcCobie(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.execute_ifc_cobie"
|
||||||
|
bl_label = "Execute IFCCOBie"
|
||||||
|
file_format: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
from cobie import IfcCobieParser
|
||||||
|
props = bpy.context.scene.COBieProperties
|
||||||
|
|
||||||
|
output_dir = os.path.dirname(props.cobie_ifc_file)
|
||||||
|
|
||||||
|
if props.should_load_from_memory:
|
||||||
|
output_dir = tempfile.gettempdir()
|
||||||
|
|
||||||
|
output = os.path.join(output_dir, "output")
|
||||||
|
logger = logging.getLogger("IFCtoCOBie")
|
||||||
|
fh = logging.FileHandler(os.path.join(output_dir, "cobie.log"))
|
||||||
|
fh.setLevel(logging.DEBUG)
|
||||||
|
fh.setFormatter(logging.Formatter("%(asctime)s : %(levelname)s : %(message)s"))
|
||||||
|
logger = logging.getLogger("IFCtoCOBie")
|
||||||
|
logger.addHandler(fh)
|
||||||
|
selector = ifcopenshell.util.selector.Selector()
|
||||||
|
if props.cobie_json_file:
|
||||||
|
with open(props.cobie_json_file, "r") as f:
|
||||||
|
custom_data = json.load(f)
|
||||||
|
else:
|
||||||
|
custom_data = {}
|
||||||
|
parser = IfcCobieParser(logger, selector)
|
||||||
|
|
||||||
|
ifc_file = IfcStore.get_file()
|
||||||
|
|
||||||
|
if not (ifc_file and props.should_load_from_memory):
|
||||||
|
ifc_file = props.cobie_ifc_file
|
||||||
|
|
||||||
|
parser.parse(
|
||||||
|
ifc_file,
|
||||||
|
props.cobie_types,
|
||||||
|
props.cobie_components,
|
||||||
|
custom_data,
|
||||||
|
)
|
||||||
|
if self.file_format == "xlsx":
|
||||||
|
from cobie import CobieXlsWriter
|
||||||
|
|
||||||
|
writer = CobieXlsWriter(parser, output)
|
||||||
|
writer.write()
|
||||||
|
webbrowser.open("file://" + output + "." + self.file_format)
|
||||||
|
elif self.file_format == "ods":
|
||||||
|
from cobie import CobieOdsWriter
|
||||||
|
|
||||||
|
writer = CobieOdsWriter(parser, output)
|
||||||
|
writer.write()
|
||||||
|
webbrowser.open("file://" + output + "." + self.file_format)
|
||||||
|
else:
|
||||||
|
from cobie import CobieCsvWriter
|
||||||
|
|
||||||
|
writer = CobieCsvWriter(parser, output_dir)
|
||||||
|
writer.write()
|
||||||
|
webbrowser.open("file://" + output_dir)
|
||||||
|
webbrowser.open("file://" + output_dir + "/cobie.log")
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import bpy
|
||||||
|
from bpy.types import PropertyGroup
|
||||||
|
from bpy.props import (
|
||||||
|
PointerProperty,
|
||||||
|
StringProperty,
|
||||||
|
EnumProperty,
|
||||||
|
BoolProperty,
|
||||||
|
IntProperty,
|
||||||
|
FloatProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class COBieProperties(PropertyGroup):
|
||||||
|
cobie_ifc_file: StringProperty(default="", name="COBie IFC File")
|
||||||
|
cobie_types: StringProperty(default=".COBieType", name="COBie Types")
|
||||||
|
cobie_components: StringProperty(default=".COBie", name="COBie Components")
|
||||||
|
cobie_json_file: StringProperty(default="", name="COBie JSON File")
|
||||||
|
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
from bpy.types import Panel
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_cobie(Panel):
|
||||||
|
bl_label = "IFC COBie"
|
||||||
|
bl_idname = "BIM_PT_cobie"
|
||||||
|
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.COBieProperties
|
||||||
|
|
||||||
|
|
||||||
|
if IfcStore.get_file():
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(props, "should_load_from_memory")
|
||||||
|
|
||||||
|
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(props, "cobie_ifc_file")
|
||||||
|
row.operator("bim.select_cobie_ifc_file", icon="FILE_FOLDER", text="")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(props, "cobie_types")
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(props, "cobie_components")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(props, "cobie_json_file")
|
||||||
|
row.operator("bim.select_cobie_json_file", icon="FILE_FOLDER", text="")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
op = row.operator("bim.execute_ifc_cobie", text="CSV")
|
||||||
|
op.file_format = "csv"
|
||||||
|
op = row.operator("bim.execute_ifc_cobie", text="ODS")
|
||||||
|
op.file_format = "ods"
|
||||||
|
op = row.operator("bim.execute_ifc_cobie", text="XLSX")
|
||||||
|
op.file_format = "xlsx"
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.AddSubcontext,
|
||||||
|
operator.RemoveSubcontext,
|
||||||
|
ui.BIM_PT_context,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
pass
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {
|
||||||
|
"context": None,
|
||||||
|
"subcontext": None,
|
||||||
|
"target_view": None,
|
||||||
|
}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
parent = [
|
||||||
|
c
|
||||||
|
for c in self.file.by_type("IfcGeometricRepresentationContext")
|
||||||
|
if c.ContextType == self.settings["context"]
|
||||||
|
]
|
||||||
|
if not parent:
|
||||||
|
self.create_origin()
|
||||||
|
if self.settings["context"] == "Plan":
|
||||||
|
context = self.file.createIfcGeometricRepresentationContext(None, "Plan", 2, 1.0e-05, self.origin)
|
||||||
|
else:
|
||||||
|
context = self.file.createIfcGeometricRepresentationContext(None, "Model", 3, 1.0e-05, self.origin)
|
||||||
|
project = self.file.by_type("IfcProject")[0]
|
||||||
|
if project.RepresentationContexts:
|
||||||
|
contexts = list(project.RepresentationContexts)
|
||||||
|
else:
|
||||||
|
contexts = []
|
||||||
|
contexts.append(context)
|
||||||
|
project.RepresentationContexts = contexts
|
||||||
|
return context
|
||||||
|
parent = parent[0]
|
||||||
|
return self.file.create_entity("IfcGeometricRepresentationSubContext", **{
|
||||||
|
"ContextIdentifier": self.settings["subcontext"],
|
||||||
|
"ContextType": self.settings["context"],
|
||||||
|
"ParentContext": parent,
|
||||||
|
"TargetView": self.settings["target_view"],
|
||||||
|
})
|
||||||
|
|
||||||
|
def create_origin(self):
|
||||||
|
self.origin = self.file.createIfcAxis2Placement3D(
|
||||||
|
self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)),
|
||||||
|
self.file.createIfcDirection((0.0, 0.0, 1.0)),
|
||||||
|
self.file.createIfcDirection((1.0, 0.0, 0.0)),
|
||||||
|
)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class Data:
|
||||||
|
is_loaded = False
|
||||||
|
contexts = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls):
|
||||||
|
file = IfcStore.get_file()
|
||||||
|
if not file:
|
||||||
|
return
|
||||||
|
cls.contexts = {}
|
||||||
|
for context in file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
|
subcontexts = {}
|
||||||
|
# See bug #1224 for why we don't use HasSubContexts
|
||||||
|
for subcontext in file.by_type("IfcGeometricRepresentationSubContext"):
|
||||||
|
if subcontext.ParentContext != context:
|
||||||
|
continue
|
||||||
|
subcontexts[int(subcontext.id())] = {
|
||||||
|
"ContextType": subcontext.ContextType,
|
||||||
|
"ContextIdentifier": subcontext.ContextIdentifier,
|
||||||
|
"TargetView": subcontext.TargetView,
|
||||||
|
}
|
||||||
|
cls.contexts[int(context.id())] = {
|
||||||
|
"ContextType": context.ContextType,
|
||||||
|
"HasSubContexts": subcontexts
|
||||||
|
}
|
||||||
|
cls.is_loaded = True
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import bpy
|
||||||
|
import blenderbim.bim.module.context.add_context as add_context
|
||||||
|
import blenderbim.bim.module.context.remove_context as remove_context
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.module.context.data import Data
|
||||||
|
|
||||||
|
|
||||||
|
class AddSubcontext(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.add_subcontext"
|
||||||
|
bl_label = "Add Subcontext"
|
||||||
|
context: bpy.props.StringProperty()
|
||||||
|
subcontext: bpy.props.StringProperty()
|
||||||
|
target_view: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
add_context.Usecase(
|
||||||
|
self.file,
|
||||||
|
{
|
||||||
|
"context": self.context or bpy.context.scene.BIMProperties.available_contexts,
|
||||||
|
"subcontext": self.subcontext or bpy.context.scene.BIMProperties.available_subcontexts,
|
||||||
|
"target_view": self.target_view or bpy.context.scene.BIMProperties.available_target_views,
|
||||||
|
},
|
||||||
|
).execute()
|
||||||
|
Data.load()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveSubcontext(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.remove_subcontext"
|
||||||
|
bl_label = "Remove Context"
|
||||||
|
ifc_definition_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
usecase = remove_context.Usecase(self.file, {"context": self.file.by_id(self.ifc_definition_id)})
|
||||||
|
usecase.execute()
|
||||||
|
Data.load()
|
||||||
|
return {"FINISHED"}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {"context": None}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
# TODO: this is a light remove only
|
||||||
|
for subcontext in self.settings["context"].HasSubContexts:
|
||||||
|
self.file.remove(subcontext)
|
||||||
|
self.file.remove(self.settings["context"])
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
from bpy.types import Panel
|
||||||
|
from blenderbim.bim.module.context.data import Data
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return IfcStore.get_file()
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
if not Data.is_loaded:
|
||||||
|
Data.load()
|
||||||
|
|
||||||
|
props = context.scene.BIMProperties
|
||||||
|
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "available_contexts", text="")
|
||||||
|
row.prop(props, "available_subcontexts", text="")
|
||||||
|
row.prop(props, "available_target_views", text="")
|
||||||
|
row.operator("bim.add_subcontext", icon="ADD", text="")
|
||||||
|
|
||||||
|
for ifc_definition_id, context in Data.contexts.items():
|
||||||
|
box = self.layout.box()
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.label(text=context["ContextType"])
|
||||||
|
row.operator("bim.remove_subcontext", icon="X", text="").ifc_definition_id = ifc_definition_id
|
||||||
|
for ifc_definition_id2, subcontext in context["HasSubContexts"].items():
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.label(text=subcontext["ContextType"])
|
||||||
|
row.label(text=subcontext["ContextIdentifier"])
|
||||||
|
row.label(text=subcontext["TargetView"])
|
||||||
|
row.operator("bim.remove_subcontext", icon="X", text="").ifc_definition_id = ifc_definition_id2
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui, prop, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.AddCsvAttribute,
|
||||||
|
operator.RemoveCsvAttribute,
|
||||||
|
operator.ExportIfcCsv,
|
||||||
|
operator.ImportIfcCsv,
|
||||||
|
operator.EyedropIfcCsv,
|
||||||
|
prop.CsvProperties,
|
||||||
|
ui.BIM_PT_ifccsv,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.Scene.CsvProperties = bpy.props.PointerProperty(type=prop.CsvProperties)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
del bpy.types.Scene.CsvProperties
|
||||||
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import bpy
|
||||||
|
import ifccsv
|
||||||
|
import ifcopenshell
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
import webbrowser
|
||||||
|
import tempfile
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AddCsvAttribute(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.add_csv_attribute"
|
||||||
|
bl_label = "Add CSV Attribute"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
attribute = bpy.context.scene.CsvProperties.csv_attributes.add()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class RemoveCsvAttribute(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.remove_csv_attribute"
|
||||||
|
bl_label = "Remove CSV Attribute"
|
||||||
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.context.scene.CsvProperties.csv_attributes.remove(self.index)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class ExportIfcCsv(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.export_ifccsv"
|
||||||
|
bl_label = "Export IFC to CSV"
|
||||||
|
filename_ext = ".csv"
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".csv")
|
||||||
|
WindowManager = context.window_manager
|
||||||
|
WindowManager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
import ifccsv
|
||||||
|
|
||||||
|
self.filepath = bpy.path.ensure_ext(self.filepath, ".csv")
|
||||||
|
ifc_file = ifcopenshell.open(bpy.context.scene.CsvProperties.csv_ifc_file)
|
||||||
|
selector = ifcopenshell.util.selector.Selector()
|
||||||
|
results = selector.parse(ifc_file, bpy.context.scene.CsvProperties.ifc_selector)
|
||||||
|
ifc_csv = ifccsv.IfcCsv()
|
||||||
|
ifc_csv.output = self.filepath
|
||||||
|
ifc_csv.attributes = [a.name for a in bpy.context.scene.CsvProperties.csv_attributes]
|
||||||
|
ifc_csv.selector = selector
|
||||||
|
if bpy.context.scene.CsvProperties.csv_delimiter == "CUSTOM":
|
||||||
|
ifc_csv.delimiter = bpy.context.scene.CsvProperties.csv_custom_delimiter
|
||||||
|
else:
|
||||||
|
ifc_csv.delimiter = bpy.context.scene.CsvProperties.csv_delimiter
|
||||||
|
ifc_csv.export(ifc_file, results)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class ImportIfcCsv(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.import_ifccsv"
|
||||||
|
bl_label = "Import CSV to IFC"
|
||||||
|
filename_ext = ".csv"
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".csv")
|
||||||
|
WindowManager = context.window_manager
|
||||||
|
WindowManager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
import ifccsv
|
||||||
|
|
||||||
|
ifc_csv = ifccsv.IfcCsv()
|
||||||
|
ifc_csv.output = self.filepath
|
||||||
|
ifc_csv.Import(bpy.context.scene.CsvProperties.csv_ifc_file)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class EyedropIfcCsv(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.eyedrop_ifccsv"
|
||||||
|
bl_label = "Query Selected Items"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
global_ids = []
|
||||||
|
for obj in bpy.context.selected_objects:
|
||||||
|
if hasattr(obj, "BIMObjectProperties") and obj.BIMObjectProperties.attributes.get("GlobalId"):
|
||||||
|
global_ids.append("#" + obj.BIMObjectProperties.attributes.get("GlobalId").string_value)
|
||||||
|
bpy.context.scene.CsvProperties.ifc_selector = "|".join(global_ids)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import bpy
|
||||||
|
from blenderbim.bim.prop import StrProperty
|
||||||
|
from bpy.types import PropertyGroup
|
||||||
|
from bpy.props import (
|
||||||
|
PointerProperty,
|
||||||
|
StringProperty,
|
||||||
|
EnumProperty,
|
||||||
|
BoolProperty,
|
||||||
|
IntProperty,
|
||||||
|
FloatProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CsvProperties(PropertyGroup):
|
||||||
|
csv_ifc_file: StringProperty(default="", name="CSV IFC File")
|
||||||
|
ifc_selector: StringProperty(default="", name="IFC Selector")
|
||||||
|
csv_attributes: CollectionProperty(name="CSV Attributes", type=StrProperty)
|
||||||
|
csv_delimiter: EnumProperty(
|
||||||
|
items=[(";", ";", ""), (",", ",", ""), (".", ".", ""), ("CUSTOM", "Custom", ""),],
|
||||||
|
name="IFC CSV Delimiter",
|
||||||
|
default=",",
|
||||||
|
)
|
||||||
|
csv_custom_delimiter: StringProperty(default="", name="Custom Delimiter")
|
||||||
|
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
|
||||||
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
from bpy.types import Panel
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_ifccsv(Panel):
|
||||||
|
bl_label = "IFC CSV Import/Export"
|
||||||
|
bl_idname = "BIM_PT_ifccsv"
|
||||||
|
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.CsvProperties
|
||||||
|
|
||||||
|
if IfcStore.get_file():
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(props, "should_load_from_memory")
|
||||||
|
|
||||||
|
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(props, "csv_ifc_file")
|
||||||
|
row.operator("bim.import_ifccsv", icon="FILE_FOLDER", text="")
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(props, "ifc_selector")
|
||||||
|
row.operator("bim.eyedrop_ifccsv", icon="EYEDROPPER", text="")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.label(text="Add IFC attributes to filter", icon="FILE_BLANK")
|
||||||
|
row.operator("bim.add_csv_attribute")
|
||||||
|
|
||||||
|
for index, attribute in enumerate(props.csv_attributes):
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(attribute, "name", text="")
|
||||||
|
row.operator("bim.remove_csv_attribute", icon="X", text="").index = index
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(props, "csv_delimiter")
|
||||||
|
|
||||||
|
if(props.csv_delimiter == 'CUSTOM'):
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(props, "csv_custom_delimiter")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.operator("bim.export_ifccsv", icon="EXPORT")
|
||||||
|
row.operator("bim.import_ifccsv", icon="IMPORT")
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui, prop, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.ProfileImportIFC,
|
||||||
|
operator.CreateShapeFromStepId,
|
||||||
|
operator.SelectHighPolygonMeshes,
|
||||||
|
operator.InspectFromStepId,
|
||||||
|
operator.InspectFromObject,
|
||||||
|
operator.RewindInspector,
|
||||||
|
prop.BIMDebugProperties,
|
||||||
|
ui.BIM_PT_debug,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.Scene.BIMDebugProperties = bpy.props.PointerProperty(type=prop.BIMDebugProperties)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
del bpy.types.Scene.BIMDebugProperties
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import bpy
|
||||||
|
import logging
|
||||||
|
import ifcopenshell
|
||||||
|
import blenderbim.bim.import_ifc as import_ifc
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileImportIFC(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.profile_import_ifc"
|
||||||
|
bl_label = "Profile Import IFC"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
import cProfile
|
||||||
|
import pstats
|
||||||
|
|
||||||
|
cProfile.run(
|
||||||
|
f"import bpy; bpy.ops.import_ifc.bim(filepath='{bpy.context.scene.BIMProperties.ifc_file}')", "blender.prof"
|
||||||
|
)
|
||||||
|
p = pstats.Stats("blender.prof")
|
||||||
|
p.sort_stats("cumulative").print_stats(50)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class CreateShapeFromStepId(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.create_shape_from_step_id"
|
||||||
|
bl_label = "Create Shape From STEP ID"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
logger = logging.getLogger("ImportIFC")
|
||||||
|
self.ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, IfcStore.path, logger)
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
element = self.file.by_id(int(bpy.context.scene.BIMDebugProperties.step_id))
|
||||||
|
settings = ifcopenshell.geom.settings()
|
||||||
|
# settings.set(settings.INCLUDE_CURVES, True)
|
||||||
|
shape = ifcopenshell.geom.create_shape(settings, element)
|
||||||
|
ifc_importer = import_ifc.IfcImporter(self.ifc_import_settings)
|
||||||
|
ifc_importer.file = self.file
|
||||||
|
mesh = ifc_importer.create_mesh(element, shape)
|
||||||
|
obj = bpy.data.objects.new("Debug", mesh)
|
||||||
|
bpy.context.scene.collection.objects.link(obj)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class SelectHighPolygonMeshes(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.select_high_polygon_meshes"
|
||||||
|
bl_label = "Select High Polygon Meshes"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
results = {}
|
||||||
|
for obj in bpy.data.objects:
|
||||||
|
if not isinstance(obj.data, bpy.types.Mesh) or len(obj.data.polygons) < int(
|
||||||
|
bpy.context.scene.BIMDebugProperties.number_of_polygons
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
obj.select_set(True)
|
||||||
|
except:
|
||||||
|
# If it is not in the view layer
|
||||||
|
pass
|
||||||
|
relating_type = obj.BIMObjectProperties.relating_type
|
||||||
|
if relating_type:
|
||||||
|
relating_type.select_set(True)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class RewindInspector(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.rewind_inspector"
|
||||||
|
bl_label = "Rewind Inspector"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
props = bpy.context.scene.BIMDebugProperties
|
||||||
|
total_breadcrumbs = len(props.step_id_breadcrumb)
|
||||||
|
if total_breadcrumbs < 2:
|
||||||
|
return {"FINISHED"}
|
||||||
|
previous_step_id = int(props.step_id_breadcrumb[total_breadcrumbs - 2].name)
|
||||||
|
props.step_id_breadcrumb.remove(total_breadcrumbs - 1)
|
||||||
|
props.step_id_breadcrumb.remove(total_breadcrumbs - 2)
|
||||||
|
bpy.ops.bim.inspect_from_step_id(step_id=previous_step_id)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class InspectFromStepId(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.inspect_from_step_id"
|
||||||
|
bl_label = "Inspect From STEP ID"
|
||||||
|
step_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
bpy.context.scene.BIMDebugProperties.active_step_id = self.step_id
|
||||||
|
crumb = bpy.context.scene.BIMDebugProperties.step_id_breadcrumb.add()
|
||||||
|
crumb.name = str(self.step_id)
|
||||||
|
element = self.file.by_id(self.step_id)
|
||||||
|
while len(bpy.context.scene.BIMDebugProperties.attributes) > 0:
|
||||||
|
bpy.context.scene.BIMDebugProperties.attributes.remove(0)
|
||||||
|
while len(bpy.context.scene.BIMDebugProperties.inverse_attributes) > 0:
|
||||||
|
bpy.context.scene.BIMDebugProperties.inverse_attributes.remove(0)
|
||||||
|
for key, value in element.get_info().items():
|
||||||
|
self.add_attribute(bpy.context.scene.BIMDebugProperties.attributes, key, value)
|
||||||
|
for key in dir(element):
|
||||||
|
if (
|
||||||
|
not key[0].isalpha()
|
||||||
|
or key[0] != key[0].upper()
|
||||||
|
or key in element.get_info()
|
||||||
|
or not getattr(element, key)
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
self.add_attribute(bpy.context.scene.BIMDebugProperties.inverse_attributes, key, getattr(element, key))
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def add_attribute(self, prop, key, value):
|
||||||
|
if isinstance(value, tuple) and len(value) < 10:
|
||||||
|
for i, item in enumerate(value):
|
||||||
|
self.add_attribute(prop, key + f"[{i}]", item)
|
||||||
|
return
|
||||||
|
elif isinstance(value, tuple) and len(value) >= 10:
|
||||||
|
key = key + "({})".format(len(value))
|
||||||
|
new = prop.add()
|
||||||
|
new.name = key
|
||||||
|
new.string_value = str(value)
|
||||||
|
if isinstance(value, ifcopenshell.entity_instance):
|
||||||
|
new.int_value = int(value.id())
|
||||||
|
|
||||||
|
|
||||||
|
class InspectFromObject(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.inspect_from_object"
|
||||||
|
bl_label = "Inspect From Object"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
ifc_definition_id = bpy.context.active_object.BIMObjectProperties.ifc_definition_id
|
||||||
|
if not ifc_definition_id:
|
||||||
|
return {"FINISHED"}
|
||||||
|
bpy.ops.bim.inspect_from_step_id(step_id=ifc_definition_id)
|
||||||
|
return {"FINISHED"}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
|
from bpy.types import PropertyGroup
|
||||||
|
from bpy.props import (
|
||||||
|
PointerProperty,
|
||||||
|
StringProperty,
|
||||||
|
EnumProperty,
|
||||||
|
BoolProperty,
|
||||||
|
IntProperty,
|
||||||
|
FloatProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import bpy
|
||||||
|
from bpy.types import Panel
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui, prop, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.SelectDiffJsonFile,
|
||||||
|
operator.VisualiseDiff,
|
||||||
|
operator.SelectDiffOldFile,
|
||||||
|
operator.SelectDiffNewFile,
|
||||||
|
operator.ExecuteIfcDiff,
|
||||||
|
prop.DiffProperties,
|
||||||
|
ui.BIM_PT_diff,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.Scene.DiffProperties = bpy.props.PointerProperty(type=prop.DiffProperties)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
del bpy.types.Scene.DiffProperties
|
||||||
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import bpy
|
||||||
|
import ifccsv
|
||||||
|
import ifcopenshell
|
||||||
|
import json
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SelectDiffJsonFile(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.select_diff_json_file"
|
||||||
|
bl_label = "Select Diff JSON File"
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.context.scene.DiffProperties.diff_json_file = self.filepath
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
context.window_manager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
|
||||||
|
class VisualiseDiff(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.visualise_diff"
|
||||||
|
bl_label = "Visualise Diff"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
#ifc_file = IfcStore.get_file() # In case we get from Store
|
||||||
|
ifc_file = ifcopenshell.open(context.scene.DiffProperties.diff_new_file) # for Now refer to the new file
|
||||||
|
with open(bpy.context.scene.DiffProperties.diff_json_file, "r") as file:
|
||||||
|
diff = json.load(file)
|
||||||
|
for obj in bpy.context.visible_objects:
|
||||||
|
obj.color = (1.0, 1.0, 1.0, 0.2)
|
||||||
|
global_id = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId
|
||||||
|
#global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
|
||||||
|
if not global_id:
|
||||||
|
continue
|
||||||
|
if global_id.string_value in diff["deleted"]:
|
||||||
|
obj.color = (1.0, 0.0, 0.0, 0.2)
|
||||||
|
elif global_id.string_value in diff["added"]:
|
||||||
|
obj.color = (0.0, 1.0, 0.0, 0.2)
|
||||||
|
elif global_id.string_value in diff["changed"]:
|
||||||
|
obj.color = (0.0, 0.0, 1.0, 0.2)
|
||||||
|
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
||||||
|
area.spaces[0].shading.color_type = "OBJECT"
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class SelectDiffOldFile(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.select_diff_old_file"
|
||||||
|
bl_label = "Select Diff Old File"
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.context.scene.DiffProperties.diff_old_file = self.filepath
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
context.window_manager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
|
||||||
|
class SelectDiffNewFile(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.select_diff_new_file"
|
||||||
|
bl_label = "Select Diff New File"
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.context.scene.DiffProperties.diff_new_file = self.filepath
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
context.window_manager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
|
||||||
|
class ExecuteIfcDiff(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.execute_ifc_diff"
|
||||||
|
bl_label = "Execute IFC Diff"
|
||||||
|
filename_ext = ".json"
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
|
||||||
|
WindowManager = context.window_manager
|
||||||
|
WindowManager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
import ifcdiff
|
||||||
|
|
||||||
|
ifc_diff = ifcdiff.IfcDiff(
|
||||||
|
bpy.context.scene.DiffProperties.diff_old_file,
|
||||||
|
bpy.context.scene.DiffProperties.diff_new_file,
|
||||||
|
self.filepath,
|
||||||
|
bpy.context.scene.DiffProperties.diff_relationships.split(),
|
||||||
|
)
|
||||||
|
ifc_diff.diff()
|
||||||
|
ifc_diff.export()
|
||||||
|
bpy.context.scene.DiffProperties.diff_json_file = self.filepath
|
||||||
|
return {"FINISHED"}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import bpy
|
||||||
|
from blenderbim.bim.prop import StrProperty
|
||||||
|
from bpy.types import PropertyGroup
|
||||||
|
from bpy.props import (
|
||||||
|
PointerProperty,
|
||||||
|
StringProperty,
|
||||||
|
EnumProperty,
|
||||||
|
BoolProperty,
|
||||||
|
IntProperty,
|
||||||
|
FloatProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DiffProperties(PropertyGroup):
|
||||||
|
diff_json_file: StringProperty(default="", name="Diff JSON File")
|
||||||
|
diff_old_file: StringProperty(default="", name="Diff Old IFC File")
|
||||||
|
diff_new_file: StringProperty(default="", name="Diff New IFC File")
|
||||||
|
diff_relationships: StringProperty(default="", name="Diff Relationships")
|
||||||
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
from bpy.types import Panel
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_diff(Panel):
|
||||||
|
bl_label = "IFC Diff"
|
||||||
|
bl_idname = "BIM_PT_diff"
|
||||||
|
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
|
||||||
|
bim_properties = scene.DiffProperties
|
||||||
|
|
||||||
|
layout.label(text="IFC Diff Setup:")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(bim_properties, "diff_old_file")
|
||||||
|
row.operator("bim.select_diff_old_file", icon="FILE_FOLDER", text="")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(bim_properties, "diff_new_file")
|
||||||
|
row.operator("bim.select_diff_new_file", icon="FILE_FOLDER", text="")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(bim_properties, "diff_relationships")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.operator("bim.execute_ifc_diff")
|
||||||
|
|
||||||
|
# TODO: show if there ifc diff operation is sucessful
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(bim_properties, "diff_json_file")
|
||||||
|
row.operator("bim.select_diff_json_file", icon="FILE_FOLDER", text="")
|
||||||
|
row.operator("bim.visualise_diff", icon="HIDE_OFF", text="")
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.EditObjectPlacement,
|
||||||
|
operator.AddRepresentation,
|
||||||
|
operator.SwitchRepresentation,
|
||||||
|
operator.RemoveRepresentation,
|
||||||
|
operator.UpdateMeshRepresentation,
|
||||||
|
operator.UpdateParametricRepresentation,
|
||||||
|
operator.GetRepresentationIfcParameters,
|
||||||
|
ui.BIM_PT_representations,
|
||||||
|
ui.BIM_PT_mesh,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.OBJECT_PT_transform.append(ui.BIM_PT_transform)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.types.OBJECT_PT_transform.remove(ui.BIM_PT_transform)
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
import ifcopenshell.util.unit
|
||||||
|
|
||||||
|
|
||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
# TODO: This usecase currently depends on Blender's data model
|
||||||
|
self.file = file
|
||||||
|
self.settings = {
|
||||||
|
"context": None, # IfcGeometricRepresentationContext
|
||||||
|
"geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now
|
||||||
|
"total_items": 1, # How many representation items to create
|
||||||
|
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
|
||||||
|
"should_force_faceted_brep": False, # If we should force faceted breps for meshes
|
||||||
|
"is_wireframe": False, # If the geometry is a wireframe
|
||||||
|
"is_curve": False, # If the geometry is a Blender curve
|
||||||
|
"is_point_cloud": False, # If the geometry is a point cloud
|
||||||
|
}
|
||||||
|
self.ifc_vertices = []
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
if self.settings["unit_scale"] is None:
|
||||||
|
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||||
|
if self.settings["context"].ContextType == "Model":
|
||||||
|
return self.create_model_representation()
|
||||||
|
elif self.settings["context"].ContextType == "Plan":
|
||||||
|
return self.create_plan_representation()
|
||||||
|
return self.create_variable_representation()
|
||||||
|
|
||||||
|
def create_model_representation(self):
|
||||||
|
if self.settings["context"].is_a() == "IfcGeometricRepresentationContext":
|
||||||
|
return self.create_variable_representation()
|
||||||
|
if self.settings["context"].ContextIdentifier == "Annotation":
|
||||||
|
return self.create_geometric_set_representation()
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Axis":
|
||||||
|
return self.create_curve3d_representation()
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Body":
|
||||||
|
return self.create_variable_representation()
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Box":
|
||||||
|
return self.create_box_representation()
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Clearance":
|
||||||
|
return self.create_variable_representation()
|
||||||
|
elif self.settings["context"].ContextIdentifier == "CoG":
|
||||||
|
return self.create_cog_representation()
|
||||||
|
elif self.settings["context"].ContextIdentifier == "FootPrint":
|
||||||
|
return self.create_variable_representation()
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Reference":
|
||||||
|
if self.settings["context"].TargetView == "GRAPH_VIEW":
|
||||||
|
return self.create_structural_reference_representation()
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Profile":
|
||||||
|
return self.create_curve3d_representation()
|
||||||
|
elif self.settings["context"].ContextIdentifier == "SurveyPoints":
|
||||||
|
return self.create_geometric_curve_set_representation()
|
||||||
|
|
||||||
|
def create_plan_representation(self):
|
||||||
|
if self.settings["context"].ContextIdentifier == "Annotation":
|
||||||
|
if self.settings["is_text"]:
|
||||||
|
return self.create_text_representation()
|
||||||
|
shape_representation = self.create_geometric_curve_set_representation(is_2d=True)
|
||||||
|
shape_representation.RepresentationType = "Annotation2D"
|
||||||
|
return shape_representation
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Axis":
|
||||||
|
return self.create_curve2d_representation()
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Body":
|
||||||
|
pass
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Box":
|
||||||
|
pass
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Clearance":
|
||||||
|
pass
|
||||||
|
elif self.settings["context"].ContextIdentifier == "CoG":
|
||||||
|
pass
|
||||||
|
elif self.settings["context"].ContextIdentifier == "FootPrint":
|
||||||
|
if self.settings["context"].TargetView in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||||
|
return self.create_geometric_curve_set_representation(is_2d=True)
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Reference":
|
||||||
|
pass
|
||||||
|
elif self.settings["context"].ContextIdentifier == "Profile":
|
||||||
|
pass
|
||||||
|
elif self.settings["context"].ContextIdentifier == "SurveyPoints":
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_variable_representation(self):
|
||||||
|
if self.settings["is_wireframe"]:
|
||||||
|
return self.create_wireframe_representation()
|
||||||
|
elif self.settings["is_curve"]:
|
||||||
|
return self.create_curve_representation()
|
||||||
|
elif self.settings["is_point_cloud"]:
|
||||||
|
return self.create_point_cloud_representation()
|
||||||
|
return self.create_mesh_representation()
|
||||||
|
|
||||||
|
def create_mesh_representation(self):
|
||||||
|
if self.file.schema == "IFC2X3" or self.settings["should_force_faceted_brep"]:
|
||||||
|
return self.create_faceted_brep()
|
||||||
|
return self.create_polygonal_face_set()
|
||||||
|
|
||||||
|
def create_faceted_brep(self):
|
||||||
|
self.create_vertices()
|
||||||
|
ifc_raw_items = [None] * self.settings["total_items"]
|
||||||
|
for i, value in enumerate(ifc_raw_items):
|
||||||
|
ifc_raw_items[i] = []
|
||||||
|
for polygon in self.settings["geometry"].polygons:
|
||||||
|
ifc_raw_items[polygon.material_index % self.settings["total_items"]].append(
|
||||||
|
self.file.createIfcFace(
|
||||||
|
[
|
||||||
|
self.file.createIfcFaceOuterBound(
|
||||||
|
self.file.createIfcPolyLoop([self.ifc_vertices[vertice] for vertice in polygon.vertices]),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# TODO: May not actually be a closed shell, but who checks anyway?
|
||||||
|
items = [self.file.createIfcFacetedBrep(self.file.createIfcClosedShell(i)) for i in ifc_raw_items if i]
|
||||||
|
return self.file.createIfcShapeRepresentation(
|
||||||
|
self.settings["context"],
|
||||||
|
self.settings["context"].ContextIdentifier,
|
||||||
|
"Brep",
|
||||||
|
items,
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_polygonal_face_set(self):
|
||||||
|
ifc_raw_items = [None] * self.settings["total_items"]
|
||||||
|
for i, value in enumerate(ifc_raw_items):
|
||||||
|
ifc_raw_items[i] = []
|
||||||
|
for polygon in self.settings["geometry"].polygons:
|
||||||
|
ifc_raw_items[polygon.material_index % self.settings["total_items"]].append(
|
||||||
|
self.file.createIfcIndexedPolygonalFace([v + 1 for v in polygon.vertices])
|
||||||
|
)
|
||||||
|
coordinates = self.file.createIfcCartesianPointList3D(
|
||||||
|
[self.convert_si_to_unit(v.co) for v in self.settings["geometry"].vertices]
|
||||||
|
)
|
||||||
|
items = [self.file.createIfcPolygonalFaceSet(coordinates, None, i) for i in ifc_raw_items if i]
|
||||||
|
return self.file.createIfcShapeRepresentation(
|
||||||
|
self.settings["context"],
|
||||||
|
self.settings["context"].ContextIdentifier,
|
||||||
|
"Tessellation",
|
||||||
|
items,
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_vertices(self, is_2d=False):
|
||||||
|
if is_2d:
|
||||||
|
for v in self.settings["geometry"].vertices:
|
||||||
|
co = self.convert_si_to_unit(v.co)
|
||||||
|
self.ifc_vertices.append(self.file.createIfcCartesianPoint((co[0], co[1])))
|
||||||
|
return
|
||||||
|
self.ifc_vertices.extend(
|
||||||
|
[
|
||||||
|
self.file.createIfcCartesianPoint(self.convert_si_to_unit(v.co))
|
||||||
|
for v in self.settings["geometry"].vertices
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def convert_si_to_unit(self, co):
|
||||||
|
return co / self.settings["unit_scale"]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {
|
||||||
|
"product": None,
|
||||||
|
"representation": None
|
||||||
|
}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
definition = self.settings["product"].Representation
|
||||||
|
if not definition:
|
||||||
|
definition = self.file.createIfcProductDefinitionShape()
|
||||||
|
self.settings["product"].Representation = definition
|
||||||
|
representations = list(definition.Representations) if definition.Representations else []
|
||||||
|
representations.append(self.settings["representation"])
|
||||||
|
definition.Representations = representations
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {
|
||||||
|
"shape_representation": None,
|
||||||
|
"styles": [],
|
||||||
|
"should_use_presentation_style_assignment": False,
|
||||||
|
}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
if not self.settings["styles"]:
|
||||||
|
return []
|
||||||
|
self.results = []
|
||||||
|
for element in self.file.traverse(self.settings["shape_representation"]):
|
||||||
|
if not element.is_a("IfcShapeRepresentation"):
|
||||||
|
continue
|
||||||
|
for item in element.Items:
|
||||||
|
if not item.is_a("IfcGeometricRepresentationItem"):
|
||||||
|
continue
|
||||||
|
style = self.settings["styles"].pop(0)
|
||||||
|
name = style.Name
|
||||||
|
if self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]:
|
||||||
|
style = self.file.createIfcPresentationStyleAssignment([style])
|
||||||
|
self.results.append(
|
||||||
|
self.file.createIfcStyledItem(
|
||||||
|
item, [style], name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return self.results
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class Data:
|
||||||
|
products = {}
|
||||||
|
representations = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls, product_id):
|
||||||
|
file = IfcStore.get_file()
|
||||||
|
if not file:
|
||||||
|
return
|
||||||
|
cls.products[product_id] = []
|
||||||
|
product = file.by_id(product_id)
|
||||||
|
if not hasattr(product, "Representation") or not product.Representation:
|
||||||
|
return
|
||||||
|
for representation in product.Representation.Representations:
|
||||||
|
c = representation.ContextOfItems
|
||||||
|
rep_id = int(representation.id())
|
||||||
|
cls.representations[rep_id] = {
|
||||||
|
"RepresentationIdentifier": representation.RepresentationIdentifier,
|
||||||
|
"RepresentationType": representation.RepresentationType,
|
||||||
|
"ContextOfItems": {
|
||||||
|
"ContextType": c.ContextType,
|
||||||
|
"ContextIdentifier": c.ContextIdentifier,
|
||||||
|
"TargetView": c.TargetView if c.is_a("IfcGeometricRepresentationSubContext") else "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cls.products[product_id].append(rep_id)
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import numpy as np
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
import ifcopenshell.util.placement
|
||||||
|
|
||||||
|
|
||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {"product": None, "matrix": np.eye(4)}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
if not hasattr(self.settings["product"], "ObjectPlacement"):
|
||||||
|
return
|
||||||
|
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||||
|
|
||||||
|
dependent_objects = []
|
||||||
|
if self.settings["product"].ObjectPlacement:
|
||||||
|
for referenced_placement in self.settings["product"].ObjectPlacement.ReferencedByPlacements:
|
||||||
|
for placed_obj in referenced_placement.PlacesObject:
|
||||||
|
dependent_objects.append(
|
||||||
|
{
|
||||||
|
"product": placed_obj,
|
||||||
|
"matrix": ifcopenshell.util.placement.get_local_placement(referenced_placement),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
placement_rel_to = None
|
||||||
|
if hasattr(self.settings["product"], "ContainedInStructure") and self.settings["product"].ContainedInStructure:
|
||||||
|
placement_rel_to = self.settings["product"].ContainedInStructure[0].RelatingStructure.ObjectPlacement
|
||||||
|
elif hasattr(self.settings["product"], "Decomposes") and self.settings["product"].Decomposes:
|
||||||
|
relating_object = self.settings["product"].Decomposes[0].RelatingObject
|
||||||
|
placement_rel_to = relating_object.ObjectPlacement if hasattr(relating_object, "ObjectPlacement") else None
|
||||||
|
|
||||||
|
placement = self.file.createIfcLocalPlacement(placement_rel_to, self.get_relative_placement(placement_rel_to))
|
||||||
|
if self.settings["product"].ObjectPlacement:
|
||||||
|
for inverse in self.file.get_inverse(self.settings["product"]):
|
||||||
|
ifcopenshell.util.element.replace_attribute(
|
||||||
|
inverse, self.settings["product"].ObjectPlacement, placement
|
||||||
|
)
|
||||||
|
self.file.remove(self.settings["product"].ObjectPlacement)
|
||||||
|
self.settings["product"].ObjectPlacement = placement
|
||||||
|
|
||||||
|
for settings in dependent_objects:
|
||||||
|
self.settings = settings
|
||||||
|
self.execute()
|
||||||
|
|
||||||
|
return placement
|
||||||
|
|
||||||
|
def get_relative_placement(self, placement_rel_to):
|
||||||
|
if placement_rel_to:
|
||||||
|
relating_object_matrix = ifcopenshell.util.placement.get_local_placement(placement_rel_to)
|
||||||
|
relating_object_matrix[0][3] = self.convert_unit_to_si(relating_object_matrix[0][3])
|
||||||
|
relating_object_matrix[1][3] = self.convert_unit_to_si(relating_object_matrix[1][3])
|
||||||
|
relating_object_matrix[2][3] = self.convert_unit_to_si(relating_object_matrix[2][3])
|
||||||
|
else:
|
||||||
|
relating_object_matrix = np.eye(4)
|
||||||
|
m = self.settings["matrix"]
|
||||||
|
x = np.array((m[0][0], m[1][0], m[2][0]))
|
||||||
|
z = np.array((m[0][2], m[1][2], m[2][2]))
|
||||||
|
o = np.array((m[0][3], m[1][3], m[2][3]))
|
||||||
|
object_matrix = ifcopenshell.util.placement.a2p(o, z, x)
|
||||||
|
relative_placement_matrix = np.linalg.inv(relating_object_matrix) @ object_matrix
|
||||||
|
return self.create_ifc_axis_2_placement_3d(
|
||||||
|
relative_placement_matrix[:, 3][0:3],
|
||||||
|
relative_placement_matrix[:, 2][0:3],
|
||||||
|
relative_placement_matrix[:, 0][0:3],
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_ifc_axis_2_placement_3d(self, point, up, forward):
|
||||||
|
return self.file.createIfcAxis2Placement3D(
|
||||||
|
self.create_cartesian_point(point),
|
||||||
|
self.file.createIfcDirection(up.tolist()),
|
||||||
|
self.file.createIfcDirection(forward.tolist()),
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_cartesian_point(self, co):
|
||||||
|
co = self.convert_si_to_unit(co)
|
||||||
|
return self.file.createIfcCartesianPoint(co.tolist())
|
||||||
|
|
||||||
|
def convert_si_to_unit(self, co):
|
||||||
|
return co / self.unit_scale
|
||||||
|
|
||||||
|
def convert_unit_to_si(self, co):
|
||||||
|
return co * self.unit_scale
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
import bpy
|
||||||
|
import numpy as np
|
||||||
|
import ifcopenshell
|
||||||
|
import logging
|
||||||
|
import blenderbim.bim.module.geometry.edit_object_placement as edit_object_placement
|
||||||
|
import blenderbim.bim.module.geometry.add_representation as add_representation
|
||||||
|
import blenderbim.bim.module.geometry.assign_styles as assign_styles
|
||||||
|
import blenderbim.bim.module.geometry.assign_representation as assign_representation
|
||||||
|
import blenderbim.bim.module.geometry.remove_representation as remove_representation
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim import import_ifc
|
||||||
|
from blenderbim.bim.module.geometry.data import Data
|
||||||
|
|
||||||
|
|
||||||
|
class EditObjectPlacement(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.edit_object_placement"
|
||||||
|
bl_label = "Edit Object Placement"
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
# TODO: determine how to deal with this module dependency
|
||||||
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
|
matrix = np.array(obj.matrix_world)
|
||||||
|
if props.has_blender_offset and props.blender_offset_type == "OBJECT_PLACEMENT":
|
||||||
|
self.calculate_unit_scale()
|
||||||
|
# TODO: np.array? Why not matrix?
|
||||||
|
matrix = np.array(
|
||||||
|
ifcopenshell.util.geolocation.local2global(
|
||||||
|
np.matrix(obj.matrix_world),
|
||||||
|
float(props.blender_eastings) * self.unit_scale,
|
||||||
|
float(props.blender_northings) * self.unit_scale,
|
||||||
|
float(props.blender_orthogonal_height) * self.unit_scale,
|
||||||
|
float(props.blender_x_axis_abscissa),
|
||||||
|
float(props.blender_x_axis_ordinate),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
edit_object_placement.Usecase(
|
||||||
|
self.file,
|
||||||
|
{
|
||||||
|
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||||
|
"matrix": matrix,
|
||||||
|
},
|
||||||
|
).execute()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def calculate_unit_scale(self):
|
||||||
|
self.unit_scale = 1
|
||||||
|
units = self.file.by_type("IfcUnitAssignment")[0]
|
||||||
|
for unit in units.Units:
|
||||||
|
if not hasattr(unit, "UnitType") or unit.UnitType != "LENGTHUNIT":
|
||||||
|
continue
|
||||||
|
while unit.is_a("IfcConversionBasedUnit"):
|
||||||
|
self.unit_scale *= unit.ConversionFactor.ValueComponent.wrappedValue
|
||||||
|
unit = unit.ConversionFactor.UnitComponent
|
||||||
|
if unit.is_a("IfcSIUnit"):
|
||||||
|
self.unit_scale *= ifcopenshell.util.unit.get_prefix_multiplier(unit.Prefix)
|
||||||
|
|
||||||
|
|
||||||
|
class AddRepresentation(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.add_representation"
|
||||||
|
bl_label = "Add Representation"
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
|
context_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
context_id = self.context_id or int(bpy.context.scene.BIMProperties.contexts)
|
||||||
|
|
||||||
|
bpy.ops.bim.edit_object_placement(obj=obj.name)
|
||||||
|
|
||||||
|
if obj.data:
|
||||||
|
result = add_representation.Usecase(
|
||||||
|
self.file,
|
||||||
|
{
|
||||||
|
"context": self.file.by_id(context_id),
|
||||||
|
"geometry": obj.data,
|
||||||
|
"total_items": max(1, len(obj.material_slots)),
|
||||||
|
},
|
||||||
|
).execute()
|
||||||
|
if not result:
|
||||||
|
print("Failed to write shape representation")
|
||||||
|
return {"FINISHED"}
|
||||||
|
assign_styles.Usecase(
|
||||||
|
self.file,
|
||||||
|
{
|
||||||
|
"shape_representation": result,
|
||||||
|
"styles": [
|
||||||
|
self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id)
|
||||||
|
for s in obj.material_slots
|
||||||
|
if s.material
|
||||||
|
],
|
||||||
|
},
|
||||||
|
).execute()
|
||||||
|
assign_representation.Usecase(
|
||||||
|
self.file,
|
||||||
|
{"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), "representation": result},
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
existing_mesh = obj.data
|
||||||
|
mesh = obj.data.copy()
|
||||||
|
mesh.name = "{}/{}".format(context_id, result.id())
|
||||||
|
mesh.BIMMeshProperties.ifc_definition_id = int(result.id())
|
||||||
|
obj.data = mesh
|
||||||
|
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class SwitchRepresentation(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.switch_representation"
|
||||||
|
bl_label = "Switch Representation"
|
||||||
|
ifc_definition_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.obj = bpy.context.active_object
|
||||||
|
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
context_of_items = self.file.by_id(self.ifc_definition_id).ContextOfItems
|
||||||
|
self.mesh_name = "{}/{}".format(context_of_items.id(), self.ifc_definition_id)
|
||||||
|
|
||||||
|
mesh = bpy.data.meshes.get(self.mesh_name)
|
||||||
|
if mesh:
|
||||||
|
self.obj.data.user_remap(mesh)
|
||||||
|
self.pull_mesh_from_ifc()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def pull_mesh_from_ifc(self):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
logger = logging.getLogger("ImportIFC")
|
||||||
|
ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, IfcStore.path, logger)
|
||||||
|
element = self.file.by_id(self.obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
settings = ifcopenshell.geom.settings()
|
||||||
|
settings.set(settings.INCLUDE_CURVES, True)
|
||||||
|
shape = ifcopenshell.geom.create_shape(settings, self.file.by_id(self.ifc_definition_id))
|
||||||
|
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
||||||
|
ifc_importer.file = self.file
|
||||||
|
mesh = ifc_importer.create_mesh(element, shape)
|
||||||
|
mesh.name = self.mesh_name
|
||||||
|
mesh.BIMMeshProperties.ifc_definition_id = self.ifc_definition_id
|
||||||
|
self.obj.data.user_remap(mesh)
|
||||||
|
material_creator = import_ifc.MaterialCreator(ifc_import_settings, ifc_importer)
|
||||||
|
material_creator.create(element, self.obj, mesh)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveRepresentation(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.remove_representation"
|
||||||
|
bl_label = "Remove Representation"
|
||||||
|
ifc_definition_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
representation = self.file.by_id(self.ifc_definition_id)
|
||||||
|
obj = bpy.context.active_object
|
||||||
|
mesh = bpy.data.meshes.get("{}/{}".format(representation.ContextOfItems.id(), representation.id()))
|
||||||
|
if mesh:
|
||||||
|
if obj.data == mesh:
|
||||||
|
# TODO we can do better than this
|
||||||
|
void_mesh = bpy.data.meshes.get("Void")
|
||||||
|
if not void_mesh:
|
||||||
|
void_mesh = bpy.data.meshes.new("Void")
|
||||||
|
obj.data = void_mesh
|
||||||
|
bpy.data.meshes.remove(mesh)
|
||||||
|
remove_representation.Usecase(self.file, {"representation": representation}).execute()
|
||||||
|
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateMeshRepresentation(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.update_mesh_representation"
|
||||||
|
bl_label = "Update Mesh Representation"
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
|
||||||
|
bpy.ops.bim.edit_object_placement(obj=obj.name)
|
||||||
|
|
||||||
|
old_representation = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||||
|
new_representation = add_representation.Usecase(
|
||||||
|
self.file,
|
||||||
|
{
|
||||||
|
"context": old_representation.ContextOfItems,
|
||||||
|
"geometry": obj.data,
|
||||||
|
"total_items": max(1, len(obj.material_slots)),
|
||||||
|
},
|
||||||
|
).execute()
|
||||||
|
if not new_representation:
|
||||||
|
print("Failed to write shape representation")
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
assign_styles.Usecase(
|
||||||
|
self.file,
|
||||||
|
{
|
||||||
|
"shape_representation": new_representation,
|
||||||
|
"styles": [
|
||||||
|
self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id)
|
||||||
|
for s in obj.material_slots
|
||||||
|
if s.material
|
||||||
|
],
|
||||||
|
},
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
# TODO: move this into a replace_representation usecase or something
|
||||||
|
for inverse in self.file.get_inverse(old_representation):
|
||||||
|
ifcopenshell.util.element.replace_attribute(inverse, old_representation, new_representation)
|
||||||
|
|
||||||
|
obj.data.BIMMeshProperties.ifc_definition_id = int(new_representation.id())
|
||||||
|
obj.data.name = f"{old_representation.ContextOfItems.id()}/{new_representation.id()}"
|
||||||
|
bpy.ops.bim.remove_representation(ifc_definition_id=old_representation.id())
|
||||||
|
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateParametricRepresentation(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.update_parametric_representation"
|
||||||
|
bl_label = "Update Parametric Representation"
|
||||||
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
obj = bpy.context.active_object
|
||||||
|
props = obj.data.BIMMeshProperties
|
||||||
|
parameter = props.ifc_parameters[self.index]
|
||||||
|
element = IfcStore.get_file().by_id(parameter.step_id)[parameter.index] = parameter.value
|
||||||
|
bpy.ops.bim.switch_representation(ifc_definition_id=props.ifc_definition_id)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class GetRepresentationIfcParameters(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.get_representation_ifc_parameters"
|
||||||
|
bl_label = "Get Representation IFC Parameters"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
obj = bpy.context.active_object
|
||||||
|
props = obj.data.BIMMeshProperties
|
||||||
|
elements = IfcStore.get_file().traverse(IfcStore.get_file().by_id(props.ifc_definition_id))
|
||||||
|
for element in elements:
|
||||||
|
if not element.is_a("IfcRepresentationItem"):
|
||||||
|
continue
|
||||||
|
for i in range(0, len(element)):
|
||||||
|
if element.attribute_type(i) == "DOUBLE":
|
||||||
|
new = props.ifc_parameters.add()
|
||||||
|
new.name = "{}/{}".format(element.is_a(), element.attribute_name(i))
|
||||||
|
new.step_id = element.id()
|
||||||
|
new.type = element.attribute_type(i)
|
||||||
|
new.index = i
|
||||||
|
if element[i]:
|
||||||
|
new.value = element[i]
|
||||||
|
return {"FINISHED"}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {"representation": None}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
styles = []
|
||||||
|
for subelement in self.file.traverse(self.settings["representation"]):
|
||||||
|
if subelement.is_a("IfcRepresentationItem") and subelement.StyledByItem:
|
||||||
|
styles.append(subelement)
|
||||||
|
for style in styles:
|
||||||
|
self.remove_deep(style)
|
||||||
|
self.remove_deep(self.settings["representation"])
|
||||||
|
|
||||||
|
def remove_deep(self, element):
|
||||||
|
subgraph = list(self.file.traverse(element))
|
||||||
|
subgraph_set = set(subgraph)
|
||||||
|
for ref in subgraph[::-1]:
|
||||||
|
if ref.id() and len(set(self.file.get_inverse(ref)) - subgraph_set) == 0:
|
||||||
|
self.file.remove(ref)
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import bpy
|
||||||
|
from bpy.types import Panel
|
||||||
|
from blenderbim.bim.module.geometry.data import Data
|
||||||
|
|
||||||
|
|
||||||
|
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 props.ifc_definition_id not in Data.products:
|
||||||
|
Data.load(props.ifc_definition_id)
|
||||||
|
|
||||||
|
representations = Data.products[props.ifc_definition_id]
|
||||||
|
if not representations:
|
||||||
|
layout.label(text="No representations found")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(bpy.context.scene.BIMProperties, "contexts", text="")
|
||||||
|
row.operator("bim.add_representation", icon="ADD", text="")
|
||||||
|
|
||||||
|
for ifc_definition_id in representations:
|
||||||
|
representation = Data.representations[ifc_definition_id]
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text=representation["ContextOfItems"]["ContextType"])
|
||||||
|
row.label(text=representation["ContextOfItems"]["ContextIdentifier"])
|
||||||
|
row.label(text=representation["ContextOfItems"]["TargetView"])
|
||||||
|
row.label(text=representation["RepresentationType"])
|
||||||
|
row.operator("bim.switch_representation", icon="OUTLINER_DATA_MESH", text="").ifc_definition_id = ifc_definition_id
|
||||||
|
row.operator("bim.remove_representation", icon="X", text="").ifc_definition_id = ifc_definition_id
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_mesh(Panel):
|
||||||
|
bl_label = "IFC Representation"
|
||||||
|
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()
|
||||||
|
row.operator("bim.get_representation_ifc_parameters")
|
||||||
|
row = layout.row()
|
||||||
|
row.operator("bim.update_mesh_representation")
|
||||||
|
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_parametric_representation", icon="FILE_REFRESH", text="").index = index
|
||||||
|
|
||||||
|
|
||||||
|
def BIM_PT_transform(self, context):
|
||||||
|
if context.active_object and context.active_object.BIMObjectProperties.ifc_definition_id:
|
||||||
|
row = self.layout.row()
|
||||||
|
row.operator("bim.edit_object_placement")
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui, prop, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.EnableEditingGeoreferencing,
|
||||||
|
operator.DisableEditingGeoreferencing,
|
||||||
|
operator.EditGeoreferencing,
|
||||||
|
operator.SetNorthOffset,
|
||||||
|
operator.GetNorthOffset,
|
||||||
|
operator.RemoveGeoreferencing,
|
||||||
|
operator.AddGeoreferencing,
|
||||||
|
operator.ConvertLocalToGlobal,
|
||||||
|
operator.ConvertGlobalToLocal,
|
||||||
|
prop.BIMGeoreferenceProperties,
|
||||||
|
ui.BIM_PT_gis,
|
||||||
|
ui.BIM_PT_gis_utilities,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.Scene.BIMGeoreferenceProperties = bpy.props.PointerProperty(type=prop.BIMGeoreferenceProperties)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
del bpy.types.Scene.BIMGeoreferenceProperties
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
source_crs = None
|
||||||
|
for context in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
|
if context.ContextType == "Model":
|
||||||
|
source_crs = context
|
||||||
|
break
|
||||||
|
if not source_crs:
|
||||||
|
return
|
||||||
|
projected_crs = self.file.create_entity("IfcProjectedCRS", **{"Name": ""})
|
||||||
|
self.file.create_entity("IfcMapConversion", **{
|
||||||
|
"SourceCRS": source_crs,
|
||||||
|
"TargetCRS": projected_crs,
|
||||||
|
"Eastings": 0,
|
||||||
|
"Northings": 0,
|
||||||
|
"OrthogonalHeight": 0,
|
||||||
|
})
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class Data:
|
||||||
|
is_loaded = False
|
||||||
|
map_conversion = {}
|
||||||
|
projected_crs = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls):
|
||||||
|
file = IfcStore.get_file()
|
||||||
|
if not file:
|
||||||
|
return
|
||||||
|
cls.map_conversion = {}
|
||||||
|
cls.projected_crs = {}
|
||||||
|
if file.schema == "IFC2X3":
|
||||||
|
return
|
||||||
|
for context in file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
|
if not context.HasCoordinateOperation:
|
||||||
|
continue
|
||||||
|
map_conversion = context.HasCoordinateOperation[0]
|
||||||
|
cls.map_conversion = map_conversion.get_info()
|
||||||
|
cls.map_conversion["SourceCRS"] = cls.map_conversion["SourceCRS"].id()
|
||||||
|
cls.map_conversion["TargetCRS"] = cls.map_conversion["TargetCRS"].id()
|
||||||
|
cls.projected_crs = map_conversion.TargetCRS.get_info()
|
||||||
|
if cls.projected_crs["MapUnit"]:
|
||||||
|
cls.projected_crs["MapUnit"] = map_conversion.TargetCRS.MapUnit.get_info()
|
||||||
|
break
|
||||||
|
cls.is_loaded = True
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import ifcopenshell.util.unit
|
||||||
|
|
||||||
|
|
||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {
|
||||||
|
"map_conversion": {},
|
||||||
|
"projected_crs": {},
|
||||||
|
"map_unit": "",
|
||||||
|
}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
map_conversion = self.file.by_type("IfcMapConversion")[0]
|
||||||
|
projected_crs = self.file.by_type("IfcProjectedCRS")[0]
|
||||||
|
for name, value in self.settings["map_conversion"].items():
|
||||||
|
setattr(map_conversion, name, value)
|
||||||
|
for name, value in self.settings["projected_crs"].items():
|
||||||
|
setattr(projected_crs, name, value)
|
||||||
|
self.remove_existing_map_unit(projected_crs)
|
||||||
|
self.set_map_unit(projected_crs)
|
||||||
|
|
||||||
|
def remove_existing_map_unit(self, projected_crs):
|
||||||
|
if projected_crs.MapUnit and len(self.file.get_inverse(projected_crs.MapUnit)) == 1:
|
||||||
|
# TODO: go deeper for conversion units
|
||||||
|
self.file.remove(projected_crs.MapUnit)
|
||||||
|
|
||||||
|
def set_map_unit(self, projected_crs):
|
||||||
|
if not self.settings["map_unit"]:
|
||||||
|
return
|
||||||
|
|
||||||
|
if "METRE" in self.settings["map_unit"]:
|
||||||
|
projected_crs.MapUnit = self.file.createIfcSIUnit(
|
||||||
|
None,
|
||||||
|
"LENGTHUNIT",
|
||||||
|
ifcopenshell.util.unit.get_prefix(self.settings["map_unit"]),
|
||||||
|
ifcopenshell.util.unit.get_unit_name(self.settings["map_unit"]),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
value_component = self.file.create_entity(
|
||||||
|
"IfcReal", **{"wrappedValue": ifcopenshell.util.unit.si_conversions[self.settings["map_unit"]]}
|
||||||
|
)
|
||||||
|
si_unit = self.file.createIfcSIUnit(None, "LENGTHUNIT", None, "METRE")
|
||||||
|
projected_crs.MapUnit = self.file.createIfcConversionBasedUnit(
|
||||||
|
self.file.createIfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0),
|
||||||
|
"LENGTHUNIT",
|
||||||
|
self.settings["map_unit"],
|
||||||
|
self.file.createIfcMeasureWithUnit(value_component, si_unit),
|
||||||
|
)
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
import bpy
|
||||||
|
import json
|
||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.unit
|
||||||
|
import blenderbim.bim.module.georeference.add_georeferencing as add_georeferencing
|
||||||
|
import blenderbim.bim.module.georeference.edit_georeferencing as edit_georeferencing
|
||||||
|
import blenderbim.bim.module.georeference.remove_georeferencing as remove_georeferencing
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.module.georeference.data import Data
|
||||||
|
from math import radians, degrees, atan, tan, cos, sin
|
||||||
|
|
||||||
|
|
||||||
|
class EnableEditingGeoreferencing(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.enable_editing_georeferencing"
|
||||||
|
bl_label = "Enable Editing Georeferencing"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
|
|
||||||
|
while len(props.map_conversion) > 0:
|
||||||
|
props.map_conversion.remove(0)
|
||||||
|
|
||||||
|
for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes():
|
||||||
|
data_type = str(attribute.type_of_attribute)
|
||||||
|
if "<entity" in data_type:
|
||||||
|
continue
|
||||||
|
new = props.map_conversion.add()
|
||||||
|
new.name = attribute.name()
|
||||||
|
new.is_null = Data.map_conversion[attribute.name()] is None
|
||||||
|
new.is_optional = attribute.optional()
|
||||||
|
if "<string>" in data_type:
|
||||||
|
new.string_value = "" if new.is_null else Data.map_conversion[attribute.name()]
|
||||||
|
new.data_type = "string"
|
||||||
|
elif "<real>" in data_type:
|
||||||
|
new.float_value = 0.0 if new.is_null else Data.map_conversion[attribute.name()]
|
||||||
|
new.data_type = "float"
|
||||||
|
elif "<integer>" in data_type:
|
||||||
|
new.int_value = 0 if new.is_null else Data.map_conversion[attribute.name()]
|
||||||
|
new.data_type = "integer"
|
||||||
|
elif "<boolean>" in data_type or "<logical>" in data_type:
|
||||||
|
new.bool_value = False if new.is_null else Data.map_conversion[attribute.name()]
|
||||||
|
new.data_type = "boolean"
|
||||||
|
|
||||||
|
while len(props.projected_crs) > 0:
|
||||||
|
props.projected_crs.remove(0)
|
||||||
|
|
||||||
|
for attribute in IfcStore.get_schema().declaration_by_name("IfcProjectedCRS").all_attributes():
|
||||||
|
data_type = str(attribute.type_of_attribute)
|
||||||
|
if "<entity" in data_type:
|
||||||
|
continue
|
||||||
|
new = props.projected_crs.add()
|
||||||
|
new.name = attribute.name()
|
||||||
|
new.is_null = Data.projected_crs[attribute.name()] is None
|
||||||
|
new.is_optional = attribute.optional()
|
||||||
|
if "<string>" in data_type:
|
||||||
|
new.string_value = "" if new.is_null else Data.projected_crs[attribute.name()]
|
||||||
|
new.data_type = "string"
|
||||||
|
elif "<real>" in data_type:
|
||||||
|
new.float_value = 0.0 if new.is_null else Data.projected_crs[attribute.name()]
|
||||||
|
new.data_type = "float"
|
||||||
|
elif "<integer>" in data_type:
|
||||||
|
new.int_value = 0 if new.is_null else Data.projected_crs[attribute.name()]
|
||||||
|
new.data_type = "integer"
|
||||||
|
elif "<boolean>" in data_type or "<logical>" in data_type:
|
||||||
|
new.bool_value = False if new.is_null else Data.projected_crs[attribute.name()]
|
||||||
|
new.data_type = "boolean"
|
||||||
|
|
||||||
|
props.is_map_unit_null = Data.projected_crs["MapUnit"] is None
|
||||||
|
if not props.is_map_unit_null:
|
||||||
|
props.map_unit_type = Data.projected_crs["MapUnit"]["type"]
|
||||||
|
if props.map_unit_type == "IfcSIUnit":
|
||||||
|
prefix = ifcopenshell.util.unit.get_prefix(Data.projected_crs["MapUnit"]["Prefix"]) or ""
|
||||||
|
name = ifcopenshell.util.unit.get_unit_name(Data.projected_crs["MapUnit"]["Name"])
|
||||||
|
props.map_unit_si = prefix + name
|
||||||
|
elif props.map_unit_type == "IfcConversionBasedUnit":
|
||||||
|
props.map_unit_imperial = Data.projected_crs["MapUnit"]["Name"]
|
||||||
|
|
||||||
|
props.is_editing = True
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class DisableEditingGeoreferencing(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.disable_editing_georeferencing"
|
||||||
|
bl_label = "Disable Editing Georeferencing"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
|
props.is_editing = False
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class EditGeoreferencing(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.edit_georeferencing"
|
||||||
|
bl_label = "Edit Georeferencing"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
|
|
||||||
|
map_conversion = {}
|
||||||
|
for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes():
|
||||||
|
data_type = str(attribute.type_of_attribute)
|
||||||
|
if "<entity" in data_type:
|
||||||
|
continue
|
||||||
|
blender_attribute = props.map_conversion.get(attribute.name())
|
||||||
|
if blender_attribute.is_null:
|
||||||
|
map_conversion[attribute.name()] = None
|
||||||
|
elif blender_attribute.data_type == "string":
|
||||||
|
map_conversion[attribute.name()] = blender_attribute.string_value
|
||||||
|
elif blender_attribute.data_type == "float":
|
||||||
|
map_conversion[attribute.name()] = blender_attribute.float_value
|
||||||
|
elif blender_attribute.data_type == "integer":
|
||||||
|
map_conversion[attribute.name()] = blender_attribute.int_value
|
||||||
|
elif blender_attribute.data_type == "boolean":
|
||||||
|
map_conversion[attribute.name()] = blender_attribute.bool_value
|
||||||
|
|
||||||
|
projected_crs = {}
|
||||||
|
for attribute in IfcStore.get_schema().declaration_by_name("IfcProjectedCRS").all_attributes():
|
||||||
|
data_type = str(attribute.type_of_attribute)
|
||||||
|
if "<entity" in data_type:
|
||||||
|
continue
|
||||||
|
blender_attribute = props.projected_crs.get(attribute.name())
|
||||||
|
if blender_attribute.is_null:
|
||||||
|
projected_crs[attribute.name()] = None
|
||||||
|
elif blender_attribute.data_type == "string":
|
||||||
|
projected_crs[attribute.name()] = blender_attribute.string_value
|
||||||
|
elif blender_attribute.data_type == "float":
|
||||||
|
projected_crs[attribute.name()] = blender_attribute.float_value
|
||||||
|
elif blender_attribute.data_type == "integer":
|
||||||
|
projected_crs[attribute.name()] = blender_attribute.int_value
|
||||||
|
elif blender_attribute.data_type == "boolean":
|
||||||
|
projected_crs[attribute.name()] = blender_attribute.bool_value
|
||||||
|
|
||||||
|
map_unit = ""
|
||||||
|
if not props.is_map_unit_null:
|
||||||
|
map_unit = props.map_unit_si if props.map_unit_type == "IfcSIUnit" else props.map_unit_imperial
|
||||||
|
|
||||||
|
edit_georeferencing.Usecase(self.file, {
|
||||||
|
"map_conversion": map_conversion,
|
||||||
|
"projected_crs": projected_crs,
|
||||||
|
"map_unit": map_unit
|
||||||
|
}).execute()
|
||||||
|
Data.load()
|
||||||
|
bpy.ops.bim.disable_editing_georeferencing()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class SetNorthOffset(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.set_north_offset"
|
||||||
|
bl_label = "Set North Offset"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
context.scene.sun_pos_properties.north_offset = -radians(
|
||||||
|
ifcopenshell.util.geolocation.xy2angle(
|
||||||
|
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").float_value,
|
||||||
|
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").float_value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class GetNorthOffset(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.get_north_offset"
|
||||||
|
bl_label = "Get North Offset"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
x_angle = -context.scene.sun_pos_properties.north_offset
|
||||||
|
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").float_value = cos(x_angle)
|
||||||
|
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").float_value = sin(x_angle)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveGeoreferencing(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.remove_georeferencing"
|
||||||
|
bl_label = "Remove Georeferencing"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
remove_georeferencing.Usecase(IfcStore.get_file()).execute()
|
||||||
|
Data.load()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class AddGeoreferencing(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.add_georeferencing"
|
||||||
|
bl_label = "Add Georeferencing"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
add_georeferencing.Usecase(IfcStore.get_file()).execute()
|
||||||
|
Data.load()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class ConvertLocalToGlobal(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.convert_local_to_global"
|
||||||
|
bl_label = "Convert Local To Global"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if not Data.is_loaded:
|
||||||
|
Data.load()
|
||||||
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
|
x, y, z = [float(co) for co in props.coordinate_input.split(",")]
|
||||||
|
results = ifcopenshell.util.geolocation.xyz2enh(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
Data.map_conversion["Eastings"],
|
||||||
|
Data.map_conversion["Northings"],
|
||||||
|
Data.map_conversion["OrthogonalHeight"],
|
||||||
|
Data.map_conversion.get("XAxisAbscissa", 1.0),
|
||||||
|
Data.map_conversion.get("XAxisOrdinate", 0.0),
|
||||||
|
Data.map_conversion.get("Scale", 1.0),
|
||||||
|
)
|
||||||
|
props.coordinate_output = ",".join([str(r) for r in results])
|
||||||
|
bpy.context.scene.cursor.location = results
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class ConvertGlobalToLocal(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.convert_global_to_local"
|
||||||
|
bl_label = "Convert Global To Local"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if not Data.is_loaded:
|
||||||
|
Data.load()
|
||||||
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
|
x, y, z = [float(co) for co in props.coordinate_input.split(",")]
|
||||||
|
results = ifcopenshell.util.geolocation.enh2xyz(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
Data.map_conversion["Eastings"],
|
||||||
|
Data.map_conversion["Northings"],
|
||||||
|
Data.map_conversion["OrthogonalHeight"],
|
||||||
|
Data.map_conversion.get("XAxisAbscissa", 1.0),
|
||||||
|
Data.map_conversion.get("XAxisOrdinate", 0.0),
|
||||||
|
Data.map_conversion.get("Scale", 1.0),
|
||||||
|
)
|
||||||
|
props.coordinate_output = ",".join([str(r) for r in results])
|
||||||
|
bpy.context.scene.cursor.location = results
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import bpy
|
||||||
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
|
from bpy.types import PropertyGroup
|
||||||
|
from bpy.props import (
|
||||||
|
PointerProperty,
|
||||||
|
StringProperty,
|
||||||
|
EnumProperty,
|
||||||
|
BoolProperty,
|
||||||
|
IntProperty,
|
||||||
|
FloatProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BIMGeoreferenceProperties(PropertyGroup):
|
||||||
|
is_editing: BoolProperty(name="Is Editing")
|
||||||
|
map_conversion: CollectionProperty(name="Map Conversion", type=Attribute)
|
||||||
|
projected_crs: CollectionProperty(name="Projected CRS", type=Attribute)
|
||||||
|
map_unit_type: EnumProperty(
|
||||||
|
items=[(n, n, "") for n in ["IfcSIUnit", "IfcConversionBasedUnit"]],
|
||||||
|
name="Map Unit Type",
|
||||||
|
default="IfcSIUnit",
|
||||||
|
)
|
||||||
|
map_unit_si: EnumProperty(
|
||||||
|
items=[(n, n.lower().capitalize(), "") for n in ["MILLIMETRE", "CENTIMETRE", "METRE", "KILOMETRE"]],
|
||||||
|
name="Map Unit SI",
|
||||||
|
default="METRE",
|
||||||
|
)
|
||||||
|
map_unit_imperial: EnumProperty(
|
||||||
|
items=[(n, n.lower().capitalize(), "") for n in ["inch", "foot", "yard", "mile"]],
|
||||||
|
name="Map Unit SI",
|
||||||
|
default="foot",
|
||||||
|
)
|
||||||
|
is_map_unit_null: BoolProperty(name="Is Map Unit Null")
|
||||||
|
coordinate_input: StringProperty(name="Coordinate Input")
|
||||||
|
coordinate_output: StringProperty(name="Coordinate Output")
|
||||||
|
has_blender_offset: BoolProperty(name="Has Blender Offset")
|
||||||
|
blender_offset_type: EnumProperty(
|
||||||
|
items=[(o, o, "") for o in ["OBJECT_PLACEMENT", "CARTESIAN_POINT"]],
|
||||||
|
name="Blender Offset",
|
||||||
|
default="OBJECT_PLACEMENT",
|
||||||
|
)
|
||||||
|
blender_eastings: StringProperty(name="Blender Eastings", default="0")
|
||||||
|
blender_northings: StringProperty(name="Blender Northings", default="0")
|
||||||
|
blender_orthogonal_height: StringProperty(name="Blender Orthogonal Height", default="0")
|
||||||
|
blender_x_axis_abscissa: StringProperty(name="Blender X Axis Abscissa", default="1")
|
||||||
|
blender_x_axis_ordinate: StringProperty(name="Blender X Axis Ordinate", default="0")
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
map_conversion = self.file.by_type("IfcMapConversion")[0]
|
||||||
|
projected_crs = self.file.by_type("IfcProjectedCRS")[0]
|
||||||
|
if projected_crs.MapUnit and len(self.file.get_inverse(projected_crs.MapUnit)) == 1:
|
||||||
|
# TODO: go deeper for conversion units
|
||||||
|
self.file.remove(projected_crs.MapUnit)
|
||||||
|
self.file.remove(projected_crs)
|
||||||
|
self.file.remove(map_conversion)
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
from bpy.types import Panel
|
||||||
|
from blenderbim.bim.module.georeference.data import Data
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
class BIM_PT_gis(Panel):
|
||||||
|
bl_label = "IFC Georeferencing"
|
||||||
|
bl_idname = "BIM_PT_gis"
|
||||||
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return IfcStore.get_file()
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
|
if not Data.is_loaded:
|
||||||
|
Data.load()
|
||||||
|
|
||||||
|
if props.is_editing:
|
||||||
|
return self.draw_editable_ui(context)
|
||||||
|
self.draw_ui(context)
|
||||||
|
|
||||||
|
def draw_editable_ui(self, context):
|
||||||
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="Map Conversion", icon="GRID")
|
||||||
|
row.operator("bim.edit_georeferencing", icon="CHECKMARK", text="")
|
||||||
|
row.operator("bim.disable_editing_georeferencing", icon="X", text="")
|
||||||
|
|
||||||
|
for attribute in props.map_conversion:
|
||||||
|
if attribute.name == "XAxisAbscissa" and hasattr(context.scene, "sun_pos_properties"):
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.operator("bim.get_north_offset", text="Set IFC North")
|
||||||
|
row.operator("bim.set_north_offset", text="Set Blender North")
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
if attribute.data_type == "string":
|
||||||
|
row.prop(attribute, "string_value", text=attribute.name)
|
||||||
|
elif attribute.data_type == "integer":
|
||||||
|
row.prop(attribute, "int_value", text=attribute.name)
|
||||||
|
elif attribute.data_type == "float":
|
||||||
|
row.prop(attribute, "float_value", text=attribute.name)
|
||||||
|
elif attribute.data_type == "boolean":
|
||||||
|
row.prop(attribute, "bool_value", text=attribute.name)
|
||||||
|
if attribute.is_optional:
|
||||||
|
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||||
|
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="Projected CRS", icon="WORLD")
|
||||||
|
|
||||||
|
for attribute in props.projected_crs:
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
if attribute.data_type == "string":
|
||||||
|
row.prop(attribute, "string_value", text=attribute.name)
|
||||||
|
elif attribute.data_type == "integer":
|
||||||
|
row.prop(attribute, "int_value", text=attribute.name)
|
||||||
|
elif attribute.data_type == "float":
|
||||||
|
row.prop(attribute, "float_value", text=attribute.name)
|
||||||
|
elif attribute.data_type == "boolean":
|
||||||
|
row.prop(attribute, "bool_value", text=attribute.name)
|
||||||
|
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||||
|
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "map_unit_type", text="MapUnit")
|
||||||
|
if props.map_unit_type == "IfcSIUnit":
|
||||||
|
row.prop(props, "map_unit_si", text="")
|
||||||
|
elif props.map_unit_type == "IfcConversionBasedUnit":
|
||||||
|
row.prop(props, "map_unit_imperial", text="")
|
||||||
|
row.prop(props, "is_map_unit_null", icon="RADIOBUT_OFF" if props.is_map_unit_null else "RADIOBUT_ON", text="")
|
||||||
|
|
||||||
|
def draw_ui(self, context):
|
||||||
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
|
|
||||||
|
if not Data.map_conversion and IfcStore.get_file().schema != "IFC2X3":
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="Not Georeferenced")
|
||||||
|
row.operator("bim.add_georeferencing", icon="ADD", text="")
|
||||||
|
|
||||||
|
if props.has_blender_offset:
|
||||||
|
row = self.layout.row()
|
||||||
|
row.label(text="Blender Offset", icon="TRACKING_REFINE_FORWARDS")
|
||||||
|
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="Type")
|
||||||
|
row.label(text=props.blender_offset_type)
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="Eastings")
|
||||||
|
row.label(text=props.blender_eastings)
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="Northings")
|
||||||
|
row.label(text=props.blender_northings)
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="OrthogonalHeight")
|
||||||
|
row.label(text=props.blender_orthogonal_height)
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="XAxisAbscissa")
|
||||||
|
row.label(text=props.blender_x_axis_abscissa)
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="XAxisOrdinate")
|
||||||
|
row.label(text=props.blender_x_axis_ordinate)
|
||||||
|
elif IfcStore.get_file().schema == "IFC2X3":
|
||||||
|
row = self.layout.row()
|
||||||
|
row.label(text="Not Georeferenced")
|
||||||
|
|
||||||
|
if Data.map_conversion:
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="Map Conversion", icon="GRID")
|
||||||
|
row.operator("bim.enable_editing_georeferencing", icon="GREASEPENCIL", text="")
|
||||||
|
row.operator("bim.remove_georeferencing", icon="X", text="")
|
||||||
|
|
||||||
|
for key, value in Data.map_conversion.items():
|
||||||
|
if key == "id" or key == "type" or key == "SourceCRS" or key == "TargetCRS" or not value:
|
||||||
|
continue
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text=key)
|
||||||
|
row.label(text=str(value))
|
||||||
|
|
||||||
|
if Data.projected_crs:
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="Projected CRS", icon="WORLD")
|
||||||
|
|
||||||
|
for key, value in Data.projected_crs.items():
|
||||||
|
if key == "id" or key == "type" or not value:
|
||||||
|
continue
|
||||||
|
if key == "MapUnit":
|
||||||
|
unit_value = value.get("Prefix", "") or ""
|
||||||
|
unit_value += value["Name"]
|
||||||
|
value = unit_value
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text=key)
|
||||||
|
row.label(text=str(value))
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_gis_utilities(Panel):
|
||||||
|
bl_idname = "BIM_PT_gis_utilities"
|
||||||
|
bl_label = "Georeferencing Utilities"
|
||||||
|
bl_space_type = "VIEW_3D"
|
||||||
|
bl_region_type = "UI"
|
||||||
|
bl_category = "BlenderBIM"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
|
|
||||||
|
row = self.layout.row()
|
||||||
|
row.prop(props, "coordinate_input", text="Input")
|
||||||
|
row = self.layout.row()
|
||||||
|
row.prop(props, "coordinate_output", text="Output")
|
||||||
|
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.operator("bim.convert_local_to_global", text="Local to Global")
|
||||||
|
row.operator("bim.convert_global_to_local", text="Global to Local")
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui, prop, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.AssignMaterial,
|
||||||
|
operator.UnassignMaterial,
|
||||||
|
operator.AddConstituent,
|
||||||
|
operator.RemoveConstituent,
|
||||||
|
operator.AddLayer,
|
||||||
|
operator.RemoveLayer,
|
||||||
|
operator.AddListItem,
|
||||||
|
operator.RemoveListItem,
|
||||||
|
operator.EnableEditingAssignedMaterial,
|
||||||
|
operator.DisableEditingAssignedMaterial,
|
||||||
|
operator.EditAssignedMaterial,
|
||||||
|
operator.EnableEditingMaterialSetItem,
|
||||||
|
operator.DisableEditingMaterialSetItem,
|
||||||
|
operator.EditMaterialSetItem,
|
||||||
|
prop.BIMObjectMaterialProperties,
|
||||||
|
ui.BIM_PT_object_material,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.Object.BIMObjectMaterialProperties = bpy.props.PointerProperty(type=prop.BIMObjectMaterialProperties)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
del bpy.types.Object.BIMObjectMaterialProperties
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import ifcopenshell
|
||||||
|
|
||||||
|
|
||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {"constituent_set": None, "material": None}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
constituents = list(self.settings["constituent_set"].MaterialConstituents)
|
||||||
|
constituent = self.file.create_entity("IfcMaterialConstituent", **{"Material": self.settings["material"]})
|
||||||
|
constituents.append(constituent)
|
||||||
|
self.settings["constituent_set"].MaterialConstituents = constituents
|
||||||
|
return constituent
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user