mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 18:43:26 +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(IFCCONVERT_DOUBLE_PRECISION "IfcConvert: Use double precision floating-point numbers." 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_GEOMSERVER "Build IfcGeomServer 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
|
||||
userdata = context.config.userdata
|
||||
context.ifcbasename = userdata["ifcbasename"]
|
||||
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
|
||||
# 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"
|
||||
|
||||
|
||||
@step("there are no {ifc_class} elements because {reason}")
|
||||
def step_impl(context, ifc_class, reason):
|
||||
@step("There are no {ifc_class} elements")
|
||||
def step_impl(context, ifc_class):
|
||||
switch_locale(context.localedir, the_lang)
|
||||
aem.no_eleclass_because_reason(
|
||||
aem.no_eleclass(
|
||||
context,
|
||||
ifc_class,
|
||||
reason
|
||||
ifc_class
|
||||
)
|
||||
|
||||
|
||||
@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):
|
||||
switch_locale(context.localedir, the_lang)
|
||||
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):
|
||||
switch_locale(context.localedir, the_lang)
|
||||
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):
|
||||
switch_locale(context.localedir, the_lang)
|
||||
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
|
||||
|
||||
|
||||
def no_eleclass_because_reason(
|
||||
context, ifc_class, reason
|
||||
def no_eleclass(
|
||||
context, ifc_class
|
||||
):
|
||||
|
||||
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 step
|
||||
|
||||
from ifcdata_methods import assert_schema
|
||||
import ifcdata_methods as idm
|
||||
from utils import IfcFile
|
||||
from utils import switch_locale
|
||||
|
||||
@@ -28,12 +28,10 @@ def step_impl(context, file):
|
||||
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):
|
||||
try:
|
||||
IfcFile.load(context.config.userdata.get("ifcfile"))
|
||||
except:
|
||||
assert False, f"The IFC {context.config.userdata.get('ifcfile')} file could not be loaded"
|
||||
switch_locale(context.localedir, the_lang)
|
||||
idm.provide_ifcfile_by_argument(context)
|
||||
|
||||
|
||||
@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")
|
||||
def step_impl(context, schema):
|
||||
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')
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
from behave import step
|
||||
|
||||
from ifcdata_methods import assert_schema
|
||||
import ifcdata_methods as idm
|
||||
from utils import switch_locale
|
||||
|
||||
|
||||
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):
|
||||
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 ifcdata_methods import assert_schema
|
||||
import ifcdata_methods as idm
|
||||
from utils import switch_locale
|
||||
|
||||
|
||||
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}")
|
||||
def step_impl(context, schema):
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
assert real_schema == target_schema, (
|
||||
_("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: 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
|
||||
|
||||
@@ -15,25 +18,32 @@ class GuiWidgetBimTester(QtWidgets.QWidget):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
features="",
|
||||
ifcfile=""
|
||||
featurespath="",
|
||||
ifcfile="",
|
||||
get_featurepath_from_ifcpath=False,
|
||||
args=[]
|
||||
):
|
||||
super(GuiWidgetBimTester, self).__init__()
|
||||
|
||||
# get features dir
|
||||
user_path = os.path.expanduser("~")
|
||||
# print(features)
|
||||
self.initial_featurespath = features
|
||||
if not os.path.isdir(self.initial_featurespath):
|
||||
self.initial_featurespath = user_path
|
||||
print(self.initial_featurespath)
|
||||
|
||||
# get features dir
|
||||
# print(featurespath)
|
||||
if not os.path.isdir(featurespath):
|
||||
featurespath = user_path
|
||||
|
||||
# get ifc file
|
||||
# print(ifcfile)
|
||||
if not os.path.isfile(ifcfile):
|
||||
ifcfile = user_path
|
||||
|
||||
self.initial_featurespath = featurespath
|
||||
self.initial_ifcfile = ifcfile
|
||||
if not os.path.isfile(self.initial_ifcfile):
|
||||
self.initial_ifcfile = user_path
|
||||
print(self.initial_ifcfile)
|
||||
self.get_featurepath_from_ifcpath = get_featurepath_from_ifcpath
|
||||
self.args = args
|
||||
# print(self.initial_featurespath)
|
||||
# print(self.initial_ifcfile)
|
||||
# print(self.get_featurepath_from_ifcpath)
|
||||
|
||||
# init ui
|
||||
self._setup_ui()
|
||||
@@ -143,6 +153,9 @@ class GuiWidgetBimTester(QtWidgets.QWidget):
|
||||
layout.setRowStretch(0, 10)
|
||||
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):
|
||||
# print(self.get_ifcfile())
|
||||
@@ -162,11 +175,8 @@ class GuiWidgetBimTester(QtWidgets.QWidget):
|
||||
def featuredirfromifc_clicked(self):
|
||||
if self.featuredirfromifc_cb.isChecked() is True:
|
||||
self.set_featurefilesdir("")
|
||||
# TODO
|
||||
self.featurefilesdir_text.setEnabled(False)
|
||||
self.feafilesdir_browse_btn.setEnabled(False)
|
||||
# deactivate feature path browser button
|
||||
# deactivate lineedit text
|
||||
else:
|
||||
self.set_featurefilesdir(self.initial_featurespath)
|
||||
self.featurefilesdir_text.setEnabled(True)
|
||||
@@ -193,18 +203,42 @@ class GuiWidgetBimTester(QtWidgets.QWidget):
|
||||
|
||||
# **********************************************************
|
||||
def run_bimtester(self):
|
||||
print("Run BIMTester")
|
||||
print("Run BIMTester by the GUI")
|
||||
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
|
||||
|
||||
# get features dir
|
||||
if self.featuredirfromifc_cb.isChecked() is True:
|
||||
the_features_path = os.path.dirname(os.path.realpath(
|
||||
self.get_ifcfile()
|
||||
))
|
||||
ifcfile = self.get_ifcfile()
|
||||
print(
|
||||
"Make sure the feature files are beside "
|
||||
"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:
|
||||
the_features_path = self.get_featurefilesdir()
|
||||
print(the_features_path)
|
||||
@@ -213,11 +247,13 @@ class GuiWidgetBimTester(QtWidgets.QWidget):
|
||||
the_ifcfile = self.get_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
|
||||
status = run_all(
|
||||
the_features_path,
|
||||
the_ifcfile,
|
||||
)
|
||||
status = run_all(patched_args)
|
||||
print(status)
|
||||
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
@@ -7,150 +7,158 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\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"
|
||||
"Last-Translator: \n"
|
||||
"Language: de\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"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.6.0\n"
|
||||
"X-Generator: Poedit 2.2.1\n"
|
||||
|
||||
#: reports.py:153 reports.py:160
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr "OpenBIM auditing ist eine Funktionalität von"
|
||||
|
||||
#: reports.py:155
|
||||
#: reports.py:158
|
||||
msgid "en"
|
||||
msgstr "de"
|
||||
|
||||
#: reports.py:156
|
||||
#: reports.py:159
|
||||
msgid "Success"
|
||||
msgstr "Bestanden"
|
||||
|
||||
#: reports.py:157
|
||||
#: reports.py:160
|
||||
msgid "Failure"
|
||||
msgstr "Durchgefallen"
|
||||
|
||||
#: reports.py:158
|
||||
#: reports.py:161
|
||||
msgid "Tests passed"
|
||||
msgstr "Erfolgreiche Tests"
|
||||
|
||||
#: reports.py:159
|
||||
#: reports.py:162
|
||||
msgid "Duration"
|
||||
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"
|
||||
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}."
|
||||
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}"
|
||||
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."
|
||||
msgstr "Fehler in falsecount, es ist etwas falsch gelaufen."
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:80
|
||||
#: 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."
|
||||
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||
"attributes {parameter} has no value."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:81
|
||||
#: 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 ""
|
||||
|
||||
#: 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: "
|
||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||
" least one of these class attributes {parameter} has no value: "
|
||||
"{falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:131
|
||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||
#: 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 "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 ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:132
|
||||
#: features/steps/attributes_eleclasses_methods.py:111
|
||||
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}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:29
|
||||
msgid ""
|
||||
"All {elemcount} {ifc_class} elements are missing the property {parameter} in "
|
||||
"the pset."
|
||||
#: features/steps/attributes_eleclasses_methods.py:137
|
||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:30
|
||||
#: features/steps/attributes_eleclasses_methods.py:138
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are missing the "
|
||||
"property {parameter} in the pset: {falseelems}"
|
||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||
"is not set: {falseelems}"
|
||||
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."
|
||||
msgstr ""
|
||||
"Alle {elemcount} {ifc_class} Bauteile haben keine geometrische Repräsentation "
|
||||
"der Klasse {parameter}."
|
||||
"Alle {elemcount} {ifc_class} Bauteile haben keine geometrische "
|
||||
"Repräsentation der Klasse {parameter}."
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:51
|
||||
#: features/steps/geometric_detail_methods.py:55
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||
"{parameter} representation: {falseelems}"
|
||||
msgstr ""
|
||||
"Die Anzahl Bauteile {falsecount} von allen {elemcount} {ifc_class} Bauteilen "
|
||||
"haben keine geometrische Repräsentation der Klasse {parameter}: {falseelems}"
|
||||
"Die Anzahl Bauteile {falsecount} von allen {elemcount} {ifc_class} "
|
||||
"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."
|
||||
msgstr ""
|
||||
"Die geometrischen Repräsentationen von allen {elemcount} {ifc_class} Bauteilen "
|
||||
"haben Fehler."
|
||||
"Die geometrischen Repräsentationen von allen {elemcount} {ifc_class} "
|
||||
"Bauteilen haben Fehler."
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:126
|
||||
#: features/steps/geometric_detail_methods.py:133
|
||||
msgid ""
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements have "
|
||||
"errors: {falseelems}"
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||
"have errors: {falseelems}"
|
||||
msgstr ""
|
||||
"Die geometrischen Repräsentationen von {falsecount} von allen {elemcount} "
|
||||
"{ifc_class} Bauteilen haben Fehler: {falseelems}"
|
||||
"Die geometrischen Repräsentationen von {falsecount} von allen {elemcount}"
|
||||
" {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 {}"
|
||||
msgstr "Wir haben das Schema {} erwartet, aber die Daten nutzen das Schema {}"
|
||||
|
||||
#~ msgid "The geometry of all {} {} elements have errors."
|
||||
#~ 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: {}"
|
||||
#~ msgstr ""
|
||||
#~ "Die geometrischen Repräsentationen von {} von allen {} {} Bauteilen haben "
|
||||
#~ "Die geometrischen Repräsentationen von {} "
|
||||
#~ "von allen {} {} Bauteilen haben "
|
||||
#~ "Fehler: {}"
|
||||
|
||||
#~ 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."
|
||||
#~ msgstr ""
|
||||
#~ "Alle {} {} Bauteile haben keine geometrische Repräsentation der Klasse "
|
||||
#~ "Alle {} {} Bauteile haben keine "
|
||||
#~ "geometrische Repräsentation der Klasse "
|
||||
#~ "IfcFacetedBrep."
|
||||
|
||||
#~ 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 ""
|
||||
#~ "All {elemcount} {ifc_class} elements are not a IfcFacetedBrep representation."
|
||||
#~ "All {elemcount} {ifc_class} elements are "
|
||||
#~ "not a IfcFacetedBrep representation."
|
||||
#~ msgstr ""
|
||||
#~ "Alle {elemcount} {ifc_class} Bauteile haben keine geometrische "
|
||||
#~ "Repräsentation der Klasse IfcFacetedBrep."
|
||||
#~ "Alle {elemcount} {ifc_class} Bauteile haben"
|
||||
#~ " keine geometrische Repräsentation der "
|
||||
#~ "Klasse IfcFacetedBrep."
|
||||
|
||||
#~ 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}"
|
||||
#~ msgstr ""
|
||||
#~ "Die Anzahl Bauteile {falsecount} von allen {elemcount} {ifc_class} Bauteilen "
|
||||
#~ "haben keine geometrische Repräsentation der Klasse IfcFacetedBrep: "
|
||||
#~ "{falseelems}"
|
||||
#~ "Die Anzahl Bauteile {falsecount} von "
|
||||
#~ "allen {elemcount} {ifc_class} Bauteilen haben"
|
||||
#~ " keine geometrische Repräsentation der "
|
||||
#~ "Klasse IfcFacetedBrep: {falseelems}"
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\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"
|
||||
"Last-Translator: \n"
|
||||
"Language: en\n"
|
||||
@@ -18,122 +18,126 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.6.0\n"
|
||||
|
||||
#: reports.py:153 reports.py:160
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:155
|
||||
#: reports.py:158
|
||||
msgid "en"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:156
|
||||
#: reports.py:159
|
||||
msgid "Success"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:157
|
||||
#: reports.py:160
|
||||
msgid "Failure"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:158
|
||||
#: reports.py:161
|
||||
msgid "Tests passed"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:159
|
||||
#: reports.py:162
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:161
|
||||
#: reports.py:163
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:164
|
||||
msgid "and"
|
||||
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}."
|
||||
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}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:41
|
||||
#: features/steps/attributes_eleclasses_methods.py:43
|
||||
#: features/steps/utils.py:158
|
||||
msgid "Error in falsecount, something went wrong."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:80
|
||||
#: 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 ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:81
|
||||
#: 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 ""
|
||||
|
||||
#: 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
|
||||
#: 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 ""
|
||||
|
||||
#: 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."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:107
|
||||
#: features/steps/attributes_eleclasses_methods.py:111
|
||||
msgid ""
|
||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||
"set: {falseelems}"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:132
|
||||
#: features/steps/attributes_eleclasses_methods.py:138
|
||||
msgid ""
|
||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||
"is not set: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:29
|
||||
#: 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:30
|
||||
#: 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:50
|
||||
#: features/steps/geometric_detail_methods.py:54
|
||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:51
|
||||
#: features/steps/geometric_detail_methods.py:55
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||
"{parameter} representation: {falseelems}"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:126
|
||||
#: features/steps/geometric_detail_methods.py:133
|
||||
msgid ""
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||
"have errors: {falseelems}"
|
||||
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 {}"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -7,131 +7,137 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\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"
|
||||
"Last-Translator: \n"
|
||||
"Language: fr\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"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.6.0\n"
|
||||
"X-Generator: Poedit 2.2.1\n"
|
||||
|
||||
#: reports.py:153 reports.py:160
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr "L'audit OpenBIM auditing est une fonctionnalité de"
|
||||
|
||||
#: reports.py:155
|
||||
#: reports.py:158
|
||||
msgid "en"
|
||||
msgstr "fr"
|
||||
|
||||
#: reports.py:156
|
||||
#: reports.py:159
|
||||
msgid "Success"
|
||||
msgstr "Succès"
|
||||
|
||||
#: reports.py:157
|
||||
#: reports.py:160
|
||||
msgid "Failure"
|
||||
msgstr "Échec"
|
||||
|
||||
#: reports.py:158
|
||||
#: reports.py:161
|
||||
msgid "Tests passed"
|
||||
msgstr "Tests réussis"
|
||||
|
||||
#: reports.py:159
|
||||
#: reports.py:162
|
||||
msgid "Duration"
|
||||
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"
|
||||
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}."
|
||||
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}"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:80
|
||||
#: 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."
|
||||
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||
"attributes {parameter} has no value."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:81
|
||||
#: 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 ""
|
||||
|
||||
#: 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: "
|
||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||
" least one of these class attributes {parameter} has no value: "
|
||||
"{falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:131
|
||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||
#: 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 ""
|
||||
|
||||
#: 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 ""
|
||||
"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}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:29
|
||||
msgid ""
|
||||
"All {elemcount} {ifc_class} elements are missing the property {parameter} in "
|
||||
"the pset."
|
||||
#: features/steps/attributes_eleclasses_methods.py:137
|
||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:30
|
||||
#: features/steps/attributes_eleclasses_methods.py:138
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are missing the "
|
||||
"property {parameter} in the pset: {falseelems}"
|
||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||
"is not set: {falseelems}"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:51
|
||||
#: features/steps/geometric_detail_methods.py:55
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||
"{parameter} representation: {falseelems}"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:126
|
||||
#: features/steps/geometric_detail_methods.py:133
|
||||
msgid ""
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements have "
|
||||
"errors: {falseelems}"
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||
"have errors: {falseelems}"
|
||||
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 {}"
|
||||
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.
|
||||
# Copyright (C) 2020 ORGANIZATION
|
||||
# Copyright (C) 2021 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2021.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\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"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -17,122 +17,126 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.6.0\n"
|
||||
|
||||
#: reports.py:153 reports.py:160
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:155
|
||||
#: reports.py:158
|
||||
msgid "en"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:156
|
||||
#: reports.py:159
|
||||
msgid "Success"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:157
|
||||
#: reports.py:160
|
||||
msgid "Failure"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:158
|
||||
#: reports.py:161
|
||||
msgid "Tests passed"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:159
|
||||
#: reports.py:162
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:161
|
||||
#: reports.py:163
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:164
|
||||
msgid "and"
|
||||
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}."
|
||||
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}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:41
|
||||
#: features/steps/attributes_eleclasses_methods.py:43
|
||||
#: features/steps/utils.py:158
|
||||
msgid "Error in falsecount, something went wrong."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:80
|
||||
#: 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 ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:81
|
||||
#: 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 ""
|
||||
|
||||
#: 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
|
||||
#: 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 ""
|
||||
|
||||
#: 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."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:107
|
||||
#: features/steps/attributes_eleclasses_methods.py:111
|
||||
msgid ""
|
||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||
"set: {falseelems}"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:132
|
||||
#: features/steps/attributes_eleclasses_methods.py:138
|
||||
msgid ""
|
||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||
"is not set: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:29
|
||||
#: 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:30
|
||||
#: 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:50
|
||||
#: features/steps/geometric_detail_methods.py:54
|
||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:51
|
||||
#: features/steps/geometric_detail_methods.py:55
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||
"{parameter} representation: {falseelems}"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:126
|
||||
#: features/steps/geometric_detail_methods.py:133
|
||||
msgid ""
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||
"have errors: {falseelems}"
|
||||
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 {}"
|
||||
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(
|
||||
adir=".",
|
||||
report_dir=".",
|
||||
use_report_folder=True,
|
||||
report_file_name="",
|
||||
html_template_file_path=""
|
||||
report_file_name="report.json",
|
||||
html_template_file_path="",
|
||||
report_file=""
|
||||
):
|
||||
|
||||
# TODO use far less parameter
|
||||
# to be discussed with other devs
|
||||
|
||||
print("# Generating HTML reports now.")
|
||||
|
||||
# get locale path
|
||||
@@ -32,23 +36,22 @@ def generate_report(
|
||||
if html_template_file_path:
|
||||
report_template_path = html_template_file_path
|
||||
|
||||
# get report file
|
||||
report_dir = adir
|
||||
if use_report_folder:
|
||||
report_dir = os.path.join(adir, "report")
|
||||
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)
|
||||
# get report file and report dir
|
||||
if report_file:
|
||||
report_file = report_file
|
||||
report_dir = os.path.dirname(report_file)
|
||||
else:
|
||||
report_path = os.path.join(report_dir, "report.json")
|
||||
# print(report_path)
|
||||
if not os.path.exists(report_path):
|
||||
return print("No report data was found.")
|
||||
if use_report_folder:
|
||||
report_dir = os.path.join(report_dir, "report")
|
||||
report_file = os.path.join(report_dir, report_file_name)
|
||||
# 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
|
||||
report = json.loads(open(report_path).read())
|
||||
report = json.loads(open(report_file).read())
|
||||
for feature in report:
|
||||
file_name = os.path.basename(feature["location"]).split(":")[0]
|
||||
data = {
|
||||
@@ -134,6 +137,10 @@ def generate_report(
|
||||
switch_locale(localedir, "de")
|
||||
elif feature["keyword"] == "Fonctionnalité":
|
||||
switch_locale(localedir, "fr")
|
||||
elif feature["keyword"] == "Funzionalità":
|
||||
switch_locale(localedir, "it")
|
||||
elif feature["keyword"] == "Functionaliteit":
|
||||
switch_locale(localedir, "nl")
|
||||
else:
|
||||
# standard English
|
||||
switch_locale(localedir, "en")
|
||||
|
||||
+161
-147
@@ -1,11 +1,13 @@
|
||||
import behave.formatter.pretty # Needed for pyinstaller to package it
|
||||
import fileinput
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
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
|
||||
@@ -26,29 +28,106 @@ def get_resource_path(relative_path):
|
||||
|
||||
|
||||
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
|
||||
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"]:
|
||||
behave_args.extend(args["advanced_arguments"].split())
|
||||
elif not args["console"]:
|
||||
|
||||
if args["ifcfile"]:
|
||||
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",
|
||||
"json.pretty",
|
||||
# report file, if relative, than relative to current shell path
|
||||
# next two lines are one arg
|
||||
"--outfile",
|
||||
"report/report.json"
|
||||
report_file,
|
||||
])
|
||||
behave_args.extend([
|
||||
"--define",
|
||||
"localedir={}".format(locale_path)
|
||||
])
|
||||
if args["ifcfile"]:
|
||||
behave_args.extend(["--define", "ifcfile={}".format(args["ifcfile"])])
|
||||
if args["path"]:
|
||||
behave_args.extend(["--define", "path={}".format(args["path"])])
|
||||
|
||||
return behave_args
|
||||
|
||||
|
||||
def run_behave(behave_args):
|
||||
|
||||
from json import dumps
|
||||
print(dumps(behave_args, indent=4))
|
||||
|
||||
from behave.__main__ import main as behave_main
|
||||
behave_main(behave_args)
|
||||
print("# All tests are finished.")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -99,41 +178,9 @@ reset_runtime()
|
||||
"""
|
||||
|
||||
|
||||
# TODO: if the ifc file name or path contains special character
|
||||
# like German Umlaute behave gives an error
|
||||
def copy_intmp_tests(args={}):
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
print("# Copy features and steps to temp.")
|
||||
|
||||
from behave import __version__ as behave_version
|
||||
# https://github.com/behave/behave/issues/871
|
||||
@@ -144,53 +191,82 @@ def run_intmp_tests(args={}):
|
||||
)
|
||||
return False
|
||||
|
||||
# print(args)
|
||||
# get the features_path, the dir where the feature files to test are in
|
||||
if "features" in args and args["features"] != "":
|
||||
the_features_path = os.path.join(args["features"], "features")
|
||||
if ("featuresdir" in args and args["featuresdir"] != ""):
|
||||
is_features = True
|
||||
the_features_path = os.path.join(args["featuresdir"], "features")
|
||||
if not os.path.isdir(the_features_path):
|
||||
print(
|
||||
"The features directory does not exist: {}"
|
||||
"Error, the features directory '{}' does not exist."
|
||||
.format(the_features_path)
|
||||
)
|
||||
return False
|
||||
else:
|
||||
# TODO assume features beside ifc thus use ifc path
|
||||
print("No features path was given.")
|
||||
return False
|
||||
is_features = False
|
||||
|
||||
# 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"]
|
||||
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))
|
||||
if os.path.isdir(ifc_path) is False:
|
||||
print("ifc path does not exist.")
|
||||
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:
|
||||
# TODO use ifc path from feature files
|
||||
print("No ifc file was given.")
|
||||
is_ifcfile = False
|
||||
|
||||
# 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
|
||||
|
||||
# set up paths
|
||||
# a unique temp path should not be used
|
||||
# behave raises an ambiguous step exception
|
||||
# run_path = tempfile.mkdtemp()
|
||||
# copy_base_path = tempfile.mkdtemp()
|
||||
# thus use the same path on every run
|
||||
# but delete it if exists
|
||||
run_path = os.path.join(tempfile.gettempdir(), "bimtesterfc")
|
||||
if os.path.isdir(run_path):
|
||||
copy_base_path = os.path.join(tempfile.gettempdir(), "bimtesterfc")
|
||||
if os.path.isdir(copy_base_path):
|
||||
from shutil import rmtree
|
||||
rmtree(run_path) # fails on read only files
|
||||
if os.path.isdir(run_path):
|
||||
print("Delete former beimtester run dir {} failed".format(run_path))
|
||||
rmtree(copy_base_path) # fails on read only files
|
||||
if os.path.isdir(copy_base_path):
|
||||
print(
|
||||
"Delete former beimtester run dir '{}' failed"
|
||||
.format(copy_base_path)
|
||||
)
|
||||
return False
|
||||
os.mkdir(run_path)
|
||||
report_path = os.path.join(run_path, "report")
|
||||
copy_features_path = os.path.join(run_path, "features")
|
||||
os.mkdir(copy_base_path)
|
||||
copy_features_path = os.path.join(copy_base_path, "features")
|
||||
|
||||
# copy features path from bimtester source code
|
||||
srccode_features_path = os.path.join(
|
||||
@@ -218,8 +294,7 @@ def run_intmp_tests(args={}):
|
||||
# dirs_exist_ok=True
|
||||
# )
|
||||
|
||||
# copy feature files and replace ifcpath in feature files
|
||||
# replaceing is IMHO better than copy the ifc file which could be 500 MB
|
||||
# copy feature files
|
||||
feature_files = os.listdir(the_features_path)
|
||||
# print(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),
|
||||
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
|
||||
# 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
|
||||
return args, copy_base_path
|
||||
|
||||
|
||||
def run_all(the_features_path, the_ifcfile):
|
||||
def run_all(args):
|
||||
|
||||
print("# Run all.")
|
||||
|
||||
# run bimtester
|
||||
runpath = run_intmp_tests({
|
||||
"features": the_features_path,
|
||||
"ifcfile": the_ifcfile
|
||||
})
|
||||
print(runpath)
|
||||
report_file = run_tests(args)
|
||||
print(report_file)
|
||||
|
||||
# check if it worked out well
|
||||
if runpath is False:
|
||||
if report_file is False:
|
||||
print("BIMTester behave tests returned False.")
|
||||
return False
|
||||
|
||||
if not os.path.isdir(runpath):
|
||||
print("runpath does not exist. This should not happen. Debug")
|
||||
if not os.path.isfile(report_file):
|
||||
print("Report directory does not exist. This should not happen. Debug")
|
||||
return False
|
||||
|
||||
# create html report and open in webbrowser
|
||||
from .reports import generate_report
|
||||
generate_report(runpath)
|
||||
generate_report(report_file=report_file)
|
||||
# get the feature files
|
||||
feature_files = os.listdir(
|
||||
os.path.join(the_features_path, "features")
|
||||
os.path.join(args["featuresdir"], "features")
|
||||
)
|
||||
# print(feature_files)
|
||||
for ff in feature_files:
|
||||
webbrowser.open(os.path.join(
|
||||
runpath,
|
||||
"report",
|
||||
os.path.dirname(report_file),
|
||||
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
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
from bimtester import clean
|
||||
from bimtester import reports
|
||||
from bimtester import run
|
||||
|
||||
|
||||
def show_widget(features="", ifcfile=""):
|
||||
def show_widget(
|
||||
features="",
|
||||
ifcfile="",
|
||||
get_featurepath_from_ifcpath=False,
|
||||
args=[]
|
||||
):
|
||||
|
||||
import sys
|
||||
from PySide2 import QtWidgets
|
||||
@@ -18,7 +24,12 @@ def show_widget(features="", ifcfile=""):
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
# Create and show the form
|
||||
form = GuiWidgetBimTester(features, ifcfile)
|
||||
form = GuiWidgetBimTester(
|
||||
features,
|
||||
ifcfile,
|
||||
get_featurepath_from_ifcpath,
|
||||
args
|
||||
)
|
||||
form.show()
|
||||
|
||||
# Run the main Qt loop
|
||||
@@ -27,15 +38,76 @@ def show_widget(features="", ifcfile=""):
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# TODO make similar to bash commands
|
||||
# use - not _ in named args
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
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(
|
||||
"-p",
|
||||
"--purge",
|
||||
action="store_true",
|
||||
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(
|
||||
"-r",
|
||||
"--report",
|
||||
@@ -49,66 +121,18 @@ if __name__ == "__main__":
|
||||
help="Generate a HTML report after running the tests"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-path",
|
||||
"--path",
|
||||
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",
|
||||
"-t",
|
||||
"--copyintemprun",
|
||||
action="store_true",
|
||||
help=(
|
||||
"Start the gui. The option t (run in temp directory) "
|
||||
"is triggered automaticly."
|
||||
"Copy steps and feature files into a temporary directory "
|
||||
"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())
|
||||
print(args)
|
||||
from json import dumps
|
||||
print(dumps(args, indent=4))
|
||||
|
||||
if args["path"]:
|
||||
if args["feature"]:
|
||||
@@ -121,9 +145,19 @@ if __name__ == "__main__":
|
||||
elif args["report"]:
|
||||
reports.generate_report()
|
||||
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:
|
||||
run.run_tests(args)
|
||||
if args["report_after_run"]:
|
||||
reports.generate_report()
|
||||
|
||||
print("# All tasks are complete :-)")
|
||||
|
||||
@@ -266,21 +266,7 @@ endif
|
||||
rm -rf dist/working
|
||||
|
||||
# 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/ && 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/libs/site/packages/ && svn export https://github.com/IfcOpenShell/IfcOpenShell/trunk/src/ifcpatch
|
||||
|
||||
cd dist/blenderbim && sed -i "s/999999/$(VERSION)/" __init__.py
|
||||
cd dist && zip -r blender28-bim-$(VERSION)-$(PLATFORM).zip ./*
|
||||
|
||||
@@ -5,46 +5,49 @@ bpy = sys.modules.get("bpy")
|
||||
|
||||
if bpy is not None:
|
||||
import bpy
|
||||
import blenderbim.bim.module.root as module_root
|
||||
import blenderbim.bim.module.aggregate as module_aggregate
|
||||
import blenderbim.bim.module.attribute as module_attribute
|
||||
import blenderbim.bim.module.bcf as module_bcf
|
||||
import blenderbim.bim.module.context as module_context
|
||||
import blenderbim.bim.module.covetool as module_covetool
|
||||
import blenderbim.bim.module.debug as module_debug
|
||||
import blenderbim.bim.module.geometry as module_geometry
|
||||
import blenderbim.bim.module.model as module_model
|
||||
import blenderbim.bim.module.owner as module_owner
|
||||
import blenderbim.bim.module.project as module_project
|
||||
import blenderbim.bim.module.pset as module_pset
|
||||
import blenderbim.bim.module.spatial as module_spatial
|
||||
import blenderbim.bim.module.style as module_style
|
||||
import blenderbim.bim.module.type as module_type
|
||||
import blenderbim.bim.module.unit as module_unit
|
||||
import importlib
|
||||
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 = [
|
||||
operator.SelectClass,
|
||||
operator.SelectType,
|
||||
operator.OpenUri,
|
||||
operator.SelectFeaturesDir,
|
||||
operator.SelectDiffJsonFile,
|
||||
operator.SelectDiffNewFile,
|
||||
operator.SelectDiffOldFile,
|
||||
operator.SelectDataDir,
|
||||
operator.SelectSchemaDir,
|
||||
operator.SelectIfcFile,
|
||||
operator.ValidateIfcFile,
|
||||
operator.ExportIFC,
|
||||
operator.ImportIFC,
|
||||
operator.ColourByClass,
|
||||
operator.ColourByAttribute,
|
||||
operator.ColourByPset,
|
||||
operator.ResetObjectColours,
|
||||
operator.ApproveClass,
|
||||
operator.RejectClass,
|
||||
operator.SelectAudited,
|
||||
operator.RejectElement,
|
||||
operator.SelectExternalMaterialDir,
|
||||
operator.AddSweptSolid,
|
||||
operator.RemoveSweptSolid,
|
||||
@@ -54,8 +57,6 @@ if bpy is not None:
|
||||
operator.SelectSweptSolidInnerCurves,
|
||||
operator.AssignSweptSolidExtrusion,
|
||||
operator.SelectSweptSolidExtrusion,
|
||||
operator.AddQto,
|
||||
operator.RemoveQto,
|
||||
operator.AddMaterialPset,
|
||||
operator.RemoveMaterialPset,
|
||||
operator.AddMaterialLayer,
|
||||
@@ -86,10 +87,6 @@ if bpy is not None:
|
||||
operator.SelectGlobalId,
|
||||
operator.SelectAttribute,
|
||||
operator.SelectPset,
|
||||
operator.CreateAggregate,
|
||||
operator.EditAggregate,
|
||||
operator.SaveAggregate,
|
||||
operator.ExplodeAggregate,
|
||||
operator.LoadClassification,
|
||||
operator.AddClassification,
|
||||
operator.RemoveClassification,
|
||||
@@ -107,8 +104,6 @@ if bpy is not None:
|
||||
operator.OpenView,
|
||||
operator.OpenViewCamera,
|
||||
operator.ActivateView,
|
||||
operator.ExecuteIfcDiff,
|
||||
operator.VisualiseDiff,
|
||||
operator.ExportClashSets,
|
||||
operator.ImportClashSets,
|
||||
operator.AddClashSet,
|
||||
@@ -133,11 +128,6 @@ if bpy is not None:
|
||||
operator.RemovePropertyTemplate,
|
||||
operator.AddSectionPlane,
|
||||
operator.RemoveSectionPlane,
|
||||
operator.AddCsvAttribute,
|
||||
operator.RemoveCsvAttribute,
|
||||
operator.ExportIfcCsv,
|
||||
operator.ImportIfcCsv,
|
||||
operator.EyedropIfcCsv,
|
||||
operator.ReloadIfcFile,
|
||||
operator.AddIfcFile,
|
||||
operator.RemoveIfcFile,
|
||||
@@ -148,14 +138,6 @@ if bpy is not None:
|
||||
operator.AddVariable,
|
||||
operator.RemoveVariable,
|
||||
operator.PropagateTextData,
|
||||
operator.ConvertLocalToGlobal,
|
||||
operator.ConvertGlobalToLocal,
|
||||
operator.GuessQuantity,
|
||||
operator.ExecuteBIMTester,
|
||||
operator.BIMTesterPurge,
|
||||
operator.SelectCobieIfcFile,
|
||||
operator.SelectCobieJsonFile,
|
||||
operator.ExecuteIfcCobie,
|
||||
operator.SelectIfcPatchInput,
|
||||
operator.SelectIfcPatchOutput,
|
||||
operator.ExecuteIfcPatch,
|
||||
@@ -178,8 +160,6 @@ if bpy is not None:
|
||||
operator.BuildSchedule,
|
||||
operator.AddScheduleToSheet,
|
||||
operator.SetViewportShadowFromSun,
|
||||
operator.SetNorthOffset,
|
||||
operator.GetNorthOffset,
|
||||
operator.AddPresentationLayer,
|
||||
operator.AssignPresentationLayer,
|
||||
operator.UnassignPresentationLayer,
|
||||
@@ -197,17 +177,10 @@ if bpy is not None:
|
||||
operator.LinkIfc,
|
||||
operator.SnapSpacesTogether,
|
||||
operator.CopyGrid,
|
||||
operator.AddSectionsAnnotations,
|
||||
prop.StrProperty,
|
||||
prop.Attribute,
|
||||
prop.MaterialLayer,
|
||||
prop.MaterialConstituent,
|
||||
prop.MaterialProfile,
|
||||
prop.MaterialSet,
|
||||
prop.Variable,
|
||||
prop.Role,
|
||||
prop.Address,
|
||||
prop.Person,
|
||||
prop.Organisation,
|
||||
prop.Classification,
|
||||
prop.ClassificationReference,
|
||||
prop.ClassificationView,
|
||||
@@ -227,8 +200,6 @@ if bpy is not None:
|
||||
prop.BIMProperties,
|
||||
prop.DocProperties,
|
||||
prop.BIMLibrary,
|
||||
prop.MapConversion,
|
||||
prop.TargetCRS,
|
||||
prop.IfcParameter,
|
||||
prop.BoundaryCondition,
|
||||
prop.PsetQto,
|
||||
@@ -245,26 +216,17 @@ if bpy is not None:
|
||||
ui.BIM_PT_drawings,
|
||||
ui.BIM_PT_schedules,
|
||||
ui.BIM_PT_sheets,
|
||||
ui.BIM_PT_bim,
|
||||
ui.BIM_PT_psets,
|
||||
ui.BIM_PT_classifications,
|
||||
ui.BIM_PT_document_information,
|
||||
ui.BIM_PT_constraints,
|
||||
ui.BIM_PT_search,
|
||||
ui.BIM_PT_ifccsv,
|
||||
ui.BIM_PT_ifcclash,
|
||||
ui.BIM_PT_qa,
|
||||
ui.BIM_PT_library,
|
||||
ui.BIM_PT_gis,
|
||||
ui.BIM_PT_presentation_layers,
|
||||
ui.BIM_PT_diff,
|
||||
ui.BIM_PT_cobie,
|
||||
ui.BIM_PT_patch,
|
||||
ui.BIM_PT_mvd,
|
||||
ui.BIM_PT_material,
|
||||
ui.BIM_PT_presentation_layer_data,
|
||||
ui.BIM_PT_object_material,
|
||||
ui.BIM_PT_object_qto,
|
||||
ui.BIM_PT_classification_references,
|
||||
ui.BIM_PT_documents,
|
||||
ui.BIM_PT_constraint_relations,
|
||||
@@ -288,22 +250,8 @@ if bpy is not None:
|
||||
ui.BIM_ADDON_preferences,
|
||||
]
|
||||
|
||||
classes.extend(module_root.classes)
|
||||
classes.extend(module_aggregate.classes)
|
||||
classes.extend(module_attribute.classes)
|
||||
classes.extend(module_bcf.classes)
|
||||
classes.extend(module_context.classes)
|
||||
classes.extend(module_covetool.classes)
|
||||
classes.extend(module_debug.classes)
|
||||
classes.extend(module_geometry.classes)
|
||||
classes.extend(module_model.classes)
|
||||
classes.extend(module_owner.classes)
|
||||
classes.extend(module_project.classes)
|
||||
classes.extend(module_pset.classes)
|
||||
classes.extend(module_spatial.classes)
|
||||
classes.extend(module_style.classes)
|
||||
classes.extend(module_type.classes)
|
||||
classes.extend(module_unit.classes)
|
||||
for module in modules.values():
|
||||
classes.extend(module.classes)
|
||||
|
||||
def menu_func_export(self, context):
|
||||
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.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
|
||||
bpy.types.Scene.BIMLibrary = bpy.props.PointerProperty(type=prop.BIMLibrary)
|
||||
bpy.types.Scene.MapConversion = bpy.props.PointerProperty(type=prop.MapConversion)
|
||||
bpy.types.Scene.TargetCRS = bpy.props.PointerProperty(type=prop.TargetCRS)
|
||||
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.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
||||
bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
|
||||
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
|
||||
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
|
||||
module_root.register()
|
||||
module_aggregate.register()
|
||||
module_attribute.register()
|
||||
module_bcf.register()
|
||||
module_context.register()
|
||||
module_covetool.register()
|
||||
module_debug.register()
|
||||
module_geometry.register()
|
||||
module_model.register()
|
||||
module_owner.register()
|
||||
module_project.register()
|
||||
module_pset.register()
|
||||
module_spatial.register()
|
||||
module_style.register()
|
||||
module_type.register()
|
||||
module_unit.register()
|
||||
|
||||
for module in modules.values():
|
||||
module.register()
|
||||
|
||||
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
|
||||
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)
|
||||
del bpy.types.Scene.BIMProperties
|
||||
del bpy.types.Scene.DocProperties
|
||||
del bpy.types.Scene.MapConversion
|
||||
del bpy.types.Scene.TargetCRS
|
||||
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.Mesh.BIMMeshProperties
|
||||
del bpy.types.Camera.BIMCameraProperties
|
||||
del bpy.types.TextCurve.BIMTextProperties
|
||||
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
|
||||
module_unit.unregister()
|
||||
module_type.unregister()
|
||||
module_style.unregister()
|
||||
module_spatial.unregister()
|
||||
module_pset.unregister()
|
||||
module_project.unregister()
|
||||
module_owner.unregister()
|
||||
module_model.unregister()
|
||||
module_geometry.unregister()
|
||||
module_debug.unregister()
|
||||
module_covetool.unregister()
|
||||
module_context.unregister()
|
||||
module_bcf.unregister()
|
||||
module_attribute.register()
|
||||
module_aggregate.register()
|
||||
module_root.unregister()
|
||||
|
||||
for module in reversed(list(modules.values())):
|
||||
module.unregister()
|
||||
|
||||
bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler)
|
||||
|
||||
@@ -13,6 +13,7 @@ import gpu
|
||||
import bgl
|
||||
from gpu.types import GPUShader, GPUBatch, GPUIndexBuf, GPUVertBuf, GPUVertFormat
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from . import helper
|
||||
|
||||
|
||||
class BaseDecorator():
|
||||
@@ -641,7 +642,7 @@ class LevelDecorator(BaseDecorator):
|
||||
self.draw_labels(context, obj, splines)
|
||||
|
||||
|
||||
class PlanDecorator(LevelDecorator):
|
||||
class PlanLevelDecorator(LevelDecorator):
|
||||
basename = "IfcAnnotation/Plan Level"
|
||||
|
||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||
@@ -723,7 +724,7 @@ class PlanDecorator(LevelDecorator):
|
||||
self.draw_label(context, text, p0, dir, gap=8, center=False)
|
||||
|
||||
|
||||
class SectionDecorator(LevelDecorator):
|
||||
class SectionLevelDecorator(LevelDecorator):
|
||||
basename = "IfcAnnotation/Section Level"
|
||||
|
||||
DEF_GLSL = BaseDecorator.DEF_GLSL + """
|
||||
@@ -996,6 +997,108 @@ class GridDecorator(BaseDecorator):
|
||||
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():
|
||||
decorators_classes = [
|
||||
DimensionDecorator,
|
||||
@@ -1004,10 +1107,11 @@ class DecorationsHandler():
|
||||
HiddenDecorator,
|
||||
LeaderDecorator,
|
||||
MiscDecorator,
|
||||
PlanDecorator,
|
||||
SectionDecorator,
|
||||
PlanLevelDecorator,
|
||||
SectionLevelDecorator,
|
||||
StairDecorator,
|
||||
BreakDecorator
|
||||
BreakDecorator,
|
||||
SectionViewDecorator
|
||||
]
|
||||
|
||||
installed = None
|
||||
@@ -1031,11 +1135,9 @@ class DecorationsHandler():
|
||||
self.decorators = [cls() for cls in self.decorators_classes]
|
||||
|
||||
def __call__(self, context):
|
||||
props = context.scene.DocProperties
|
||||
if props.active_drawing_index is None or len(props.drawings) == 0:
|
||||
collection, _ = helper.get_active_drawing(context.scene)
|
||||
if collection is None:
|
||||
return
|
||||
drawing = props.drawings[props.active_drawing_index]
|
||||
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
|
||||
|
||||
for decorator in self.decorators:
|
||||
for obj in decorator.get_objects(collection):
|
||||
|
||||
@@ -299,6 +299,27 @@ def parse_diagram_scale(camera):
|
||||
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):
|
||||
"""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
|
||||
:arg margin: margins, in scene units
|
||||
: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
|
||||
size = camera.ortho_scale
|
||||
@@ -317,7 +338,11 @@ def ortho_view_frame(camera, margin=0.015):
|
||||
scale = parse_diagram_scale(camera)
|
||||
xmarg = margin * scale
|
||||
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):
|
||||
@@ -329,11 +354,11 @@ def clip_segment(bounds, segm):
|
||||
"""
|
||||
# Liang–Barsky algorithm
|
||||
|
||||
xmin, xmax, ymin, ymax = bounds
|
||||
xmin, xmax, ymin, ymax, _, _ = bounds
|
||||
p1, p2 = segm
|
||||
|
||||
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:
|
||||
return None # outside
|
||||
else:
|
||||
@@ -368,3 +393,19 @@ def clip_segment(bounds, segm):
|
||||
p2c = p1 + dlt * t2
|
||||
|
||||
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 zipfile
|
||||
import tempfile
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
from itertools import cycle
|
||||
from datetime import datetime
|
||||
@@ -85,7 +86,7 @@ class MaterialCreator:
|
||||
item_id = self.mesh.BIMMeshProperties.ifc_item_ids.add()
|
||||
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)
|
||||
|
||||
if not style_name:
|
||||
@@ -130,7 +131,9 @@ class MaterialCreator:
|
||||
material_to_slot[i] = slot_index
|
||||
|
||||
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)
|
||||
|
||||
def canonicalise_material_name(self, name):
|
||||
@@ -180,86 +183,31 @@ class MaterialCreator:
|
||||
def create_single(self, material):
|
||||
if material.Name not in self.materials:
|
||||
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):
|
||||
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:
|
||||
new = props.material_set.material_layers.add()
|
||||
if layer.Material:
|
||||
if layer.Material.Name not in self.materials:
|
||||
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):
|
||||
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:
|
||||
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:
|
||||
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):
|
||||
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:
|
||||
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:
|
||||
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):
|
||||
props = self.obj.BIMObjectProperties
|
||||
props.material_type = "IfcMaterialConstituentSet" # Constituent sets are the recommended upgrade path
|
||||
for material in material_list.Materials:
|
||||
new = props.material_set.material_constituents.add()
|
||||
if material.Name not in self.materials:
|
||||
self.create_new_single(material)
|
||||
new.material = self.materials[material.Name]
|
||||
|
||||
def create_new_single(self, material):
|
||||
self.materials[material.Name] = obj = bpy.data.materials.new(material.Name)
|
||||
obj.BIMMaterialProperties.ifc_definition_id = int(material.id())
|
||||
self.ifc_importer.add_element_attributes(material, obj.BIMMaterialProperties)
|
||||
for pset in getattr(material, "HasProperties", ()):
|
||||
self.ifc_importer.add_pset(pset, obj.BIMMaterialProperties)
|
||||
obj.BIMObjectProperties.ifc_definition_id = int(material.id())
|
||||
if not material.HasRepresentation or not material.HasRepresentation[0].Representations:
|
||||
return
|
||||
for representation in material.HasRepresentation[0].Representations:
|
||||
@@ -280,7 +228,7 @@ class MaterialCreator:
|
||||
if style.Name:
|
||||
return style.Name
|
||||
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):
|
||||
styles = self.get_styled_item_styles(styled_item)
|
||||
@@ -288,7 +236,6 @@ class MaterialCreator:
|
||||
if not style.is_a("IfcSurfaceStyle"):
|
||||
continue
|
||||
material.BIMMaterialProperties.ifc_style_id = int(style.id())
|
||||
external_style = None
|
||||
for surface_style in style.Styles:
|
||||
if surface_style.is_a("IfcSurfaceStyleShading"):
|
||||
alpha = 1.0
|
||||
@@ -301,13 +248,6 @@ class MaterialCreator:
|
||||
surface_style.SurfaceColour.Blue,
|
||||
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
|
||||
# However it is still widely used thanks to Revit :(
|
||||
@@ -395,8 +335,6 @@ class IfcImporter:
|
||||
self.profile_code("Purge diffs")
|
||||
self.load_existing_rooted_elements()
|
||||
self.profile_code("Load existing rooted elements")
|
||||
self.cache_file()
|
||||
self.profile_code("Caching file")
|
||||
self.load_file()
|
||||
self.profile_code("Loading file")
|
||||
self.set_ifc_file()
|
||||
@@ -406,8 +344,8 @@ class IfcImporter:
|
||||
self.profile_code("Set vendor worksarounds")
|
||||
self.calculate_unit_scale()
|
||||
self.profile_code("Calculate unit scale")
|
||||
self.patch_ifc()
|
||||
self.profile_code("Patching ifc")
|
||||
self.calculate_model_offset()
|
||||
self.profile_code("Calculate model offset")
|
||||
self.set_units()
|
||||
self.profile_code("Set units")
|
||||
self.create_project()
|
||||
@@ -434,8 +372,6 @@ class IfcImporter:
|
||||
if self.ifc_import_settings.should_import_native:
|
||||
self.parse_native_elements()
|
||||
self.profile_code("Parsing native elements")
|
||||
self.create_georeferencing()
|
||||
self.profile_code("Georeferencing ifc")
|
||||
self.create_groups()
|
||||
self.profile_code("Creating groups")
|
||||
self.create_grids()
|
||||
@@ -443,12 +379,8 @@ class IfcImporter:
|
||||
if self.ifc_import_settings.should_import_native:
|
||||
self.create_native_products()
|
||||
self.profile_code("Creating native products")
|
||||
# TODO: Deprecate after bug #682 is fixed and the new importer is stable
|
||||
if self.ifc_import_settings.should_use_legacy:
|
||||
self.create_products_legacy()
|
||||
else:
|
||||
self.create_products()
|
||||
self.profile_code("Creating meshified products")
|
||||
self.create_products()
|
||||
self.profile_code("Creating meshified products")
|
||||
self.relate_openings()
|
||||
self.profile_code("Relating openings")
|
||||
self.place_objects_in_spatial_tree()
|
||||
@@ -471,46 +403,22 @@ class IfcImporter:
|
||||
self.profile_code("Create presentation layers")
|
||||
self.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.profile_code("Mesh cleaning")
|
||||
|
||||
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")
|
||||
if not applications:
|
||||
return
|
||||
if applications[0].ApplicationIdentifier == "Revit":
|
||||
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():
|
||||
if "prostructures" in applications[0].ApplicationFullName.lower():
|
||||
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):
|
||||
for site in self.file.by_type(ifc_class):
|
||||
if (
|
||||
not site.ObjectPlacement
|
||||
or not site.ObjectPlacement.RelativePlacement
|
||||
or not site.ObjectPlacement.RelativePlacement.Location
|
||||
):
|
||||
continue
|
||||
if self.is_point_far_away(site.ObjectPlacement.RelativePlacement.Location):
|
||||
return True
|
||||
def is_element_far_away(self, element):
|
||||
try:
|
||||
return self.is_point_far_away(element.ObjectPlacement.RelativePlacement.Location)
|
||||
except:
|
||||
pass
|
||||
|
||||
def is_point_far_away(self, point):
|
||||
# Arbitrary threshold based on experience
|
||||
@@ -600,94 +508,94 @@ class IfcImporter:
|
||||
products.extend(self.get_products_from_shape_representation(inverse_element))
|
||||
return products
|
||||
|
||||
def patch_ifc(self):
|
||||
def calculate_model_offset(self):
|
||||
project = self.file.by_type("IfcProject")[0]
|
||||
if self.ifc_import_settings.should_ignore_site_coordinates:
|
||||
sites = self.find_decomposed_ifc_class(project, "IfcSite")
|
||||
if self.ifc_import_settings.should_guess_georeferencing and sites:
|
||||
self.guess_georeferencing(sites[0])
|
||||
for site in sites:
|
||||
self.patch_placement_to_origin(site)
|
||||
if self.ifc_import_settings.should_ignore_building_coordinates:
|
||||
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()
|
||||
site = self.find_decomposed_ifc_class(project, "IfcSite")
|
||||
if site and self.is_element_far_away(site[0]):
|
||||
return self.guess_georeferencing(site[0])
|
||||
building = self.find_decomposed_ifc_class(project, "IfcBuilding")
|
||||
if building and self.is_element_far_away(building[0]):
|
||||
return self.guess_georeferencing(building[0])
|
||||
return self.guess_absolute_coordinate()
|
||||
|
||||
def guess_georeferencing(self, element):
|
||||
if not element.ObjectPlacement.is_a("IfcLocalPlacement"):
|
||||
return
|
||||
placement = element.ObjectPlacement.RelativePlacement
|
||||
bpy.context.scene.MapConversion.eastings = str(placement.Location.Coordinates[0])
|
||||
bpy.context.scene.MapConversion.northings = str(placement.Location.Coordinates[1])
|
||||
bpy.context.scene.MapConversion.orthogonal_height = str(placement.Location.Coordinates[2])
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props.blender_eastings = str(placement.Location.Coordinates[0])
|
||||
props.blender_northings = str(placement.Location.Coordinates[1])
|
||||
props.blender_orthogonal_height = str(placement.Location.Coordinates[2])
|
||||
if placement.RefDirection:
|
||||
bpy.context.scene.MapConversion.x_axis_abscissa = str(placement.RefDirection.DirectionRatios[0])
|
||||
bpy.context.scene.MapConversion.x_axis_ordinate = str(placement.RefDirection.DirectionRatios[1] * -1)
|
||||
bpy.context.scene.MapConversion.scale = "1"
|
||||
props.blender_x_axis_abscissa = str(placement.RefDirection.DirectionRatios[0])
|
||||
props.blender_x_axis_ordinate = str(placement.RefDirection.DirectionRatios[1])
|
||||
props.has_blender_offset = True
|
||||
props.blender_offset_type = "OBJECT_PLACEMENT"
|
||||
|
||||
def reset_absolute_coordinates(self):
|
||||
# 12D can have some funky coordinates out of any sensible range. This
|
||||
# method will not work all the time, but will catch most issues.
|
||||
def guess_absolute_coordinate(self):
|
||||
# Civil BIM applications like to work in absolute coordinates, where the ObjectPlacement is 0,0,0 but each
|
||||
# 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
|
||||
elements_checked = 0
|
||||
# If more than these points aren't far away, the file probably isn't absolutely positioned
|
||||
element_checking_threshold = 100
|
||||
try:
|
||||
point_lists = self.file.by_type("IfcCartesianPointList3D")
|
||||
except:
|
||||
# IFC2X3 does not have IfcCartesianPointList3D
|
||||
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):
|
||||
if len(point) == 2 or not self.is_point_far_away(point):
|
||||
coord_list[i] = point
|
||||
continue
|
||||
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
|
||||
if len(point) == 3 and self.is_point_far_away(point):
|
||||
return point[0]
|
||||
# offset_point = (point[0], point[1], point[2])
|
||||
|
||||
for point in self.file.by_type("IfcCartesianPoint"):
|
||||
if len(point.Coordinates) == 2 or not self.is_point_far_away(point):
|
||||
continue
|
||||
if not offset_point:
|
||||
offset_point = (point.Coordinates[0], point.Coordinates[1], point.Coordinates[2])
|
||||
self.ifc_import_settings.logger.info("Resetting absolute coordinates by %s", point)
|
||||
point.Coordinates = (
|
||||
point.Coordinates[0] - offset_point[0],
|
||||
point.Coordinates[1] - offset_point[1],
|
||||
point.Coordinates[2] - offset_point[2],
|
||||
elements_checked += 1
|
||||
if elements_checked > element_checking_threshold:
|
||||
return
|
||||
if len(point.Coordinates) == 3 and self.is_point_far_away(point):
|
||||
return point[0]
|
||||
|
||||
def apply_blender_offset_to_matrix(self, matrix):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
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
|
||||
if self.file.wrapped_data.schema == "IFC2X3":
|
||||
properties = [
|
||||
self.file.createIfcPropertySingleValue(
|
||||
"Eastings", None, self.file.createIfcLengthMeasure(offset_point[0])
|
||||
),
|
||||
self.file.createIfcPropertySingleValue(
|
||||
"Northings", None, self.file.createIfcLengthMeasure(offset_point[1])
|
||||
),
|
||||
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
|
||||
|
||||
return 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()
|
||||
)
|
||||
self.file.createIfcRelDefinesByProperties(
|
||||
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])
|
||||
return mathutils.Matrix(matrix.tolist())
|
||||
|
||||
def find_decomposed_ifc_class(self, element, ifc_class):
|
||||
results = []
|
||||
@@ -701,60 +609,6 @@ class IfcImporter:
|
||||
results.extend(self.find_decomposed_ifc_class(part, ifc_class))
|
||||
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):
|
||||
group_collection = None
|
||||
for collection in self.project["blender"].children:
|
||||
@@ -773,7 +627,6 @@ class IfcImporter:
|
||||
else:
|
||||
obj = bpy.data.objects.new(f"{element.is_a()}/{element.Name}", None)
|
||||
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||
self.add_element_attributes(element, obj.BIMObjectProperties)
|
||||
group_collection.objects.link(obj)
|
||||
self.groups[element.GlobalId] = {"ifc": element, "blender": obj}
|
||||
|
||||
@@ -811,7 +664,6 @@ class IfcImporter:
|
||||
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
|
||||
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||
obj.matrix_world = matrix_world
|
||||
self.add_element_attributes(axis, obj.BIMObjectProperties)
|
||||
grid.objects.link(obj)
|
||||
|
||||
def create_type_products(self):
|
||||
@@ -863,11 +715,6 @@ class IfcImporter:
|
||||
):
|
||||
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):
|
||||
if not self.native_elements:
|
||||
return
|
||||
@@ -962,20 +809,19 @@ class IfcImporter:
|
||||
|
||||
if shape:
|
||||
m = shape.transformation.matrix.data
|
||||
mat = mathutils.Matrix(
|
||||
([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])
|
||||
# We use numpy here because Blender mathutils.Matrix is not accurate enough
|
||||
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 = mat
|
||||
obj.matrix_world = self.apply_blender_offset_to_matrix(mat)
|
||||
self.material_creator.create(element, obj, mesh)
|
||||
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_classifications(element, obj)
|
||||
self.add_element_document_relations(element, obj)
|
||||
self.add_opening_relation(element, obj)
|
||||
self.add_product_definitions(element, obj)
|
||||
self.added_data[element.GlobalId] = obj
|
||||
|
||||
if element.is_a("IfcOpeningElement"):
|
||||
@@ -1336,7 +1182,7 @@ class IfcImporter:
|
||||
obj = self.added_data[product.GlobalId]
|
||||
obj.data.BIMMeshProperties.presentation_layer_index = layer_index
|
||||
except:
|
||||
pass # Occurs for example in opening elements or exclusions
|
||||
pass # Occurs for example in opening elements or exclusions
|
||||
|
||||
def clean_mesh(self):
|
||||
obj = None
|
||||
@@ -1355,64 +1201,6 @@ class IfcImporter:
|
||||
bpy.ops.mesh.normals_make_consistent(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):
|
||||
if not element.is_a("IfcOpeningElement"):
|
||||
return
|
||||
@@ -1429,15 +1217,6 @@ class IfcImporter:
|
||||
with open(self.ifc_import_settings.diff_file, "r") as 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):
|
||||
self.ifc_import_settings.logger.info("loading file %s", self.ifc_import_settings.input_file)
|
||||
extension = self.ifc_import_settings.input_file.split(".")[-1]
|
||||
@@ -1708,7 +1487,6 @@ class IfcImporter:
|
||||
|
||||
self.add_element_classifications(element, obj)
|
||||
self.add_element_document_relations(element, obj)
|
||||
self.add_product_definitions(element, obj)
|
||||
self.aggregates[element.GlobalId] = obj
|
||||
self.aggregate_collections[rel_aggregate.id()] = collection
|
||||
|
||||
@@ -1731,47 +1509,6 @@ class IfcImporter:
|
||||
objects_to_purge.append(obj)
|
||||
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):
|
||||
for association in element.HasAssociations:
|
||||
if association.is_a("IfcRelAssociatesDocument"):
|
||||
@@ -1812,7 +1549,7 @@ class IfcImporter:
|
||||
return self.place_object_in_spatial_tree(container, obj)
|
||||
elif element.is_a("IfcGrid"):
|
||||
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)
|
||||
grid_collection.objects.link(obj)
|
||||
else:
|
||||
@@ -1852,21 +1589,6 @@ class IfcImporter:
|
||||
self.ifc_import_settings.logger.warning("Warning: this object is outside the spatial hierarchy %s", element)
|
||||
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):
|
||||
if key == "RefLatitude" or key == "RefLongitude":
|
||||
return ifcopenshell.util.geolocation.dms2dd(*value)
|
||||
@@ -1904,38 +1626,11 @@ class IfcImporter:
|
||||
return self.get_referenced_source_name(element.ReferencedSource)
|
||||
|
||||
def get_element_matrix(self, element, mesh_name=None):
|
||||
element_matrix = self.get_local_placement(element.ObjectPlacement)
|
||||
|
||||
if mesh_name:
|
||||
# Blender supports reusing a mesh with a different transformation
|
||||
# applied at the object level. In contrast, IFC supports reusing a mesh
|
||||
# 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
|
||||
result = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
result[0][3] *= self.unit_scale
|
||||
result[1][3] *= self.unit_scale
|
||||
result[2][3] *= self.unit_scale
|
||||
return result
|
||||
|
||||
def get_body_representations(self, representations, matrix=None):
|
||||
if matrix is None:
|
||||
@@ -2005,11 +1700,29 @@ class IfcImporter:
|
||||
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
|
||||
else:
|
||||
representation_id = int(re.sub(r"\D", "", representation_id))
|
||||
mesh = bpy.data.meshes.new("{}/{}".format(
|
||||
self.file.by_id(representation_id).ContextOfItems.id(), geometry.id))
|
||||
mesh = bpy.data.meshes.new(
|
||||
"{}/{}".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:
|
||||
num_vertices = len(geometry.verts) // 3
|
||||
num_vertices = len(verts) // 3
|
||||
total_faces = len(geometry.faces)
|
||||
loop_start = range(0, 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
|
||||
v_index = cycle((0, 1, 2))
|
||||
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)
|
||||
else:
|
||||
mesh.vertices.foreach_set("co", geometry.verts)
|
||||
mesh.vertices.foreach_set("co", verts)
|
||||
mesh.loops.add(num_vertex_indices)
|
||||
mesh.loops.foreach_set("vertex_index", geometry.faces)
|
||||
mesh.polygons.add(num_loops)
|
||||
@@ -2034,7 +1747,7 @@ class IfcImporter:
|
||||
mesh.update()
|
||||
else:
|
||||
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)]
|
||||
edges = [[e[i], e[i + 1]] for i in range(0, len(e), 2)]
|
||||
mesh.from_pydata(vertices, edges, [])
|
||||
@@ -2051,6 +1764,7 @@ class IfcImporter:
|
||||
except:
|
||||
self.ifc_import_settings.logger.error("Could not create mesh for %s", element)
|
||||
import traceback
|
||||
|
||||
print(traceback.format_exc())
|
||||
|
||||
def create_curve(self, geometry):
|
||||
@@ -2155,7 +1869,6 @@ class IfcImportSettings:
|
||||
self.should_auto_set_workarounds = True
|
||||
self.should_ignore_site_coordinates = False
|
||||
self.should_ignore_building_coordinates = False
|
||||
self.should_reset_absolute_coordinates = False
|
||||
self.should_merge_materials_by_colour = False
|
||||
self.should_import_type_representations = False
|
||||
self.should_import_curves = False
|
||||
@@ -2164,7 +1877,6 @@ class IfcImportSettings:
|
||||
self.should_use_cpu_multiprocessing = False
|
||||
self.should_import_with_profiling = False
|
||||
self.should_import_native = False
|
||||
self.should_use_legacy = False
|
||||
self.should_merge_aggregates = False
|
||||
self.should_merge_by_class = False
|
||||
self.should_merge_by_material = False
|
||||
@@ -2181,10 +1893,6 @@ class IfcImportSettings:
|
||||
settings.diff_file = scene_bim.diff_json_file
|
||||
settings.ifc_import_filter = scene_bim.ifc_import_filter
|
||||
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_curves = scene_bim.import_should_import_curves
|
||||
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_native = scene_bim.import_should_import_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_merge_aggregates = scene_bim.import_should_merge_aggregates
|
||||
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