From cb4001b47b1a4de83229cb590948b6278e42f6d4 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 24 Aug 2021 12:12:43 +1000 Subject: [PATCH 001/113] Detect units from project libraries, and convert nested double aggregates --- src/ifcparse/IfcParse.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index f9b873ec97..66ee47e5e4 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -1744,6 +1744,18 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) (*it) *= conversion_factor; } + IfcWrite::IfcWriteArgument* copy = new IfcWrite::IfcWriteArgument(); + copy->set(v); + we->setArgument(i, copy); + } else if (attr_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE) { + std::vector> v = *attr; + for (std::vector>::iterator it = v.begin(); it != v.end(); ++it) { + std::vector& v2 = (*it); + for (std::vector::iterator jt = v2.begin(); jt != v2.end(); ++jt) { + (*jt) *= conversion_factor; + } + } + IfcWrite::IfcWriteArgument* copy = new IfcWrite::IfcWriteArgument(); copy->set(v); we->setArgument(i, copy); @@ -2247,6 +2259,11 @@ void IfcFile::setDefaultHeaderValues() { std::pair IfcFile::getUnit(const std::string& unit_type) { std::pair return_value(0, 1.); IfcEntityList::ptr projects = instances_by_type(schema()->declaration_by_name("IfcProject")); + if ( ! projects) { + try { + projects = instances_by_type(schema()->declaration_by_name("IfcContext")); + } catch ( IfcException& e ) {} + } if (projects && projects->size() == 1) { IfcUtil::IfcBaseClass* project = *projects->begin(); From 486eb4d9a04f3961872a3f1ad163ad72c7b3d439 Mon Sep 17 00:00:00 2001 From: ArturTomczak Date: Tue, 24 Aug 2021 12:57:44 +0200 Subject: [PATCH 002/113] Implement new IDS schema 0.4.1 (#1623) --- .../ifcopenshell-python/api-documentation.rst | 3 + src/ifcopenshell-python/ifcopenshell/ids.py | 1203 ++++++++++++----- src/ifcopenshell-python/ifcopenshell/ids.xsd | 232 ++++ .../ifcopenshell/test_ids.py | 253 +++- 4 files changed, 1341 insertions(+), 350 deletions(-) create mode 100644 src/ifcopenshell-python/ifcopenshell/ids.xsd diff --git a/src/blenderbim/docs/ifcopenshell-python/api-documentation.rst b/src/blenderbim/docs/ifcopenshell-python/api-documentation.rst index feabcc8345..299250fb90 100644 --- a/src/blenderbim/docs/ifcopenshell-python/api-documentation.rst +++ b/src/blenderbim/docs/ifcopenshell-python/api-documentation.rst @@ -16,3 +16,6 @@ API Documentation .. automodule:: ifcopenshell.validate :members: + +.. automodule:: ifcopenshell.ids + :members: diff --git a/src/ifcopenshell-python/ifcopenshell/ids.py b/src/ifcopenshell-python/ifcopenshell/ids.py index 4b0f9f762c..769016b600 100644 --- a/src/ifcopenshell-python/ifcopenshell/ids.py +++ b/src/ifcopenshell-python/ifcopenshell/ids.py @@ -1,26 +1,407 @@ -import operator -import ifcopenshell.util.element +# IDS - Information Delivery Specification. +# Copyright (C) 2021 Artur Tomczak , Thomas Krijnen +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + import re +import logging +import operator +import os +import csv +import numpy as np +from datetime import date + +import ifcopenshell.util.element +import ifcopenshell.util.placement + +from bcf.v2.bcfxml import BcfXml +from bcf.v2 import data as bcf + +from bcf import bcfxml + from xmlschema import XMLSchema +from xmlschema import XMLSchemaConverter from xmlschema import etree_tostring +from lxml import etree as ElementTree from xmlschema.validators import facets from xmlschema.validators import identities -ids_schema = XMLSchema("http://standards.buildingsmart.org/IDS/ids.xsd") - -class exception(Exception): - pass +cwd = os.path.dirname(os.path.realpath(__file__)) +ids_schema = XMLSchema(os.path.join(cwd, "ids.xsd")) # source: "http://standards.buildingsmart.org/IDS/ids_04.xsd" def error(msg): - raise exception(msg) + raise Exception(msg) + + +class ids: + """Represents the XML root node and its childNodes.""" + + def __init__( + self, + ifcversion=None, + description=None, + author=None, + copyright=None, + version=None, + creation_date=None, + purpose=None, + milestone=None, + ): + """Create an IDS object. + + :param ifcversion: IFC schema version. If None, then schema independent. Options: '2.3.0.1'|'4.0.2.1'|'4.3.0.0'|None, defaults to None + :type ifcversion: str, optional + :param description:, defaults to None + :type description: str, optional + :param author: Email of the IDS author, defaults to None + :type author: str, optional + :param copyright:, defaults to None + :type copyright: str, optional + :param version: IDS file version, defaults to None + :type version: float, optional + :param creation_date: Date in 'yyyy-mm-dd' format, defaults to current date + :type creation_date: str, optional + :param purpose:, defaults to None + :type purpose: str, optional + :param milestone:, defaults to None + :type milestone: str, optional + """ + self.specifications = [] + self.info = {} + if ifcversion: + if ifcversion in ["2.3.0.1", "4.0.2.1", "4.3.0.0"]: + self.info["ifcversion"] = ifcversion + if author: + if "@" in author: + self.info["author"] = author + if description: + self.info["description"] = description + if copyright: + self.info["copyright"] = copyright + if version: + self.info["version"] = version + if creation_date: + if re.match(r"\d\d\d\d-\d\d-\d\d", creation_date): + self.info["date"] = creation_date # date.fromisoformat(creation_date).isoformat() + if "date" not in self.info: + self.info["date"] = date.today().isoformat() + if purpose: + self.info["purpose"] = purpose + if milestone: + self.info["milestone"] = milestone + + def asdict(self): + """Converts object to a dictionary, adding required attributes. + + :return: Xmlschema compliant dictionary. + :rtype: dict + """ + ids_dict = { + "@xmlns": "http://standards.buildingsmart.org/IDS", + "@xmlns:xs": "http://www.w3.org/2001/XMLSchema", + "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", + "@xsi:schemaLocation": "http://standards.buildingsmart.org/IDS/ids_04.xsd", + "specification": [], + "info": self.info, + } + for spec in self.specifications: + ids_dict["specification"].append(spec.asdict()) + return ids_dict + + def to_xml(self, filepath="./", ids_schema=ids_schema): + """Save IDS object as .xml file. + + :param filepath: Path for the new file, defaults to "./" + :type filepath: str, optional + :param ids_schema: XML Schema for an IDS file, defaults to ids_schema object from buildingSMART + :type ids_schema: XMLschema, optional + :return: Result of the newly created file validation against the schema. + :rtype: bool + """ + + if filepath.endswith("/"): + filepath = filepath + "IDS" + if not filepath.endswith(".xml"): + filepath = filepath + ".xml" + + ids_dict = self.asdict() + + ids_xml = ids_schema.encode( + ids_dict, + namespaces={ + "": "http://standards.buildingsmart.org/IDS", + "xs": "http://www.w3.org/2001/XMLSchema", + "xsi": "http://www.w3.org/2001/XMLSchema-instance", + "xsi:schemaLocation": "http://standards.buildingsmart.org/IDS/ids_04.xsd", + }, + ) # validation='skip', + + ids_str = etree_tostring( + ids_xml, + namespaces={ + "": "http://standards.buildingsmart.org/IDS", + # 'xs': 'http://www.w3.org/2001/XMLSchema', + # 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', + # 'xsi:schemaLocation': "http://standards.buildingsmart.org/IDS/ids_04.xsd" + }, + ) + + with open(filepath, "w") as f: + f.write('\n') + f.write("\n") + f.write(ids_str) + f.close() + + # ids_schema.validate(filepath) + return ids_schema.is_valid(filepath) + + @staticmethod + def open(filepath, ids_schema=ids_schema): + """Use to open ids.xml files + + :param filepath: ids file path + :type filepath: str + :param ids_schema: XML Schema for an IDS file, defaults to ids_schema object from buildingSMART + :type ids_schema: XMLschema, optional + :return: IDS file as a python object + :rtype: ids object + """ + + ids_schema.validate(filepath) + ids_content = ids_schema.decode( + filepath, strip_namespaces=True, namespaces={"": "http://standards.buildingsmart.org/IDS"} + ) + ids_file = ids() + ids_file.specifications = [specification.parse(s) for s in ids_content["specification"]] + return ids_file + + def validate(self, ifc_file, logger=None): + """Use to validate IFC model against IDS specifications. + + :param ifc_file: path to ifc file + :type ifc_file: str + :param logger: Logging object with handlers, defaults to None + :type logger: logging, optional + """ + if not isinstance(logger, logging.Logger): + logger = logging.getLogger("IDS_Logger") + logging.basicConfig(level=logging.INFO, format="%(message)s") + logger.setLevel(logging.INFO) + + if "ifcversion" in self.info.keys(): + if self.info["ifcversion"] in ["2.3.0.1", "4.0.2.1", "4.3.0.0"]: + if self.info["ifcversion"][0:3] == "2.3": + if not ifc_file.schema.startswith("IFC2x3"): + logger.error("IFC file is of %s not of %s schema." % (ifc_file.schema, self.info["ifcversion"])) + elif self.info["ifcversion"][0:3] == "4.0": + if not ifc_file.schema == "IFC4": + logger.error("IFC file is of %s not of %s schema." % (ifc_file.schema, self.info["ifcversion"])) + elif self.info["ifcversion"][0:3] == "4.3": + if not ifc_file.schema.startswith("IFC4x3"): + logger.error("IFC file is of %s not of %s schema." % (ifc_file.schema, self.info["ifcversion"])) + else: + logger.error("IFC version not recognized") + + # Consider other way around: for elem, for spec so we can see if an element pass all IDSes? + for spec in self.specifications: + self.ifc_applicable = 0 + self.ifc_passed = 0 + for elem in ifc_file.by_type("IfcObject"): + apply, comply = spec(elem, logger) + if apply: + self.ifc_applicable += 1 + if comply: + self.ifc_passed += 1 + if self.ifc_applicable == 0: + if spec.necessity == "required": + logger.error("No applicable elements found. Minimum 1 applicable element required.") + else: + logger.debug("No applicable elements found. None required.") + + logger.debug( + "Out of %s IFC elements, %s were applicable and %s of them passed (%s)." + % ( + len(ifc_file.by_type("IfcProduct")), + self.ifc_applicable, + self.ifc_passed, + str(self.ifc_passed / self.ifc_applicable * 100) + "%", + ) + ) + for h in logger.handlers: + h.flush() + + +class specification: + """Represents the XML node and its two children and """ + + def __init__(self, name="Specification", necessity="required"): + """Create a specification to be added in ids. + + :param name:, defaults to "Specification" + :type name: str, optional + :param necessity: 'required'|'optional', defaults to "required" + :type necessity: str, optional + """ + self.name = name + self.applicability = None + self.requirements = None + self.necessity = necessity + + def asdict(self): + """Converts object to a dictionary, adding required attributes. + + :return: Xmlschema compliant dictionary. + :rtype: dict + """ + # if older python collections.OrderedDict() + spec_dict = { + "@name": self.name, + "@necessity": self.necessity, + "applicability": {}, + "requirements": {}, + } + for x in ["applicability", "requirements"]: + for fac in (getattr(self, x)).terms: + fclass = type(fac).__name__ + if fclass in spec_dict[x]: + spec_dict[x][fclass].append(fac.asdict()) + else: + spec_dict[x][fclass] = [fac.asdict()] + return spec_dict + + @staticmethod + def parse(ids_dict): + """Parse xml specification to python object. + + :param ids_dict: + :type ids_dict: dict + """ + + def parse_rules(dict): + facet_names = list(dict.keys()) + facet_properties = [v[0] if isinstance(v, list) else v for v in list(dict.values())] + classes = [meta_facet.facets.__getitem__(f) for f in facet_names] + facets = [cls(n) for cls, n in zip(classes, facet_properties)] + return facets + + spec = specification() + spec.name = ids_dict["@name"] + spec.necessity = ids_dict["@necessity"] + spec.applicability = boolean_and(parse_rules(ids_dict["applicability"])) + spec.requirements = boolean_and(parse_rules(ids_dict["requirements"])) + return spec + + def add_applicability(self, facet): + """Applicability specifies what conditions must be meet for an IFC object to be used for validation. Note, that at least one entity facet is required. + + :param facet: any of entity|classification|property|material + :type facet: facet + + Example:: + + i = ids.ids() + i.specifications.append(ids.specification(name="Test_Specification")) + e = ids.entity.create(name="Test_Name", predefinedtype="Test_PredefinedType") + i.specifications[0].add_applicability(e) + """ + if self.applicability: + self.applicability = boolean_and(self.applicability.terms + [facet]) + else: + self.applicability = boolean_and([facet]) + + def add_requirement(self, facet): + """Requirement is validated on all applicable IFC elements. Note, that at least one facet of any type is required. + + :param facet: any of entity|classification|property|material + :type facet: facet + """ + if self.requirements: + self.requirements = boolean_and(self.requirements.terms + [facet]) + else: + self.requirements = boolean_and([facet]) + + def __call__(self, inst, logger): + """When specification is called on an ifc instance, it validates against applicability and requirements. + + :param inst: IFC entity element + :type inst: IFC entity + :param logger: Logging object + :type logger: logging + :return: results of validation on applicability and requirements + :rtype: [bool,bool] + """ + if self.applicability(inst, logger): + + valid = self.requirements(inst, logger) + + if valid: + logger.info( + { + "guid": inst.GlobalId, + "result": valid.success, + "sentence": str(self) + + ".\n" + + inst.is_a() + + " '" + + str(inst.Name) + + "' (#" + + str(inst.id()) + + ") has " + + str(valid) + + " so is compliant", + "ifc_element": inst, + } + ) + return True, True + else: + # BUG "has does not have" + logger.error( + { + "guid": inst.GlobalId, + "result": valid.success, + "sentence": str(self) + + ".\n" + + inst.is_a() + + " '" + + str(inst.Name) + + "' (#" + + str(inst.id()) + + ") has " + + str(valid) + + " so is not compliant", + "ifc_element": inst, + } + ) + return True, False + else: + return False, False + + def __str__(self): + """Represent the specification in human readible sentence. + + :return: sentence + :rtype: str + """ + return "Given an instance with %(applicability)s\nWe expect %(requirements)s" % self.__dict__ class facet_evaluation: - """ - The evaluation of a facet with data from IFC. Converts to bool and has a human readable string format. - """ + """The evaluation of a facet with data from IFC. Converts to bool and has a human readable string format.""" def __init__(self, success, str): self.success = success @@ -34,9 +415,7 @@ class facet_evaluation: class meta_facet(type): - """ - A metaclass for automatically registering facets in a map to be instantiated based on XML tagnames. - """ + """A metaclass for automatically registering facets in a map to be instantiated based on XML tagnames.""" facets = {} @@ -51,27 +430,34 @@ class facet(metaclass=meta_facet): The base class for IDS facets. IDS facets are functors constructed from XML nodes that return True or False. A getattr method is provided for conveniently extracting XML child node text content. - """ + Use child classes instead: entity, classification, property and material. + """ def __init__(self, node=None, location=None): if node: self.node = node - if '@location' in self: - self.location = self.node['@location'] + if "@location" in self: + self.location = self.node["@location"] else: - self.location = 'any' + self.location = "any" if location: self.location = location else: - self.location = 'any' + self.location = "any" def __getattr__(self, k): if k in self.node: v = self.node[k] - if isinstance(v, dict): #is restriction? - return restriction(v['xs:restriction'][0]) + # BUG list of dictionaries should not happen + if isinstance(v, list): + v = v[0] + if "simpleValue" in list(v): + return v["simpleValue"] + elif "restriction" in list(v): + return restriction.parse(v["restriction"][0]) + # TODO handle more than one restriction: return [restriction(r) for r in v["restriction"]] else: - return v + raise Exception("Unknown value declaration.") else: return None @@ -88,38 +474,59 @@ class facet(metaclass=meta_facet): class entity(facet): - """ - The IDS entity facet currently *with* inheritance - """ + """The IDS entity facet currently *with* inheritance""" parameters = ["name", "predefinedtype"] - + + @staticmethod def create(name=None, predefinedtype=None): + """Create an entity facet that can be added to applicability or requirements of IDS specification. + + :param name: IFC entity name that is required. e.g. IfcWall, defaults to None + :type name: str, optional + :param predefinedtype: name of the predefined type, defaults to None + :type predefinedtype: str, optional + :return: entity object + :rtype: entity + """ + inst = entity() inst.name = name inst.predefinedtype = predefinedtype return inst def asdict(self): - fac_dict = {'name': self.name} - if 'predefinedtype' in self: - fac_dict['predefinedtype'] = self.predefinedtype + """Converts object to a dictionary, adding required attributes. + + :return: Xmlschema compliant dictionary. + :rtype: dict + """ + fac_dict = {"name": parameter_asdict(self.name)} + if "predefinedtype" in self: + fac_dict["predefinedtype"] = parameter_asdict(self.predefinedtype) return fac_dict def __call__(self, inst, logger): + """Validate an ifc instance against that entity facet. + + :param inst: IFC entity element + :type inst: IFC entity + :param logger: Logging object + :type logger: logging + :return: result of the validation as bool and message + :rtype: facet_evaluation(bool, str) + """ + # @nb with inheritance if self.predefinedtype and hasattr(inst, "PredefinedType"): self.message = "an entity name '%(name)s' of predefined type '%(predefinedtype)s'" return facet_evaluation( inst.is_a(self.name) and inst.PredefinedType == self.predefinedtype, - self.message % {"name": inst.is_a(), "predefinedtype": inst.PredefinedType} - ) + self.message % {"name": inst.is_a(), "predefinedtype": inst.PredefinedType}, + ) else: self.message = "an entity name '%(name)s'" - return facet_evaluation( - inst.is_a(self.name), - self.message % {"name": inst.is_a()} - ) + return facet_evaluation(inst.is_a(self.name), self.message % {"name": inst.is_a()}) class classification(facet): @@ -130,7 +537,20 @@ class classification(facet): parameters = ["system", "value", "location"] message = "%(location)sclassification reference %(value)s from '%(system)s'" - def create(location='any', value=None, system=None): + @staticmethod + def create(location="any", value=None, system=None): + """Create a classification facet that can be added to applicability or requirements of IDS specification. + + :param location: Define where to check for the parameter. One of "any"|"instance"|"type", defaults to "any" + :type location: str, optional + :param value: Value that is required. Could be alphanumeric or restriction object, defaults to None + :type value: restriction|alphanumeric, optional + :param system: System that is required. Could be alphanumeric or restriction object, defaults to None + :type system: restriction|alphanumeric, optional + :return: classification object + :rtype: classification + """ + inst = classification() inst.location = location inst.value = value @@ -138,26 +558,41 @@ class classification(facet): return inst def asdict(self): + """Converts object to a dictionary, adding required attributes. + + :return: Xmlschema compliant dictionary. + :rtype: dict + """ fac_dict = { - '@location': self.location, - 'value': self.value, - 'system': self.system - } + "value": parameter_asdict(self.value), + "system": parameter_asdict(self.system), + "@location": self.location, + # "instructions": "SAMPLE_INSTRUCTIONS", + } return fac_dict def __call__(self, inst, logger): - + """Validate an ifc instance against that classification facet. + + :param inst: IFC entity element + :type inst: IFC entity + :param logger: Logging object + :type logger: logging + :return: result of the validation as bool and message + :rtype: facet_evaluation(bool, str) + """ + instance_classiciations = inst.HasAssociations if ifcopenshell.util.element.get_type(inst): type_classifications = ifcopenshell.util.element.get_type(inst).HasAssociations else: type_classifications = () - if self.location == 'instance' and instance_classiciations: + if self.location == "instance" and instance_classiciations: associations = instance_classiciations - elif self.location == 'type' and type_classifications: + elif self.location == "type" and type_classifications: associations = type_classifications - elif self.location == 'any' and (instance_classiciations or type_classifications): + elif self.location == "any" and (instance_classiciations or type_classifications): associations = instance_classiciations + type_classifications else: associations = () @@ -166,23 +601,25 @@ class classification(facet): for association in associations: if association.is_a("IfcRelAssociatesClassification"): cref = association.RelatingClassification - if hasattr(cref, 'ItemReference'): #IFC2x3 + if hasattr(cref, "ItemReference"): # IFC2x3 refs.append((cref.ReferencedSource.Name, cref.ItemReference)) - elif hasattr(cref, 'Identification'): # IFC4 - refs.append((cref.ReferencedSource.Name, cref.Identification)) + elif hasattr(cref, "Identification"): # IFC4 + refs.append((cref.ReferencedSource.Name, cref.Identification)) self.location_msg = location[self.location] if refs: return facet_evaluation( (self.system, self.value) in refs, - self.message % {"system": refs[0][0], "value": "'"+refs[0][1]+"'", "location": self.location_msg} # what if not first item of refs? - ) - else: - return facet_evaluation( - False, - "does not have %sclassification reference" % self.location_msg + self.message + % { + "system": refs[0][0], + "value": "'" + refs[0][1] + "'", + "location": self.location_msg, + }, # what if not first item of refs? ) + else: + return facet_evaluation(False, "does not have %sclassification reference" % self.location_msg) class property(facet): @@ -192,8 +629,22 @@ class property(facet): parameters = ["name", "propertyset", "value", "location"] message = "%(location)sproperty '%(name)s' in '%(propertyset)s' with a value %(value)s" - - def create(location='any', propertyset=None, name=None, value=None): + + @staticmethod + def create(location="any", propertyset=None, name=None, value=None): + """Create a property facet that can be added to applicability or requirements of IDS specification. + + :param location: Define where to check for the parameter. One of "any"|"instance"|"type", defaults to "any" + :type location: str, optional + :param propertyset: Propertyset that is required. Could be alphanumeric or restriction object, defaults to None + :type propertyset: restriction|alphanumeric, optional + :param name: Name that is required. Could be alphanumeric or restriction object, defaults to None + :type name: restriction|alphanumeric, optional + :param value: Value that is required. Could be alphanumeric or restriction object, defaults to None + :type value: restriction|alphanumeric, optional + :return: property object + :rtype: property + """ inst = property() inst.location = location inst.propertyset = propertyset @@ -205,46 +656,56 @@ class property(facet): return inst def asdict(self): + """Converts object to a dictionary, adding required attributes. + + :return: Xmlschema compliant dictionary. + :rtype: dict + """ fac_dict = { - '@location': self.location, - 'propertyset': self.propertyset, - 'name': self.name, - 'value': self.value, + "@location": self.location, + "propertyset": parameter_asdict(self.propertyset), + "name": parameter_asdict(self.name), + "value": parameter_asdict(self.value), + # "instructions": "SAMPLE_INSTRUCTIONS", # TODO '@href': 'http://identifier.buildingsmart.org/uri/buildingsmart/ifc-4.3/prop/FireRating', #https://identifier.buildingsmart.org/uri/something - # TODO 'instructions': 'Please add the desired rating.' - } + } return fac_dict - def __call__(self, inst, logger): + """Validate an ifc instance against that property facet. - self.location = self.node['@location'] + :param inst: IFC entity element + :type inst: IFC entity + :param logger: Logging object + :type logger: logging + :return: result of the validation as bool and message + :rtype: facet_evaluation(bool, str) + """ + self.location = self.node["@location"] + + # TODO sometimes AttributeError: 'str' object has no attribute 'wrappedValue' instance_props = ifcopenshell.util.element.get_psets(inst) + if ifcopenshell.util.element.get_type(inst): - type_props = ifcopenshell.util.element.get_psets( ifcopenshell.util.element.get_type(inst) ) + type_props = ifcopenshell.util.element.get_psets(ifcopenshell.util.element.get_type(inst)) else: type_props = {} - if self.location == 'instance': + if self.location == "instance": props = instance_props - elif self.location == 'type' and type_props: + elif self.location == "type" and type_props: props = type_props - elif self.location == 'any' and (instance_props or type_props): - props = {**instance_props , **type_props} + elif self.location == "any" and (instance_props or type_props): + props = {**instance_props, **type_props} else: props = {} - + pset = props.get(self.propertyset) val = pset.get(self.name) if pset else None - + self.location_msg = location[self.location] - di = { - "name": self.name, - "propertyset": self.propertyset, - "value": "'%s'" % val, - "location": self.location_msg - } + di = {"name": self.name, "propertyset": self.propertyset, "value": "'%s'" % val, "location": self.location_msg} if val is not None: msg = self.message % di @@ -254,55 +715,88 @@ class property(facet): else: msg = "does not have %(location)sset '%(propertyset)s'" % di - #TODO implement data type comparison - return facet_evaluation( - val == self.value, - msg - ) + # TODO implement data type comparison + # xs:string + # xs:decimal + # xs:integer + # xs:boolean + # xs:anyURI + # xs:date YYYY-MM-DD + # xs:time hh:mm:ss + # xs:dateTime YYYY-MM-DDThh:mm:ss + # xs:duration PnYnMnDTnHnMnS + + return facet_evaluation(val == self.value, msg) class material(facet): - """ - The IDS material facet by traversing the HasAssociations inverse attribute - """ + """The IDS material facet used to traverse the HasAssociations inverse attribute.""" + parameters = ["value", "location"] message = "%(location)smaterial '%(value)s'" - - def create(location='any', value=None): + + @staticmethod + def create(location="any", value=None): + """Create a material facet that can be added to applicability or requirements of IDS specification. + + :param location: Define where to check for the parameter. One of "any"|"instance"|"type", defaults to "any" + :type location: str, optional + :param value: Value that is required. Could be alphanumeric or restriction object, defaults to None + :type value: restriction|alphanumeric, optional + :return: material object + :rtype: material + """ inst = material() inst.location = location inst.value = value - # self.attributes = {'@location': location} # 'type', 'instance', 'any' - # # BUG '@use': 'optional' - # # BUG '@href': 'https://identifier.buildingsmart.org/uri/something', - # # BUG 'instructions': 'Please add the desired...', + # TODO '@use': 'optional' + # TODO '@href': 'https://identifier.buildingsmart.org/uri/something', + # TODO 'instructions': 'Please add the desired...', return inst def asdict(self): + """Converts object to a dictionary, adding required attributes. + + :return: Xmlschema compliant dictionary. + :rtype: dict + """ fac_dict = { - '@location': self.location, - 'value': self.value, + "value": parameter_asdict(self.value), + "@location": self.location, + # TODO "instructions": "SAMPLE_INSTRUCTIONS", # TODO '@href': 'http://identifier.buildingsmart.org/uri/buildingsmart/ifc-4.3/prop/FireRating', #https://identifier.buildingsmart.org/uri/something - # TODO 'instructions': 'Please add the desired rating.' # TODO '@use': 'optional' - } + } return fac_dict def __call__(self, inst, logger): + """Validate an ifc instance against that material facet. - self.location = self.node['@location'] + :param inst: IFC entity element + :type inst: IFC entity + :param logger: Logging object + :type logger: logging + :return: result of the validation as bool and message + :rtype: facet_evaluation(bool, str) + """ + + self.location = self.node["@location"] instance_material_rel = [rel for rel in inst.HasAssociations if rel.is_a("IfcRelAssociatesMaterial")] if ifcopenshell.util.element.get_type(inst): - type_material_rel = [rel for rel in ifcopenshell.util.element.get_type(inst).HasAssociations if rel.is_a("IfcRelAssociatesMaterial")] + type_material_rel = [ + rel + for rel in ifcopenshell.util.element.get_type(inst).HasAssociations + if rel.is_a("IfcRelAssociatesMaterial") + ] else: type_material_rel = [] - if self.location == 'instance': + if self.location == "instance": material_relations = list(instance_material_rel) - elif self.location == 'type' and type_material_rel: + elif self.location == "type" and type_material_rel: material_relations = list(type_material_rel) - elif self.location == 'any' and (instance_material_rel or type_material_rel): + elif self.location == "any" and (instance_material_rel or type_material_rel): material_relations = instance_material_rel + type_material_rel else: material_relations = [] @@ -311,7 +805,7 @@ class material(facet): for rel in material_relations: if rel.RelatingMaterial.is_a() == "IfcMaterial": materials.append(rel.RelatingMaterial.Name) - elif rel.RelatingMaterial.is_a() == "IfcMaterialMaterialList": #DEPRECATED in IFC4 + elif rel.RelatingMaterial.is_a() == "IfcMaterialMaterialList": # DEPRECATED in IFC4 [materials.append(mat.Name) for mat in rel.RelatingMaterial] elif rel.RelatingMaterial.is_a() == "IfcMaterialConstituentSet": [materials.append(mat.Material.Name) for mat in rel.RelatingMaterial.MaterialConstituents] @@ -326,10 +820,10 @@ class material(facet): profileSets = rel.RelatingMaterial.ForProfileSet.MaterialProfiles [materials.append(pset.Material.Name) for pset in profileSets] else: - logger.error({'guid':inst.GlobalId, 'result':'ERROR', 'sentence':'IfcRelAssociatesMaterial not implemented'}) + raise Exception("IfcRelAssociatesMaterial not implemented") if not materials: - materials.append('UNDEFINED') + materials.append("UNDEFINED") self.location_msg = location[self.location] @@ -339,10 +833,27 @@ class material(facet): ) +def parameter_asdict(parameter): + """Converts parameter to an IDS compliant dictionary, handling both value and restrictions. + + :return: Xmlschema compliant dictionary. + :rtype: dict + """ + if isinstance(parameter, str): + parameter_dict = {"simpleValue": parameter} + elif isinstance(parameter, restriction): + parameter_dict = {"xs:restriction": [parameter.asdict()]} + elif isinstance(parameter, list): + restrictions = {"@base": "xs:" + parameter[0].base} + for p in parameter: + x = p.asdict() + restrictions[list(x)[1]] = x[list(x)[1]] + parameter_dict = {"xs:restriction": [restrictions]} + return parameter_dict + + class boolean_logic: - """ - Boolean conjunction over a collection of functions - """ + """Boolean conjunction over a collection of functions""" def __init__(self, terms): self.terms = terms @@ -350,10 +861,7 @@ class boolean_logic: def __call__(self, *args): eval = [t(*args) for t in self.terms] join = [" and ", " or "][self.fold == any] - return facet_evaluation( - self.fold(eval), - join.join(map(str, eval)) - ) + return facet_evaluation(self.fold(eval), join.join(map(str, eval))) def __str__(self): return [" and ", " or "][self.fold == any].join(map(str, self.terms)) @@ -372,267 +880,330 @@ class restriction: The value restriction from XSD implemented as a list of values and a containment test """ - def __init__(self, node): - - self.restriction_on = node['@base'][3:] + def __init__(self): + """Create a restriction that can be used instead of value of a parameter.""" self.type = "" self.options = [] - for n in node: - if n[0:3] == "xs:": - if n[3:] == "enumeration": - self.type = "enumeration" - for x in node[n]: - self.options.append(x["@value"]) - elif n[8:] == "clusive": - self.type = "bounds" - if n[3:6] == 'min': - self.options.insert(0,'>') - else: - self.options.insert(0,'<') - if n[6:9] == 'Inc': - self.options[0] += '=' - self.options[0] += node[n]['@value'] + @staticmethod + def parse(ids_dict): + """Parse xml restriction to python object. + + :param ids_dict: + :type ids_dict: dict + """ + r = restriction() + if ids_dict: + # TODO 'base' missing in some IDS?! + r.base = ids_dict["@base"][3:] + for n in ids_dict: + if n == "enumeration": + r.type = "enumeration" + for x in ids_dict[n]: + r.options.append(x["@value"]) + elif n[-7:] == "clusive": + r.type = "bounds" + r.options.append({n: ids_dict[n]["@value"]}) elif n[-5:] == "ength": - self.type = "length" + r.type = "length" if n[3:6] == "min": - self.options.append('>=') + r.options.append(">=") elif n[3:6] == "max": - self.options.append('<=') + r.options.append("<=") else: - self.options.append('==') - self.options[-1] += str(node[n]['@value']) - elif n[3:] == "pattern": - self.type = "pattern" - self.options.append(node[n]['@value']) - #TODO add fractionDigits - #TODO add totalDigits - #TODO add whiteSpace + self.options.append("==") + r.options[-1] += str(ids_dict[n]["@value"]) + elif n == "pattern": + r.type = "pattern" + r.options.append(ids_dict[n]["@value"]) + # TODO add fractionDigits + # TODO add totalDigits + # TODO add whiteSpace + elif n == "@base": + pass else: - logger.error({'result':'ERROR', 'sentence':'Restriction not implemented'}) + print("Error! Restriction not implemented") + return r + + def asdict(self): + """Converts object to a dictionary, adding required attributes. + + :return: Xmlschema compliant dictionary. + :rtype: dict + """ + rest_dict = {"@base": "xs:" + self.base} + if self.type == "enumeration": + for option in self.options: + if "xs:enumeration" not in rest_dict: + rest_dict["xs:enumeration"] = [{"@value": option}] + else: + rest_dict["xs:enumeration"].append({"@value": option}) + elif self.type == "bounds": + for option in self.options: + if "xs:option" not in rest_dict: + rest_dict["xs:" + option] = [{"@value": option}] + else: + rest_dict["xs:" + option].append({"@value": self.options[option], "@fixed": False}) + elif self.type == "pattern": + if "xs:pattern" not in rest_dict: + rest_dict["xs:pattern"] = [{"@value": self.options}] + else: + rest_dict["xs:pattern"].append({"@value": self.options}) + return rest_dict + + @staticmethod + def create(options, type="pattern", base="string"): + """Create restriction instead of simpleValue + + :param type: One of "enumeration"|"pattern"|"bounds", defaults to "pattern" + :type type: str, optional + :param options: if enumeration: list of possible values + if pattern: xml regular expression string + if bounds: dictionary with possible keys: 'minInclusive', 'maxInclusive', 'minExclusive', 'maxExclusive' + :type options: list|str|dict + :param base: One of "string"|"boolean"|"decimal"|"integer", defaults to "string" + :type base: str, optional + :raises Exception: If not properly defined restriction. + :return: restriction object + :rtype: restriction + """ + rest = restriction() + if type in ["enumeration", "pattern", "bounds"]: + rest.type = type + rest.base = base + rest.options = options + if ( + (type == "enumeration" and isinstance(options, list)) + or (type == "bounds" and isinstance(options, dict)) + or (type == "pattern" and isinstance(options, str)) + ): + rest.options = options + else: + Exception("Options were not properly defined.") + return rest + else: + raise Exception( + "Such restriction not implemented. Try: 'enumeration', 'pattern' or 'min/maxInclusive' or 'min/maxExclusive'." + ) def __eq__(self, other): - result=False - #TODO implement data type comparison - if self and other: - if self.type == "enumeration" and self.restriction_on == 'bool': + """Evaluate the restriction using equality sign. + + :param other: value to compare with the restriction. + :type other: str|float|int + :return: True if 'other' match the restriction, False if not. + :rtype: bool + """ + result = False + # TODO implement data type comparison + if self and (other or other == 0): + if self.type == "enumeration" and self.base == "bool": self.options = [x.lower() for x in self.options] result = str(other).lower() in self.options elif self.type == "enumeration": result = other in self.options elif self.type == "bounds": - for op in self.options: - if eval(str(other)+op): #TODO eval not safe? - result = True + result = True + for sign in self.options.keys(): + if sign == "minInclusive" and other < self.options[sign]: + result = False + elif sign == "maxInclusive" and other > self.options[sign]: + result = False + elif sign == "minExclusive" and other <= self.options[sign]: + result = False + elif sign == "maxExclusive" and other >= self.options[sign]: + result = False elif self.type == "length": for op in self.options: - if eval(str(len(other))+op): #TODO eval not safe? + if eval(str(len(other)) + op): # TODO eval not safe? result = True elif self.type == "pattern": - self.options - translated_pattern = identities.translate_pattern(r'[A-Z]{1,3}') # Between one and three capital letters + translated_pattern = identities.translate_pattern(self.options) regex_pattern = re.compile(translated_pattern) if regex_pattern.fullmatch(other) is not None: result = True - #TODO add fractionDigits - #TODO add totalDigits - #TODO add whiteSpace + # TODO add fractionDigits + # TODO add totalDigits + # TODO add whiteSpace return result def __repr__(self): + """Represent the restriction in human readible sentence. + + :return: sentence + :rtype: str + """ + msg = "of type '%s', " % (self.base) if self.type == "enumeration": - return "'%s'" % "' or '".join(self.options) + msg = msg + "of value: '%s'" % "' or '".join(self.options) elif self.type == "bounds": - self.options.sort() - return "of type '%s', having a value %s" % (self.restriction_on, ' and '.join(self.options)) + msg = msg + "of value %s" % ", and ".join([bounds[x] + str(self.options[x]) for x in self.options]) elif self.type == "length": - return "of type '%s' with %s letters" % (self.restriction_on, ' and '.join(self.options)) + msg = msg + "with %s letters" % " and ".join(self.options) elif self.type == "pattern": - return "of type '%s' respecting pattern '%s'" % (self.restriction_on, ' and '.join(self.options)) - #TODO add fractionDigits - #TODO add totalDigits - #TODO add whiteSpace + msg = msg + "respecting the pattern '%s'" % self.options + # TODO add fractionDigits + # TODO add totalDigits + # TODO add whiteSpace + return msg -class specification: - """ - Represents the XML node and its two children and - """ +class SimpleHandler(logging.StreamHandler): + """Logging handler listing all cases in python list.""" - def __init__(self, name='Specification'): - self.name = name - self.applicability = None - self.requirements = None + def __init__(self, report_valid=False): + """Logging handler listing all cases in python list. - def asdict(self): - spec_dict = { - '@name': self.name, - 'applicability': {}, - 'requirements': {} - } - for fac in self.applicability.terms: - fclass = type(fac).__name__ - if fclass in spec_dict['applicability']: - spec_dict['applicability'][fclass].append(fac.asdict()) - else: - spec_dict['applicability'][fclass] = [fac.asdict()] - for fac in self.requirements.terms: - fclass = type(fac).__name__ - if fclass in spec_dict['requirements']: - spec_dict['requirements'][fclass].append(fac.asdict()) - else: - spec_dict['requirements'][fclass] = [fac.asdict()] - return spec_dict - - @staticmethod - def parse(node): - def parse_rules(node): - names = [req for req in node for n in node[req]] - children = [child for req in node for child in node[req]] - classes = map(meta_facet.facets.__getitem__, names) - # return [cls.parse(n) for cls, n in zip(classes, children)] - return [cls(n) for cls, n in zip(classes, children)] # list of facet objects - - spec = specification() - spec.name = node['@name'] - spec.applicability = boolean_and(parse_rules(node['applicability'])) - spec.requirements = boolean_and(parse_rules(node['requirements'])) - return spec - - # TODO adding applicability/requirements to specification. How to avoid repetitions? - def add_applicability(self, facet): + :param report_valid: True if you want to list all the compliant cases as well, defaults to False + :type report_valid: bool, optional """ - Applicability specifies what conditions must be meet for an IFC object to be used for validation. - Takes: entity, classification, property or material objects as an input (at least one entity is required). - """ - if self.applicability: - self.applicability = boolean_and( self.applicability.terms + [facet] ) + logging.StreamHandler.__init__(self) + self.statements = [] + if report_valid: + self.setLevel(logging.INFO) else: - self.applicability = boolean_and([facet]) - - def add_requirement(self, facet): + self.setLevel(logging.ERROR) + + def emit(self, mymsg): + """Triggered on each use of logging with the Simple handler enabled. + + :param log_content: default logger message + :type log_content: string|dict """ - Requirement is validated on all applicable IFC elements. - Takes: entity, classification, property or material objects as an input (at least one of them is required). - """ - if self.requirements: - self.requirements = boolean_and( self.requirements.terms + [facet] ) - else: - self.requirements = boolean_and([facet]) - - def __call__(self, inst, logger): - if self.applicability(inst, logger): - - valid = self.requirements(inst, logger) - - if valid: - logger.info({'guid':inst.GlobalId, 'result':valid.success,'sentence':str(self) + ".\n" + inst.is_a() + " '" + str(inst.Name) + "' (#" + str(inst.id()) + ") has " + str(valid) + " so is compliant"}) - return True, True - else: - # BUG "has does not have" - logger.error({'guid':inst.GlobalId, 'result':valid.success, 'sentence':str(self) + ".\n" + inst.is_a() + " '" + str(inst.Name) + "' (#" + str(inst.id()) + ") has " + str(valid) + " so is not compliant"}) - return True, False - else: - return False, False - - def __str__(self): - return "Given an instance with %(applicability)s\nWe expect %(requirements)s" % self.__dict__ + self.statements.append(mymsg.msg) -class ids: - """ - Represents the XML root node and its childNodes. +class BcfHandler(logging.StreamHandler): + """Logging handler for creation of BCF report files. + + :param project_name: defaults to "IDS Project" + :type project_name: str, optional + :param author: Email of the person creating the BCF report, defaults to "your@email.com" + :type author: str, optional + :param filepath: Path to save the BCF report, defaults to None + :type filepath: str, optional + :param report_valid: True if you want to list all the compliant cases as well, defaults to False + :type report_valid: bool, optional + + Example:: + + bcf_handler = BcfHandler( + project_name="Default IDS Project", + author="your@email.com", + filepath=r".\example.bcfzip", + ) + logger = logging.getLogger("IDS_Logger") + logging.basicConfig(level=logging.INFO, format="%(message)s") + logger.addHandler(bcf_handler) """ - def __init__(self): - self.specifications = [] - self.info = None - #self.attributes = { - # '@xmlns:xs': 'http://www.w3.org/2001/XMLSchema', - # '@xmlns': 'http://standards.buildingsmart.org/IDS', - # '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', - # '@xsi:schemaLocation': 'http://standards.buildingsmart.org/IDS http://standards.buildingsmart.org/IDS/ids.xsd', - # } + def __init__(self, project_name="IDS Project", author="your@email.com", filepath=None, report_valid=False): - def asdict(self): - ids_dict = {'@xmlns': 'http://standards.buildingsmart.org/IDS', - '@xmlns:xs': 'http://www.w3.org/2001/XMLSchema', - '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', - '@xsi:schemaLocation': 'http://standards.buildingsmart.org/IDS ' - 'http://standards.buildingsmart.org/IDS/ids.xsd', - 'specification': [], - 'info': self.info, - } - for spec in self.specifications: - ids_dict['specification'].append(spec.asdict()) - return ids_dict + logging.StreamHandler.__init__(self) + if report_valid: + self.setLevel(logging.INFO) + else: + self.setLevel(logging.ERROR) + self.bcf = BcfXml() + self.bcf.author = author + self.bcf.new_project() + self.bcf.project.name = project_name + self.filepath = filepath + self.bcf.edit_project() - def to_xml(self, fn='./', ids_schema=ids_schema): - if fn.endswith('/'): - fn = fn + 'IDS' - if not fn.endswith('.xml'): - fn = fn + '.xml' + def emit(self, log_content): + """Triggered on each use of logging with the BCF handler enabled. - ids_dict = self.asdict() + :param log_content: default logger message + :type log_content: string|dict + """ + topic = bcf.Topic() + topic.title = log_content.msg["sentence"].split(".\n")[1] + topic.description = log_content.msg["sentence"].split(".\n")[0] + self.bcf.add_topic(topic) + try: # Add viewpoint and link to ifc object + viewpoint = bcf.Viewpoint() + viewpoint.perspective_camera = bcf.PerspectiveCamera() + ifc_elem = log_content.msg["ifc_element"] + # ifc_elem = ifc_file.by_guid(log_content.msg["guid"]) + target_position = np.array(ifcopenshell.util.placement.get_local_placement(ifc_elem.ObjectPlacement)) + target_position = target_position[:, 3][0:3] + camera_position = target_position + np.array((5, 5, 5)) + viewpoint.perspective_camera.camera_view_point.x = camera_position[0] + viewpoint.perspective_camera.camera_view_point.y = camera_position[1] + viewpoint.perspective_camera.camera_view_point.z = camera_position[2] + camera_direction = camera_position - target_position + camera_direction = camera_direction / np.linalg.norm(camera_direction) + camera_right = np.cross(np.array([0.0, 0.0, 1.0]), camera_direction) + camera_right = camera_right / np.linalg.norm(camera_right) + camera_up = np.cross(camera_direction, camera_right) + camera_up = camera_up / np.linalg.norm(camera_up) + rotation_transform = np.zeros((4, 4)) + rotation_transform[0, :3] = camera_right + rotation_transform[1, :3] = camera_up + rotation_transform[2, :3] = camera_direction + rotation_transform[-1, -1] = 1 + translation_transform = np.eye(4) + translation_transform[:3, -1] = -camera_position + look_at_transform = np.matmul(rotation_transform, translation_transform) + mat = np.linalg.inv(look_at_transform) + viewpoint.perspective_camera.camera_direction.x = mat[0][2] * -1 + viewpoint.perspective_camera.camera_direction.y = mat[1][2] * -1 + viewpoint.perspective_camera.camera_direction.z = mat[2][2] * -1 + viewpoint.perspective_camera.camera_up_vector.x = mat[0][1] + viewpoint.perspective_camera.camera_up_vector.y = mat[1][1] + viewpoint.perspective_camera.camera_up_vector.z = mat[2][1] + viewpoint.components = bcf.Components() + c = bcf.Component() + c.ifc_guid = log_content.msg["guid"] + viewpoint.components.selection.append(c) + viewpoint.components.visibility = bcf.ComponentVisibility() + viewpoint.components.visibility.default_visibility = True + viewpoint.snapshot = None + self.bcf.add_viewpoint(topic, viewpoint) + except: + pass - ids_xml = ids_schema.encode(ids_dict) #, namespaces='http://standards.buildingsmart.org/IDS') - ids_str = etree_tostring(ids_xml, namespaces={'': 'http://standards.buildingsmart.org/IDS'}) # if restrictions, add also: 'xs': 'http://www.w3.org/2001/XMLSchema' - ids_schema.validate(ids_str) - - with open(fn, 'w') as f: - f.write('\n') - f.write('\n') - f.write(ids_str) - f.close() - - ids_schema.validate(fn) - return ids_schema.is_valid(fn) - - @staticmethod - def parse(fn, ids_schema=ids_schema): - ids_schema.validate(fn) - ids_content = ids_schema.decode(fn) - new_ids = ids() - new_ids.specifications = [specification.parse(s) for s in ids_content['specification']] - return new_ids - - - def validate(self, ifc_file, logger): - self.ifc_checked = 0 - self.ifc_passed = 0 - for spec in self.specifications: - for elem in ifc_file.by_type("IfcObject"): - apply, comply = spec(elem, logger) - if apply: self.ifc_checked += 1 - if comply: self.ifc_passed += 1 + def flush(self): + """Saves the BCF report to file. Triggered at the end of the validation process.""" + if not self.filepath: + self.filepath = os.getcwd() + r"\IDS_report.bcfzip" + if not (self.filepath.endswith(".bcf") or self.filepath.endswith(".bcfzip")): + self.filepath = self.filepath + r"\IDS_report.bcfzip" + self.bcf.save_project(self.filepath) -location = { - 'instance': 'an instance ', - 'type': 'a type ', - 'any': 'a ' +location = {"instance": "an instance ", "type": "a type ", "any": "a "} + +bounds = { + "minInclusive": "larger or equal ", + "maxInclusive": "smaller or equal ", + "minExclusive": "larger than ", + "maxExclusive": "smaller than ", } - if __name__ == "__main__": - import time - start_time = time.time() import sys, os - import logging import ifcopenshell - from datetime import date - - filename = os.path.join(os.getcwd(), str(date.today())+"_ids_result.txt") - - logger = logging.getLogger("IDS") - logging.basicConfig(filename=filename, level=logging.INFO, format="%(message)s") - logging.FileHandler(filename, mode='w') + ids_file = ids.open(sys.argv[1]) ifc_file = ifcopenshell.open(sys.argv[2]) - ids_file = ids.parse(sys.argv[1]) + filepath = sys.argv[3] + + logger = logging.getLogger("IDS_Logger") + logging.basicConfig(filename=filepath, level=logging.INFO, format="%(message)s") + logging.FileHandler(filepath + r"\report.txt", mode="w") + + bcf_handler = BcfHandler( + project_name="Default IDS Project", + author="your@email.com", + filepath=filepath + r"\report.bcfzip", + ) + logger.addHandler(bcf_handler) + + report = SimpleHandler() + logger.addHandler(report) + ids_file.validate(ifc_file, logger) - - print("Out of %s IFC elements, %s were checked against %s requirements in %s specification(s) and %s of them passed (%s).\nRuntime=%ss. Results saved to %s" - % (len(ifc_file.by_type('IfcProduct')), ids_file.ifc_checked, len(ids_file.specifications[0].requirements.terms), len(ids_file.specifications), ids_file.ifc_passed, str(ids_file.ifc_passed/ids_file.ifc_checked*100)+'%', round(time.time() - start_time, 2), filename)) diff --git a/src/ifcopenshell-python/ifcopenshell/ids.xsd b/src/ifcopenshell-python/ifcopenshell/ids.xsd new file mode 100644 index 0000000000..5d8ba0a295 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/ids.xsd @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Make sure 'Name' value of requirements entity is the same as the 'applicability' node, or a wildcard (inclusive pattern). + + + + + + + + + + Author of the IDS can leave instructions for the authors of the IFC. This text could/should be displayed in the BIM/IFC authoring tool. + + + + + + + + + + + + + + + + + + + + + + + + Author of the IDS can leave instructions for the authors of the IFC. This text could/should be displayed in the BIM/IFC authoring tool. + + + + + + + + + + + + + + + + + + + + + + + + Author of the IDS can leave instructions for the authors of the IFC. This text could/should be displayed in the BIM/IFC authoring tool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ifcopenshell-python/ifcopenshell/test_ids.py b/src/ifcopenshell-python/ifcopenshell/test_ids.py index bec6587600..2684e32937 100644 --- a/src/ifcopenshell-python/ifcopenshell/test_ids.py +++ b/src/ifcopenshell-python/ifcopenshell/test_ids.py @@ -1,8 +1,16 @@ import unittest import ids + +# from ids import ids import requests import os +import logging + # from xmlschema.validators.exceptions import XMLSchemaChildrenValidationError +import ifcopenshell +from bcf import bcfxml + +import tempfile def read_web_file(URL): @@ -11,62 +19,104 @@ def read_web_file(URL): class TestIdsParsing(unittest.TestCase): - def test_basic_ids_parse(self): + """Parsing basic IDS files""" + + def test_parse_basic_ids(self): IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_all_fields.xml" - ids_file = ids.ids.parse(read_web_file(IDS_URL)) + ids_file = ids.ids.open(read_web_file(IDS_URL)) self.assertEqual(type(ids_file).__name__, "ids") - def test_entity_facet(self): + def test_parse_entity_facet(self): IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_entity.xml" - ids_file = ids.ids.parse(read_web_file(IDS_URL)) - self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["name"], "IfcWall") + ids_file = ids.ids.open(read_web_file(IDS_URL)) + self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["name"]["simpleValue"], "IfcWall") - def test_predefinedtype_facet(self): + def test_parse_predefinedtype_facet(self): IDS_URL = ( "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_predefinedtype.xml" ) - ids_file = ids.ids.parse(read_web_file(IDS_URL)) - self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["predefinedtype"], "CLADDING") + ids_file = ids.ids.open(read_web_file(IDS_URL)) + self.assertEqual( + ids_file.specifications[0].requirements.terms[0].node["predefinedtype"]["simpleValue"], "CLADDING" + ) - def test_property_facet(self): + def test_parse_property_facet(self): IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_property.xml" - ids_file = ids.ids.parse(read_web_file(IDS_URL)) - self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["propertyset"], "Test_PropertySet") - self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["name"], "Test_Parameter") - self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["value"], "Test_Value") + ids_file = ids.ids.open(read_web_file(IDS_URL)) + self.assertEqual( + ids_file.specifications[0].requirements.terms[0].node["propertyset"]["simpleValue"], "Test_PropertySet" + ) + self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["name"]["simpleValue"], "Test_Parameter") + self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["value"]["simpleValue"], "Test_Value") - def test_material_facet(self): + def test_parse_material_facet(self): IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_material.xml" - ids_file = ids.ids.parse(read_web_file(IDS_URL)) - self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["value"], "Test_Material") + ids_file = ids.ids.open(read_web_file(IDS_URL)) + self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["value"]["simpleValue"], "Test_Material") - def test_classification_facet(self): + def test_parse_classification_facet(self): IDS_URL = ( "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_classification.xml" ) - ids_file = ids.ids.parse(read_web_file(IDS_URL)) - self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["value"], "Test_Classification") - self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["system"], "Test_System") + ids_file = ids.ids.open(read_web_file(IDS_URL)) + self.assertEqual( + ids_file.specifications[0].requirements.terms[0].node["value"]["simpleValue"], "Test_Classification" + ) + self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["system"]["simpleValue"], "Test_System") """ Parsing invalid IDS.xml """ # TODO # def test_invalid_classification_facet(self): # IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/Invalid_IDS_Wall_needs_classification.xml" - # self.assertRaises( XMLSchemaChildrenValidationError, ids.parse(read_web_file(IDS_URL)) ) + # self.assertRaises( XMLSchemaChildrenValidationError, ids.open(read_web_file(IDS_URL)) ) """ Saving parsed IDS to IDS.xml """ def test_parsed_ids_to_xml(self): IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_all_fields.xml" - ids_file = ids.ids.parse(read_web_file(IDS_URL)) + ids_file = ids.ids.open(read_web_file(IDS_URL)) fn = "TEST_FILE.xml" result = ids_file.to_xml(fn) os.remove(fn) self.assertTrue(result) + """ Parsing IDS files with restrictions """ + + def test_parse_restrictions_enumeration(self): + IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_property_with_restriction_enumeration.xml" + ids_file = ids.ids.open(read_web_file(IDS_URL)) + self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["name"]["simpleValue"], "Test_Parameter") + self.assertEqual( + [ + x["@value"] + for x in ids_file.specifications[0].requirements.terms[0].node["value"]["restriction"][0]["enumeration"] + ], + ["testA", "testB"], + ) + + def test_parse_restrictions_bounds(self): + IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_property_with_restriction_bounds.xml" + ids_file = ids.ids.open(read_web_file(IDS_URL)) + self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["name"]["simpleValue"], "Test_Parameter") + self.assertEqual( + ids_file.specifications[0].requirements.terms[0].node["value"]["restriction"][0]["minInclusive"]["@value"], + "0", + ) + + def test_parse_restrictions_pattern_simple(self): + IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_property_with_restriction_pattern.xml" + ids_file = ids.ids.open(read_web_file(IDS_URL)) + self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["name"]["simpleValue"], "Test_Parameter") + self.assertEqual( + ids_file.specifications[0].requirements.terms[0].node["value"]["restriction"][0]["pattern"]["@value"], + "[A-Z]{2,4}", + ) + class TestIdsAuthoring(unittest.TestCase): + """Creating basic IDS""" + def test_entity_create(self): e = ids.entity.create(name="Test_Name", predefinedtype="Test_PredefinedType") self.assertEqual(e.name, "Test_Name") @@ -96,11 +146,6 @@ class TestIdsAuthoring(unittest.TestCase): s = ids.specification(name="Test_Specification") self.assertEqual(s.name, "Test_Specification") - def test_ids_create(self): - i = ids.ids() - self.assertEqual(i.specifications, []) - self.assertEqual(i.info, None) - def test_ids_add_content(self): i = ids.ids() i.specifications.append(ids.specification(name="Test_Specification")) @@ -115,6 +160,55 @@ class TestIdsAuthoring(unittest.TestCase): i.specifications[0].add_requirement(m) self.assertEqual(i.specifications[0].requirements.terms[1].value, "Test_Value") + """ Creating IDS with restrictions """ + + def test_create_restrictions_enumeration(self): + i = ids.ids() + i.specifications.append(ids.specification(name="Test_Specification")) + i.specifications[0].add_applicability(ids.entity.create(name="Test_Name")) + r = ids.restriction.create(options=["testA", "testB"], type="enumeration", base="string") + m = ids.material.create(location="any", value=r) + i.specifications[0].add_requirement(m) + self.assertEqual(i.specifications[0].requirements.terms[0].value, "testA") + self.assertEqual(i.specifications[0].requirements.terms[0].value, "testB") + self.assertNotEqual(i.specifications[0].requirements.terms[0].value, "testC") + + def test_create_restrictions_bounds(self): + i = ids.ids() + i.specifications.append(ids.specification(name="Test_Specification")) + i.specifications[0].add_applicability(ids.entity.create(name="Test_Name")) + r = ids.restriction.create(options={"minInclusive": 0, "maxExclusive": 10}, type="bounds", base="integer") + p = ids.property.create(location="any", propertyset="Test", name="Test", value=r) + i.specifications[0].add_requirement(p) + self.assertEqual(i.specifications[0].requirements.terms[0].value, 0) + self.assertEqual(i.specifications[0].requirements.terms[0].value, 5) + self.assertNotEqual(i.specifications[0].requirements.terms[0].value, -1) + self.assertNotEqual(i.specifications[0].requirements.terms[0].value, 10) + + def test_create_restrictions_pattern_simple(self): + i = ids.ids() + i.specifications.append(ids.specification(name="Test_Specification")) + i.specifications[0].add_applicability(ids.entity.create(name="Test_Name")) + r = ids.restriction.create(options="[A-Z]{2,4}", type="pattern", base="string") + p = ids.property.create(location="any", propertyset="Test", name="Test", value=r) + i.specifications[0].add_requirement(p) + self.assertEqual(i.specifications[0].requirements.terms[0].value, "XYZ") + self.assertNotEqual(i.specifications[0].requirements.terms[0].value, "abc") + self.assertNotEqual(i.specifications[0].requirements.terms[0].value, "ABCDE") + self.assertNotEqual(i.specifications[0].requirements.terms[0].value, "A") + + def test_create_restrictions_pattern_advanced(self): + i = ids.ids() + i.specifications.append(ids.specification(name="Test_Specification")) + i.specifications[0].add_applicability(ids.entity.create(name="Test_Name")) + # r = ids.restriction.create(options="^(Wanddurchbruch.*|Deckendurchbruch.*)", type="pattern", base="string") + r = ids.restriction.create(options="(Wanddurchbruch|Deckendurchbruch).*", type="pattern", base="string") + p = ids.property.create(location="any", propertyset="Test", name="Test", value=r) + i.specifications[0].add_requirement(p) + self.assertEqual(i.specifications[0].requirements.terms[0].value, "Wanddurchbruch") + self.assertEqual(i.specifications[0].requirements.terms[0].value, "Deckendurchbruch") + self.assertNotEqual(i.specifications[0].requirements.terms[0].value, "Deeckendurchbruch") + """ Saving created IDS to IDS.xml """ def test_created_ids_to_xml(self): @@ -123,25 +217,116 @@ class TestIdsAuthoring(unittest.TestCase): e = ids.entity.create(name="Test_Name", predefinedtype="Test_PredefinedType") c = ids.classification.create(location="any", value="Test_Value", system="Test_System") m = ids.material.create(location="any", value="Test_Value") - p = ids.property.create(location="any", propertyset="Test_PropertySet", name="Test_Parameter", value="Test_Value") + re = ids.restriction.create(options=["testA", "testB"], type="enumeration", base="string") + rb = ids.restriction.create(options={"minInclusive": 0, "maxExclusive": 10}, type="bounds", base="integer") + rp = ids.restriction.create(options="[A-Z]{2,4}", type="pattern", base="string") + p1 = ids.property.create(location="any", propertyset="Test_PropertySet", name="Test_Parameter", value=re) + p2 = ids.property.create(location="any", propertyset="Test_PropertySet", name="Test_Parameter", value=rb) + p3 = ids.property.create(location="any", propertyset="Test_PropertySet", name="Test_Parameter", value=rp) + p4 = ids.property.create( + location="any", propertyset="Test_PropertySet", name="Test_Parameter", value=[re, rb, rp] + ) i.specifications[0].add_applicability(e) i.specifications[0].add_applicability(m) i.specifications[0].add_requirement(c) - i.specifications[0].add_requirement(p) + i.specifications[0].add_requirement(p1) + i.specifications[0].add_requirement(p2) + i.specifications[0].add_requirement(p3) + i.specifications[0].add_requirement(p4) fn = "TEST_FILE.xml" result = i.to_xml(fn) os.remove(fn) self.assertTrue(result) + """ IDS information """ + + def test_create_full_information(self): + i = ids.ids( + ifcversion="2.3.0.1", + description="test", + author="test@test.com", + copyright="test", + version=1.23, + creation_date="2021-01-01", + purpose="test", + milestone="test", + ) + self.assertEqual(i.info["version"], 1.23) + class TestIfcValidation(unittest.TestCase): - pass - # TODO + def test_validate_simple(self): + # TODO + pass + + def test_validate_all_facets(self): + # TODO + pass + + """ Validating IDS files with restrictions """ + + # def test_validate_restrictions_enumeration(self): + # IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_property_with_restriction_enumeration.xml" + # ids_file = ids.ids.open(read_web_file(IDS_URL)) + # self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["name"]['simpleValue'], "Test_Parameter") + # self.assertEqual( [x['@value'] for x in ids_file.specifications[0].requirements.terms[0].node["value"]['restriction'][0]['enumeration'] ], ['testA', 'testB']) + # # TODO actual test of validation result + # # self.assertTrue( ) + + # def test_validate_restrictions_boundsInclusive(self): + # IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_property_with_restriction_bounds.xml" + # ids_file = ids.ids.open(read_web_file(IDS_URL)) + # self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["name"]['simpleValue'], "Test_Parameter") + # self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["value"]['restriction'][0]['minInclusive']['@value'], '0') + # # TODO actual test of validation result + # # self.assertTrue( ) + + # def test_validate_restrictions_boundsExclusive(self): + # #TODO + # pass + + # def test_validate_restrictions_pattern_simple(self): + # IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_property_with_restriction_pattern.xml" + # ids_file = ids.ids.open(read_web_file(IDS_URL)) + # self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["name"]['simpleValue'], "Test_Parameter") + # self.assertEqual(ids_file.specifications[0].requirements.terms[0].node["value"]['restriction'][0]['pattern']['@value'], '[A-Z]{2,4}') + # # TODO actual test of validation result + # # self.assertTrue( ) -class TestIdsResults(unittest.TestCase): - pass - # TODO +class TestIdsReporting(unittest.TestCase): + + TEST_PATH = os.getcwd() + IFC_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IFC/IFC4_Wall_3_with_properties.ifc" + IDS_URL = "https://raw.githubusercontent.com/atomczak/Sample-BIM-Files/main/IDS/IDS_Wall_needs_all_fields.xml" + + logger = logging.getLogger("IDS_Logger") + # logging.basicConfig(level=logging.INFO, format="%(message)s") + # logging.basicConfig(filename=TEST_PATH+r"\log.txt", level=logging.INFO, format="%(message)s") + + content = read_web_file(IFC_URL) + file = open(TEST_PATH + r"\test.ifc", "w") + file.write(content) + file.close() + ifc_file = ifcopenshell.open(TEST_PATH + r"\test.ifc") + os.remove(TEST_PATH + r"\test.ifc") + + def test_simple_report(self): + ids_file = ids.ids.open(read_web_file(self.IDS_URL)) + report = ids.SimpleHandler() + self.logger.addHandler(report) + ids_file.validate(self.ifc_file, self.logger) + self.assertEqual(len(report.statements), 5) + + def test_bcf_report(self): + ids_file = ids.ids.open(read_web_file(self.IDS_URL)) + fn = tempfile.gettempdir() + r"\bcf_test.bcfzip" + bcf_handler = ids.BcfHandler(project_name="Default IDS Project", author="your@email.com", filepath=fn) + self.logger.addHandler(bcf_handler) + ids_file.validate(self.ifc_file, self.logger) + my_bcfxml = bcfxml.load(fn) + topics = my_bcfxml.get_topics() + self.assertEqual(len(topics), 5) if __name__ == "__main__": From 112ce48350093f115a6bab8ceaaa9dd81437ca5c Mon Sep 17 00:00:00 2001 From: Gorgious56 Date: Tue, 24 Aug 2021 13:58:52 +0200 Subject: [PATCH 003/113] Improve groups and systems interface (#1688) * Enable Batch adding/removing objects from groups Might hang up Blender for a few secs with a big number of object on execution * Enable Batch adding/removing objects from systems Might hang up Blender for a few secs with a big number of object on execution * Add Group Panel in object properties + Transfer adding object(s) to group into the object properties * Add Systems Panel in object properties + Transfer adding object(s) to system into the object properties * Simplify group & systems operators * Disabling Editing of groups/systems also disables individual edition --- .../blenderbim/bim/module/group/__init__.py | 3 + .../blenderbim/bim/module/group/operator.py | 55 ++++++++----- .../blenderbim/bim/module/group/prop.py | 1 + .../blenderbim/bim/module/group/ui.py | 81 ++++++++++++++----- .../blenderbim/bim/module/system/__init__.py | 3 + .../blenderbim/bim/module/system/operator.py | 61 +++++++++----- .../blenderbim/bim/module/system/prop.py | 1 + .../blenderbim/bim/module/system/ui.py | 81 ++++++++++++++----- 8 files changed, 206 insertions(+), 80 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/group/__init__.py b/src/blenderbim/blenderbim/bim/module/group/__init__.py index 4cf5131a2f..0bea604778 100644 --- a/src/blenderbim/blenderbim/bim/module/group/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/group/__init__.py @@ -26,6 +26,7 @@ classes = ( operator.AddGroup, operator.EditGroup, operator.RemoveGroup, + operator.ToggleAssigningGroup, operator.AssignGroup, operator.UnassignGroup, operator.EnableEditingGroup, @@ -34,7 +35,9 @@ classes = ( prop.Group, prop.BIMGroupProperties, ui.BIM_PT_groups, + ui.BIM_PT_object_groups, ui.BIM_UL_groups, + ui.BIM_UL_object_groups, ) diff --git a/src/blenderbim/blenderbim/bim/module/group/operator.py b/src/blenderbim/blenderbim/bim/module/group/operator.py index 0cd773791f..0485249733 100644 --- a/src/blenderbim/blenderbim/bim/module/group/operator.py +++ b/src/blenderbim/blenderbim/bim/module/group/operator.py @@ -48,6 +48,7 @@ class DisableGroupEditingUI(bpy.types.Operator): def execute(self, context): context.scene.BIMGroupProperties.is_editing = False + context.scene.BIMGroupProperties.active_group_id = 0 return {"FINISHED"} @@ -145,6 +146,16 @@ class DisableEditingGroup(bpy.types.Operator): return {"FINISHED"} +class ToggleAssigningGroup(bpy.types.Operator): + bl_idname = "bim.toggle_assigning_group" + bl_label = "Toggle Assigning Group" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + context.scene.BIMGroupProperties.is_adding = not context.scene.BIMGroupProperties.is_adding + return {"FINISHED"} + + class AssignGroup(bpy.types.Operator): bl_idname = "bim.assign_group" bl_label = "Assign Group" @@ -156,17 +167,20 @@ class AssignGroup(bpy.types.Operator): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - product = bpy.data.objects.get(self.product) if self.product else context.active_object self.file = IfcStore.get_file() - ifcopenshell.api.run( - "group.assign_group", - self.file, - **{ - "product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id), - "group": self.file.by_id(self.group), - } - ) - Data.load(IfcStore.get_file()) + products = [bpy.data.objects.get(self.product)] if self.product else context.selected_objects + for product in products: + if not product.BIMObjectProperties.ifc_definition_id: + continue + ifcopenshell.api.run( + "group.assign_group", + self.file, + **{ + "product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id), + "group": self.file.by_id(self.group), + } + ) + Data.load(self.file) return {"FINISHED"} @@ -181,16 +195,19 @@ class UnassignGroup(bpy.types.Operator): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - product = bpy.data.objects.get(self.product) if self.product else context.active_object self.file = IfcStore.get_file() - ifcopenshell.api.run( - "group.unassign_group", - self.file, - **{ - "product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id), - "group": self.file.by_id(self.group), - } - ) + products = [bpy.data.objects.get(self.product)] if self.product else context.selected_objects + for product in products: + if not product.BIMObjectProperties.ifc_definition_id: + continue + ifcopenshell.api.run( + "group.unassign_group", + self.file, + **{ + "product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id), + "group": self.file.by_id(self.group), + } + ) Data.load(IfcStore.get_file()) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/group/prop.py b/src/blenderbim/blenderbim/bim/module/group/prop.py index 05decfb78d..590d2efd85 100644 --- a/src/blenderbim/blenderbim/bim/module/group/prop.py +++ b/src/blenderbim/blenderbim/bim/module/group/prop.py @@ -40,6 +40,7 @@ class Group(PropertyGroup): class BIMGroupProperties(PropertyGroup): group_attributes: CollectionProperty(name="Group Attributes", type=Attribute) is_editing: BoolProperty(name="Is Editing", default=False) + is_adding: BoolProperty(name="Is Adding", default=False) groups: CollectionProperty(name="Groups", type=Group) active_group_index: IntProperty(name="Active Group Index") active_group_id: IntProperty(name="Active Group Id") diff --git a/src/blenderbim/blenderbim/bim/module/group/ui.py b/src/blenderbim/blenderbim/bim/module/group/ui.py index 479a4c787a..4142ab0db2 100644 --- a/src/blenderbim/blenderbim/bim/module/group/ui.py +++ b/src/blenderbim/blenderbim/bim/module/group/ui.py @@ -68,37 +68,78 @@ class BIM_PT_groups(Panel): row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") +class BIM_PT_object_groups(Panel): + bl_label = "IFC Groups" + bl_idname = "BIM_PT_object_groups" + bl_options = {"DEFAULT_CLOSED"} + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "object" + + @classmethod + def poll(cls, context): + return IfcStore.get_file() and context.active_object.BIMObjectProperties.ifc_definition_id + + def draw(self, context): + if not Data.is_loaded: + Data.load(IfcStore.get_file()) + self.props = context.scene.BIMGroupProperties + row = self.layout.row(align=True) + if self.props.is_adding: + row.label(text="Adding Groups", icon="OUTLINER") + row.operator("bim.toggle_assigning_group", text="", icon="CANCEL") + self.layout.template_list( + "BIM_UL_object_groups", + "", + self.props, + "groups", + self.props, + "active_group_index", + ) + else: + row.label(text=f"{len(Data.groups)} Groups in IFC Project", icon="OUTLINER") + row.operator("bim.toggle_assigning_group", text="", icon="ADD") + + groups_object = Data.products.get(context.active_object.BIMObjectProperties.ifc_definition_id, []) + for group_id in groups_object: + row = self.layout.row(align=True) + row.label(text=Data.groups[group_id].get("Name", "Unnamed")) + op = row.operator("bim.unassign_group", text="", icon="X") + op.group = group_id + + if not groups_object: + self.layout.label(text="No Group associated with Active Object") + class BIM_UL_groups(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: row = layout.row(align=True) row.label(text=item.name) - - if context.active_object: - oprops = context.active_object.BIMObjectProperties - if ( - oprops.ifc_definition_id in Data.products - and item.ifc_definition_id in Data.products[oprops.ifc_definition_id] - ): - op = row.operator("bim.unassign_group", text="", icon="KEYFRAME_HLT", emboss=False) - op.group = item.ifc_definition_id - else: - op = row.operator("bim.assign_group", text="", icon="KEYFRAME", emboss=False) - op.group = item.ifc_definition_id - - if context.scene.BIMGroupProperties.active_group_id == item.ifc_definition_id: + group_id = item.ifc_definition_id + if context.scene.BIMGroupProperties.active_group_id == group_id: op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF") - op.group = item.ifc_definition_id + op.group = group_id row.operator("bim.edit_group", text="", icon="CHECKMARK") row.operator("bim.disable_editing_group", text="", icon="CANCEL") elif context.scene.BIMGroupProperties.active_group_id: op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF") - op.group = item.ifc_definition_id - row.operator("bim.remove_group", text="", icon="X").group = item.ifc_definition_id + op.group = group_id + op = row.operator("bim.remove_group", text="", icon="X") + op.group = group_id else: op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF") - op.group = item.ifc_definition_id + op.group = group_id op = row.operator("bim.enable_editing_group", text="", icon="GREASEPENCIL") - op.group = item.ifc_definition_id - row.operator("bim.remove_group", text="", icon="X").group = item.ifc_definition_id + op.group = group_id + op = row.operator("bim.remove_group", text="", icon="X") + op.group = group_id + + +class BIM_UL_object_groups(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + if item: + row = layout.row(align=True) + row.label(text=item.name) + op = row.operator("bim.assign_group", text="", icon="ADD") + op.group = item.ifc_definition_id diff --git a/src/blenderbim/blenderbim/bim/module/system/__init__.py b/src/blenderbim/blenderbim/bim/module/system/__init__.py index c5652828e0..47c1210569 100644 --- a/src/blenderbim/blenderbim/bim/module/system/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/system/__init__.py @@ -26,6 +26,7 @@ classes = ( operator.AddSystem, operator.EditSystem, operator.RemoveSystem, + operator.ToggleAssigningSystem, operator.AssignSystem, operator.UnassignSystem, operator.EnableEditingSystem, @@ -34,7 +35,9 @@ classes = ( prop.System, prop.BIMSystemProperties, ui.BIM_PT_systems, + ui.BIM_PT_object_systems, ui.BIM_UL_systems, + ui.BIM_UL_object_systems, ) diff --git a/src/blenderbim/blenderbim/bim/module/system/operator.py b/src/blenderbim/blenderbim/bim/module/system/operator.py index 51597aa7c0..23ce075299 100644 --- a/src/blenderbim/blenderbim/bim/module/system/operator.py +++ b/src/blenderbim/blenderbim/bim/module/system/operator.py @@ -48,6 +48,7 @@ class DisableSystemEditingUI(bpy.types.Operator): def execute(self, context): context.scene.BIMSystemProperties.is_editing = False + context.scene.BIMSystemProperties.active_system_id = 0 return {"FINISHED"} @@ -145,6 +146,16 @@ class DisableEditingSystem(bpy.types.Operator): return {"FINISHED"} +class ToggleAssigningSystem(bpy.types.Operator): + bl_idname = "bim.toggle_assigning_system" + bl_label = "Toggle Assigning System" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + context.scene.BIMSystemProperties.is_adding = not context.scene.BIMSystemProperties.is_adding + return {"FINISHED"} + + class AssignSystem(bpy.types.Operator): bl_idname = "bim.assign_system" bl_label = "Assign System" @@ -156,17 +167,20 @@ class AssignSystem(bpy.types.Operator): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - product = bpy.data.objects.get(self.product) if self.product else context.active_object self.file = IfcStore.get_file() - ifcopenshell.api.run( - "system.assign_system", - self.file, - **{ - "product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id), - "system": self.file.by_id(self.system), - } - ) - Data.load(IfcStore.get_file()) + products = [bpy.data.objects.get(self.product)] if self.product else context.selected_objects + for product in products: + if not product.BIMObjectProperties.ifc_definition_id: + continue + ifcopenshell.api.run( + "system.assign_system", + self.file, + **{ + "product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id), + "system": self.file.by_id(self.system), + } + ) + Data.load(self.file) return {"FINISHED"} @@ -181,17 +195,23 @@ class UnassignSystem(bpy.types.Operator): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - product = bpy.data.objects.get(self.product) if self.product else context.active_object self.file = IfcStore.get_file() - ifcopenshell.api.run( - "system.unassign_system", - self.file, - **{ - "product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id), - "system": self.file.by_id(self.system), - } - ) - Data.load(IfcStore.get_file()) + products = [bpy.data.objects.get(self.product)] if self.product else context.selected_objects + for product in products: + props = product.BIMObjectProperties + if not props.ifc_definition_id: + continue + if not (props.ifc_definition_id in Data.products and self.system in Data.products[props.ifc_definition_id]): + continue + ifcopenshell.api.run( + "system.unassign_system", + self.file, + **{ + "product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id), + "system": self.file.by_id(self.system), + } + ) + Data.load(self.file) return {"FINISHED"} @@ -202,7 +222,6 @@ class SelectSystemProducts(bpy.types.Operator): system: bpy.props.IntProperty() def execute(self, context): - self.file = IfcStore.get_file() for obj in context.visible_objects: obj.select_set(False) if not obj.BIMObjectProperties.ifc_definition_id: diff --git a/src/blenderbim/blenderbim/bim/module/system/prop.py b/src/blenderbim/blenderbim/bim/module/system/prop.py index f792ac8597..328b56882f 100644 --- a/src/blenderbim/blenderbim/bim/module/system/prop.py +++ b/src/blenderbim/blenderbim/bim/module/system/prop.py @@ -40,6 +40,7 @@ class System(PropertyGroup): class BIMSystemProperties(PropertyGroup): system_attributes: CollectionProperty(name="System Attributes", type=Attribute) is_editing: BoolProperty(name="Is Editing", default=False) + is_adding: BoolProperty(name="Is Adding", default=False) systems: CollectionProperty(name="Systems", type=System) active_system_index: IntProperty(name="Active System Index") active_system_id: IntProperty(name="Active System Id") diff --git a/src/blenderbim/blenderbim/bim/module/system/ui.py b/src/blenderbim/blenderbim/bim/module/system/ui.py index 516e382a90..e63267ca2a 100644 --- a/src/blenderbim/blenderbim/bim/module/system/ui.py +++ b/src/blenderbim/blenderbim/bim/module/system/ui.py @@ -68,37 +68,78 @@ class BIM_PT_systems(Panel): row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") +class BIM_PT_object_systems(Panel): + bl_label = "IFC Systems" + bl_idname = "BIM_PT_object_systems" + bl_options = {"DEFAULT_CLOSED"} + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "object" + + @classmethod + def poll(cls, context): + return IfcStore.get_file() and context.active_object.BIMObjectProperties.ifc_definition_id + + def draw(self, context): + if not Data.is_loaded: + Data.load(IfcStore.get_file()) + self.props = context.scene.BIMSystemProperties + row = self.layout.row(align=True) + if self.props.is_adding: + row.label(text="Adding Systems", icon="OUTLINER") + row.operator("bim.toggle_assigning_system", text="", icon="CANCEL") + self.layout.template_list( + "BIM_UL_object_systems", + "", + self.props, + "systems", + self.props, + "active_system_index", + ) + else: + row.label(text=f"{len(Data.systems)} Systems in IFC Project", icon="OUTLINER") + row.operator("bim.toggle_assigning_system", text="", icon="ADD") + + systems_object = Data.products.get(context.active_object.BIMObjectProperties.ifc_definition_id, []) + for system_id in systems_object: + row = self.layout.row(align=True) + row.label(text=Data.systems[system_id].get("Name", "Unnamed")) + op = row.operator("bim.unassign_system", text="", icon="X") + op.system = system_id + + if not systems_object: + self.layout.label(text="No System associated with Active Object") + class BIM_UL_systems(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: row = layout.row(align=True) row.label(text=item.name) - - if context.active_object: - oprops = context.active_object.BIMObjectProperties - if ( - oprops.ifc_definition_id in Data.products - and item.ifc_definition_id in Data.products[oprops.ifc_definition_id] - ): - op = row.operator("bim.unassign_system", text="", icon="KEYFRAME_HLT", emboss=False) - op.system = item.ifc_definition_id - else: - op = row.operator("bim.assign_system", text="", icon="KEYFRAME", emboss=False) - op.system = item.ifc_definition_id - - if context.scene.BIMSystemProperties.active_system_id == item.ifc_definition_id: + system_id = item.ifc_definition_id + if context.scene.BIMSystemProperties.active_system_id == system_id: op = row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF") - op.system = item.ifc_definition_id + op.system = system_id row.operator("bim.edit_system", text="", icon="CHECKMARK") row.operator("bim.disable_editing_system", text="", icon="CANCEL") elif context.scene.BIMSystemProperties.active_system_id: op = row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF") - op.system = item.ifc_definition_id - row.operator("bim.remove_system", text="", icon="X").system = item.ifc_definition_id + op.system = system_id + op = row.operator("bim.remove_system", text="", icon="X") + op.system = system_id else: op = row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF") - op.system = item.ifc_definition_id + op.system = system_id op = row.operator("bim.enable_editing_system", text="", icon="GREASEPENCIL") - op.system = item.ifc_definition_id - row.operator("bim.remove_system", text="", icon="X").system = item.ifc_definition_id + op.system = system_id + op = row.operator("bim.remove_system", text="", icon="X") + op.system = system_id + + +class BIM_UL_object_systems(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + if item: + row = layout.row(align=True) + row.label(text=item.name) + op = row.operator("bim.assign_system", text="", icon="ADD") + op.system = item.ifc_definition_id From a430d356c079d6bd135be9e3a9bdfa66e210aede Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 25 Aug 2021 16:27:23 +1000 Subject: [PATCH 004/113] You can now parametrically derive task durations from resource utilisation --- .../bim/module/sequence/__init__.py | 1 + .../bim/module/sequence/operator.py | 21 ++++++- .../blenderbim/bim/module/sequence/ui.py | 19 +++--- .../api/sequence/calculate_task_duration.py | 62 +++++++++++++++++++ 4 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 10d973b167..1997e93671 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -98,6 +98,7 @@ classes = ( operator.LoadTaskInputs, operator.LoadTaskResources, operator.LoadTaskOutputs, + operator.DeriveTaskDuration, prop.WorkPlan, prop.BIMWorkPlanProperties, prop.Task, diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index 0af442fffe..00c0e8bfd3 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -443,7 +443,7 @@ class AddTask(bpy.types.Operator): def _execute(self, context): props = context.scene.BIMWorkScheduleProperties self.file = IfcStore.get_file() - ifcopenshell.api.run("sequence.add_task", self.file, **{"parent_task": self.file.by_id(self.task)}) + ifcopenshell.api.run("sequence.add_task", self.file, parent_task=self.file.by_id(self.task)) Data.load(self.file) bpy.ops.bim.enable_editing_tasks(work_schedule=props.active_work_schedule_id) return {"FINISHED"} @@ -461,7 +461,7 @@ class AddSummaryTask(bpy.types.Operator): def _execute(self, context): props = context.scene.BIMWorkScheduleProperties self.file = IfcStore.get_file() - ifcopenshell.api.run("sequence.add_task", self.file, **{"work_schedule": self.file.by_id(self.work_schedule)}) + ifcopenshell.api.run("sequence.add_task", self.file, work_schedule=self.file.by_id(self.work_schedule)) Data.load(self.file) bpy.ops.bim.enable_editing_tasks(work_schedule=props.active_work_schedule_id) return {"FINISHED"} @@ -2098,9 +2098,24 @@ class LoadTaskOutputs(bpy.types.Operator): self.tprops = context.scene.BIMTaskTreeProperties ifc_definition_id = self.tprops.tasks[self.props.active_task_index].ifc_definition_id self.props.task_outputs.clear() - for output_id in Data.tasks[ifc_definition_id]["outputs"]: + for output_id in Data.tasks[ifc_definition_id]["Outputs"]: product = self.file.by_id(output_id) new = self.props.task_outputs.add() new.ifc_definition_id = output_id new.name = product.Name or "Unnamed" return {"FINISHED"} + + +class DeriveTaskDuration(bpy.types.Operator): + bl_idname = "bim.derive_task_duration" + bl_label = "Derive Task Duration" + bl_options = {"REGISTER", "UNDO"} + task: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMWorkScheduleProperties + self.file = IfcStore.get_file() + ifcopenshell.api.run("sequence.calculate_task_duration", self.file, task=self.file.by_id(self.task)) + Data.load(self.file) + bpy.ops.bim.enable_editing_tasks(work_schedule=props.active_work_schedule_id) + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index 7586690242..42dea09f5e 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -363,9 +363,7 @@ class BIM_PT_task_icom(Panel): op.related_object = "" row2 = col.row() - row2.template_list( - "BIM_UL_task_inputs", "", self.props, "task_inputs", self.props, "active_task_input_index" - ) + row2.template_list("BIM_UL_task_inputs", "", self.props, "task_inputs", self.props, "active_task_input_index") # Column2 col = grid.column() @@ -373,17 +371,24 @@ class BIM_PT_task_icom(Panel): row2 = col.row(align=True) row2.label(text="Resources") + op = row2.operator("bim.derive_task_duration", text="", icon="TIME") + op.task = task.ifc_definition_id + total_resources = len(context.scene.BIMResourceTreeProperties.resources) if total_resources and context.scene.BIMResourceProperties.active_resource_index < total_resources: - resource_id = context.scene.BIMResourceTreeProperties.resources[context.scene.BIMResourceProperties.active_resource_index].ifc_definition_id op = row2.operator("bim.assign_process", icon="ADD", text="") op.task = task.ifc_definition_id op.related_object_type = "RESOURCE" - op.resource = resource_id + op.resource = context.scene.BIMResourceTreeProperties.resources[ + context.scene.BIMResourceProperties.active_resource_index + ].ifc_definition_id + + total_task_resources = len(self.props.task_resources) + if total_task_resources and self.props.active_task_resource_index < total_task_resources: op = row2.operator("bim.unassign_process", icon="REMOVE", text="") op.task = task.ifc_definition_id op.related_object_type = "RESOURCE" - op.resource = resource_id + op.resource = self.props.task_resources[self.props.active_task_resource_index].ifc_definition_id row2 = col.row() row2.template_list( @@ -430,7 +435,7 @@ class BIM_UL_task_inputs(UIList): if item: row = layout.row(align=True) row.prop(item, "name", emboss=False, text="") - #row.operator("bim.remove_task_column", text="", icon="X").name = item.name + # row.operator("bim.remove_task_column", text="", icon="X").name = item.name class BIM_UL_task_resources(UIList): diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py new file mode 100644 index 0000000000..93520337ff --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py @@ -0,0 +1,62 @@ +import math +import ifcopenshell.api +import ifcopenshell.util.date +import ifcopenshell.util.element + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"task": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + self.seconds_per_day = self.calculate_seconds_per_day() + duration = self.calculate_max_resource_usage_duration() + if duration: + self.set_task_duration(duration) + + def calculate_max_resource_usage_duration(self): + max_duration = 0 + for rel in self.settings["task"].OperatesOn or []: + for related_object in rel.RelatedObjects: + if related_object.is_a("IfcConstructionResource"): + duration = self.calculate_duration_in_days(related_object) + if duration and duration > max_duration: + max_duration = duration + return max_duration + + def set_task_duration(self, duration): + if not self.settings["task"].TaskTime: + ifcopenshell.api.run("sequence.add_task_time", self.file, task=self.settings["task"]) + self.settings["task"].TaskTime.ScheduleDuration = f"P{duration}D" + + def calculate_seconds_per_day(self): + default_seconds_per_day = 8 * 60 * 60 + work_schedule = self.get_work_schedule() + if not work_schedule: + return default_seconds_per_day + psets = ifcopenshell.util.element.get_psets(work_schedule) + if not psets or "Pset_WorkControlCommon" not in psets or "WorkDayDuration" not in psets["Pset_WorkControlCommon"]: + return default_seconds_per_day + work_day_duration = ifcopenshell.util.date.ifc2datetime(psets["Pset_WorkControlCommon"]["WorkDayDuration"]) + return work_day_duration.seconds + + def get_work_schedule(self): + for rel in self.settings["task"].HasAssignments or []: + if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkSchedule"): + return rel.RelatingControl + for rel in self.settings["task"].Nests or []: + return self.get_work_schedule(rel.RelatingObject) + + def calculate_duration_in_days(self, resource): + if not resource.Usage or not resource.Usage.ScheduleWork: + return + schedule_usage = resource.Usage.ScheduleUsage or 1 + schedule_duration = ifcopenshell.util.date.ifc2datetime(resource.Usage.ScheduleWork) + schedule_seconds = 0 + if schedule_duration.days: + schedule_seconds += schedule_duration.days * self.seconds_per_day + schedule_seconds += schedule_duration.seconds + return math.ceil((schedule_seconds / self.seconds_per_day) / schedule_usage) From 8fa1a99864c8b2723f7e87ac3cbab303d1944a3d Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 25 Aug 2021 17:22:02 +1000 Subject: [PATCH 005/113] Fix bug where the pset templates with multiple applicable entities only checked the first entity --- src/ifcopenshell-python/ifcopenshell/util/pset.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/util/pset.py b/src/ifcopenshell-python/ifcopenshell/util/pset.py index 1f6bf735bb..da63a46fe9 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/pset.py +++ b/src/ifcopenshell-python/ifcopenshell/util/pset.py @@ -1,11 +1,11 @@ -import pathlib import re +import pathlib +import ifcopenshell +import ifcopenshell.util.schema +from ifcopenshell.entity_instance import entity_instance from functools import lru_cache from typing import List, Generator, Optional -import ifcopenshell -from ifcopenshell.entity_instance import entity_instance - templates = {} @@ -71,10 +71,8 @@ class PsetQto: continue applicable_class = match.group(1) - if entity.name() == applicable_class: + if ifcopenshell.util.schema.is_a(entity, applicable_class): return True - if entity.supertype(): - return self.is_applicable(entity.supertype(), applicable_class) return False @lru_cache() From ae42d91c5cc18471ee78c02ca692554bd3a05c40 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 25 Aug 2021 17:24:18 +1000 Subject: [PATCH 006/113] You can now manage psets on resources --- .../blenderbim/bim/module/pset/__init__.py | 1 + .../blenderbim/bim/module/pset/prop.py | 12 +++++++ .../blenderbim/bim/module/pset/ui.py | 34 +++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/src/blenderbim/blenderbim/bim/module/pset/__init__.py b/src/blenderbim/blenderbim/bim/module/pset/__init__.py index ace6e1253f..9ce18f5016 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/pset/__init__.py @@ -40,6 +40,7 @@ classes = ( ui.BIM_PT_material_psets, ui.BIM_PT_task_qtos, ui.BIM_PT_resource_qtos, + ui.BIM_PT_resource_psets, ui.BIM_PT_profile_psets, ui.BIM_PT_work_schedule_psets, ) diff --git a/src/blenderbim/blenderbim/bim/module/pset/prop.py b/src/blenderbim/blenderbim/bim/module/pset/prop.py index ee2c1c4f4a..03959f9969 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/prop.py +++ b/src/blenderbim/blenderbim/bim/module/pset/prop.py @@ -80,6 +80,17 @@ def getTaskQtoNames(self, context): return qtonames[ifc_class] +def getResourcePsetNames(self, context): + global psetnames + rprops = context.scene.BIMResourceProperties + rtprops = context.scene.BIMResourceTreeProperties + ifc_class = IfcStore.get_file().by_id(rtprops.resources[rprops.active_resource_index].ifc_definition_id).is_a() + if ifc_class not in psetnames: + psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True) + psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets] + return psetnames[ifc_class] + + def getResourceQtoNames(self, context): global qtonames rprops = context.scene.BIMResourceProperties @@ -147,6 +158,7 @@ class ResourcePsetProperties(PropertyGroup): active_pset_id: IntProperty(name="Active Pset ID") active_pset_name: StringProperty(name="Pset Name") properties: CollectionProperty(name="Properties", type=Attribute) + pset_name: EnumProperty(items=getResourcePsetNames, name="Pset Name") qto_name: EnumProperty(items=getResourceQtoNames, name="Qto Name") diff --git a/src/blenderbim/blenderbim/bim/module/pset/ui.py b/src/blenderbim/blenderbim/bim/module/pset/ui.py index 75d087812a..f5613cf4c0 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/ui.py +++ b/src/blenderbim/blenderbim/bim/module/pset/ui.py @@ -304,6 +304,40 @@ class BIM_PT_resource_qtos(Panel): draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Resource") +class BIM_PT_resource_psets(Panel): + bl_label = "IFC Resource Property Sets" + bl_idname = "BIM_PT_resource_psets" + bl_options = {"DEFAULT_CLOSED"} + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + bl_parent_id = "BIM_PT_resources" + + @classmethod + def poll(cls, context): + props = context.scene.BIMResourceProperties + total_resources = len(context.scene.BIMResourceTreeProperties.resources) + if total_resources > 0 and props.active_resource_index < total_resources: + return True + return False + + def draw(self, context): + props = context.scene.ResourcePsetProperties + rprops = context.scene.BIMResourceProperties + rtprops = context.scene.BIMResourceTreeProperties + ifc_definition_id = rtprops.resources[rprops.active_resource_index].ifc_definition_id + if ifc_definition_id not in Data.products: + Data.load(IfcStore.get_file(), ifc_definition_id) + row = self.layout.row(align=True) + row.prop(props, "pset_name", text="") + op = row.operator("bim.add_pset", icon="ADD", text="") + op.obj_type = "Resource" + + psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[ifc_definition_id]["psets"]] + for pset_id, pset in sorted(psets, key=lambda v: v[1]["Name"]): + draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Resource") + + class BIM_PT_profile_psets(Panel): bl_label = "IFC Profile Property Sets" bl_idname = "BIM_PT_profile_psets" From 8c97b53bd206189b554789f0a1cbc97713d30b2f Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 25 Aug 2021 18:15:19 +1000 Subject: [PATCH 007/113] You can now store productivity rates and calculate resource work parametrically based on productivity --- .../bim/data/pset/EPset_Productivity.ifc | 13 +++++ .../bim/module/resource/__init__.py | 1 + .../bim/module/resource/operator.py | 14 +++++ .../blenderbim/bim/module/resource/ui.py | 10 +++- .../bim/module/sequence/__init__.py | 2 +- .../bim/module/sequence/operator.py | 8 ++- .../blenderbim/bim/module/sequence/ui.py | 2 +- .../api/resource/calculate_resource_work.py | 56 +++++++++++++++++++ 8 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 src/blenderbim/blenderbim/bim/data/pset/EPset_Productivity.ifc create mode 100644 src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py diff --git a/src/blenderbim/blenderbim/bim/data/pset/EPset_Productivity.ifc b/src/blenderbim/blenderbim/bim/data/pset/EPset_Productivity.ifc new file mode 100644 index 0000000000..283946eb80 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/data/pset/EPset_Productivity.ifc @@ -0,0 +1,13 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION((),'2;1'); +FILE_NAME('EPset_Productivity.ifc','2020-01-01T00:00:00',(),(),'EPset_Productivity','EPset_Productivity',$); +FILE_SCHEMA(('IFC4')); +ENDSEC; +DATA; +#1=IFCPROPERTYSETTEMPLATE('24XxTA2ED4o83uiZBG59D_',$,'EPset_Productivity','',.PSET_TYPEDRIVENOVERRIDE.,'IfcConstructionEquipmentResource,IfcLaborResource',(#2,#3,#4)); +#2=IFCSIMPLEPROPERTYTEMPLATE('3mL27FQ4X4MQn5fiGqIt8x',$,'BaseQuantityConsumed','',.P_SINGLEVALUE.,'IfcDuration',$,$,$,$,$,.READWRITE.); +#3=IFCSIMPLEPROPERTYTEMPLATE('1DdJt5_Ar2mR4UDpO$98Ws',$,'BaseQuantityProducedName','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); +#4=IFCSIMPLEPROPERTYTEMPLATE('2OZPZ$Onb2gOvW20q7gacH',$,'BaseQuantityProducedValue','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.); +ENDSEC; +END-ISO-10303-21; diff --git a/src/blenderbim/blenderbim/bim/module/resource/__init__.py b/src/blenderbim/blenderbim/bim/module/resource/__init__.py index 8794902494..cb2f056939 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/resource/__init__.py @@ -36,6 +36,7 @@ classes = ( operator.EnableEditingResourceTime, operator.EditResourceTime, operator.DisableEditingResourceTime, + operator.CalculateResourceWork, prop.Resource, prop.BIMResourceProperties, prop.BIMResourceTreeProperties, diff --git a/src/blenderbim/blenderbim/bim/module/resource/operator.py b/src/blenderbim/blenderbim/bim/module/resource/operator.py index 34b70a80f4..34dbdd12be 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/operator.py +++ b/src/blenderbim/blenderbim/bim/module/resource/operator.py @@ -371,3 +371,17 @@ class EditResourceTime(bpy.types.Operator): return True attributes[prop.name] = helper.parse_duration(prop.string_value) return True + + +class CalculateResourceWork(bpy.types.Operator): + bl_idname = "bim.calculate_resource_work" + bl_label = "Calculate Resource Work" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + + def execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run("resource.calculate_resource_work", self.file, resource=self.file.by_id(self.resource)) + Data.load(self.file) + bpy.ops.bim.load_resources() + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/resource/ui.py b/src/blenderbim/blenderbim/bim/module/resource/ui.py index 8c63c25cab..ec10d592df 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/ui.py +++ b/src/blenderbim/blenderbim/bim/module/resource/ui.py @@ -75,13 +75,21 @@ class BIM_PT_resources(Panel): and Data.resources[self.tprops.resources[self.props.active_resource_index].ifc_definition_id]["type"] != "IfcSubContractResource" ): + ifc_definition_id = self.tprops.resources[self.props.active_resource_index].ifc_definition_id + row = self.layout.row(align=True) for ifc_class, icon in icon_map.items(): label = ifc_class.replace("Ifc", "").replace("Construction", "").replace("Resource", "") op = row.operator("bim.add_resource", text=label, icon=icon) - op.resource = self.tprops.resources[self.props.active_resource_index].ifc_definition_id + op.resource = ifc_definition_id op.ifc_class = ifc_class + row = self.layout.row(align=True) + row.alignment = "RIGHT" + + op = row.operator("bim.calculate_resource_work", text="", icon="TEMP") + op.resource = ifc_definition_id + self.layout.template_list( "BIM_UL_resources", "", diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 1997e93671..2522c36759 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -98,7 +98,7 @@ classes = ( operator.LoadTaskInputs, operator.LoadTaskResources, operator.LoadTaskOutputs, - operator.DeriveTaskDuration, + operator.CalculateTaskDuration, prop.WorkPlan, prop.BIMWorkPlanProperties, prop.Task, diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index 00c0e8bfd3..b908d80bab 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -797,6 +797,7 @@ class AssignProduct(bpy.types.Operator): related_object=self.file.by_id(self.task), ) Data.load(self.file) + bpy.ops.bim.load_task_outputs() return {"FINISHED"} @@ -825,6 +826,7 @@ class UnassignProduct(bpy.types.Operator): related_object=self.file.by_id(self.task), ) Data.load(self.file) + bpy.ops.bim.load_task_outputs() return {"FINISHED"} @@ -2106,9 +2108,9 @@ class LoadTaskOutputs(bpy.types.Operator): return {"FINISHED"} -class DeriveTaskDuration(bpy.types.Operator): - bl_idname = "bim.derive_task_duration" - bl_label = "Derive Task Duration" +class CalculateTaskDuration(bpy.types.Operator): + bl_idname = "bim.calculate_task_duration" + bl_label = "Calculate Task Duration" bl_options = {"REGISTER", "UNDO"} task: bpy.props.IntProperty() diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index 42dea09f5e..37c4bd138e 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -371,7 +371,7 @@ class BIM_PT_task_icom(Panel): row2 = col.row(align=True) row2.label(text="Resources") - op = row2.operator("bim.derive_task_duration", text="", icon="TIME") + op = row2.operator("bim.calculate_task_duration", text="", icon="TEMP") op.task = task.ifc_definition_id total_resources = len(context.scene.BIMResourceTreeProperties.resources) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py new file mode 100644 index 0000000000..aa68a8fe54 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py @@ -0,0 +1,56 @@ +import math +import ifcopenshell.api +import ifcopenshell.util.date +import ifcopenshell.util.element + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"resource": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + self.productivity = ifcopenshell.util.element.get_psets(self.settings["resource"]).get( + "EPset_Productivity", None + ) + if not self.productivity: + return + unit_consumed = self.get_unit_consumed() + self.unit_produced_name = self.productivity.get("BaseQuantityProducedName", None) + unit_produced = self.productivity.get("BaseQuantityProducedValue", None) + total_produced = self.get_total_produced() + if not unit_consumed or not unit_produced or not total_produced: + return + seconds_worked = total_produced / unit_produced * unit_consumed + self.set_schedule_work(seconds_worked) + + def get_unit_consumed(self): + duration = self.productivity.get("BaseQuantityConsumed", None) + if not duration: + return + return ifcopenshell.util.date.ifc2datetime(duration).seconds + + def get_total_produced(self): + total = 0 + for rel in self.settings["resource"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToProcess"): + continue + for rel2 in rel.RelatingProcess.HasAssignments or []: + if not rel2.is_a("IfcRelAssignsToProduct"): + continue + psets = ifcopenshell.util.element.get_psets(rel2.RelatingProduct) + for pset in psets.values(): + for name, value in pset.items(): + if name == self.unit_produced_name: + try: + total += float(value) + except: + pass + return total + + def set_schedule_work(self, seconds): + if not self.settings["resource"].Usage: + ifcopenshell.api.run("resource.add_resource_time", self.file, resource=self.settings["resource"]) + self.settings["resource"].Usage.ScheduleWork = f"PT{seconds / 60 / 60}H" From 178611faa83871180f3ee52c4362a15c1c2d2e79 Mon Sep 17 00:00:00 2001 From: ArturTomczak Date: Wed, 25 Aug 2021 13:49:25 +0200 Subject: [PATCH 008/113] Finalizing Gsoc#45 ids checking (#1694) --- src/ifcopenshell-python/ifcopenshell/ids.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ifcopenshell-python/ifcopenshell/ids.py b/src/ifcopenshell-python/ifcopenshell/ids.py index 769016b600..b3ac926a79 100644 --- a/src/ifcopenshell-python/ifcopenshell/ids.py +++ b/src/ifcopenshell-python/ifcopenshell/ids.py @@ -502,8 +502,10 @@ class entity(facet): :rtype: dict """ fac_dict = {"name": parameter_asdict(self.name)} - if "predefinedtype" in self: + try: fac_dict["predefinedtype"] = parameter_asdict(self.predefinedtype) + except (RecursionError, UnboundLocalError) as e: + print(e) return fac_dict def __call__(self, inst, logger): From 0a03632e956c3d59d26d473815ea7e2255ecc9ba Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Wed, 25 Aug 2021 18:44:35 +0200 Subject: [PATCH 009/113] #1693 get_info_2() derived and select --- src/ifcwrap/IfcParseWrapper.i | 38 +++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/ifcwrap/IfcParseWrapper.i b/src/ifcwrap/IfcParseWrapper.i index 81fa7e1c6c..e36d13d56a 100644 --- a/src/ifcwrap/IfcParseWrapper.i +++ b/src/ifcwrap/IfcParseWrapper.i @@ -774,17 +774,34 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas %inline %{ PyObject* get_info_cpp(IfcUtil::IfcBaseClass* v) { PyObject *d = PyDict_New(); - const std::vector attrs = v->declaration().as_entity()->all_attributes(); - std::vector::const_iterator it = attrs.begin(); - for (; it != attrs.end(); ++it) { - const std::string& name_cpp = (*it)->name(); + + if (v->declaration().as_entity()) { + const std::vector attrs = v->declaration().as_entity()->all_attributes(); + std::vector::const_iterator it = attrs.begin(); + auto dit = v->declaration().as_entity()->derived().begin(); + for (; it != attrs.end(); ++it, ++dit) { + const std::string& name_cpp = (*it)->name(); + auto name_py = pythonize(name_cpp); + auto attr_type = *dit + ? IfcUtil::Argument_DERIVED + : IfcUtil::from_parameter_type((*it)->type_of_attribute()); + auto value_cpp = v->data().getArgument(std::distance(attrs.begin(), it)); + auto value_py = convert_cpp_attribute_to_python(attr_type, *value_cpp); + PyDict_SetItem(d, name_py, value_py); + } + + const std::string& id_cpp = "id"; + auto id_py = pythonize(id_cpp); + auto id_v_py = pythonize(v->data().id()); + PyDict_SetItem(d, id_py, id_v_py); + } else { + const std::string& name_cpp = "wrappedValue"; auto name_py = pythonize(name_cpp); - auto attr_type = IfcUtil::from_parameter_type((*it)->type_of_attribute()); - auto value_cpp = v->data().getArgument(std::distance(attrs.begin(), it)); - auto value_py = convert_cpp_attribute_to_python(attr_type, *value_cpp); + auto value_cpp = v->data().getArgument(0); + auto value_py = convert_cpp_attribute_to_python(value_cpp->type(), *value_cpp); PyDict_SetItem(d, name_py, value_py); } - + // @todo type and id can be static? const std::string& type_cpp = "type"; auto type_py = pythonize(type_cpp); @@ -792,11 +809,6 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas auto type_v_py = pythonize(type_v_cpp); PyDict_SetItem(d, type_py, type_v_py); - const std::string& id_cpp = "id"; - auto id_py = pythonize(id_cpp); - auto id_v_py = pythonize(v->data().id()); - PyDict_SetItem(d, id_py, id_v_py); - return d; } %} From 6e3484fd466fb66d130a3426efa24660d70afc7d Mon Sep 17 00:00:00 2001 From: Gorgious56 Date: Thu, 26 Aug 2021 01:06:27 +0200 Subject: [PATCH 010/113] Fix https://github.com/IfcOpenShell/IfcOpenShell/issues/1695 : (#1697) Removing material from IfcMaterialList removes all occurences of the material --- src/blenderbim/blenderbim/bim/module/material/operator.py | 3 ++- src/blenderbim/blenderbim/bim/module/material/ui.py | 2 ++ .../ifcopenshell/api/material/remove_list_item.py | 5 +++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/material/operator.py b/src/blenderbim/blenderbim/bim/module/material/operator.py index e735461ef8..dfea986a57 100644 --- a/src/blenderbim/blenderbim/bim/module/material/operator.py +++ b/src/blenderbim/blenderbim/bim/module/material/operator.py @@ -365,6 +365,7 @@ class RemoveListItem(bpy.types.Operator): obj: bpy.props.StringProperty() list_item_set: bpy.props.IntProperty() list_item: bpy.props.IntProperty() + list_item_index: bpy.props.IntProperty() def execute(self, context): return IfcStore.execute_ifc_operator(self, context) @@ -377,7 +378,7 @@ class RemoveListItem(bpy.types.Operator): self.file, **{ "material_list": self.file.by_id(self.list_item_set), - "material": self.file.by_id(self.list_item), + "material_index": self.list_item_index, }, ) Data.load_lists() diff --git a/src/blenderbim/blenderbim/bim/module/material/ui.py b/src/blenderbim/blenderbim/bim/module/material/ui.py index f5630e2aec..b0101ae014 100644 --- a/src/blenderbim/blenderbim/bim/module/material/ui.py +++ b/src/blenderbim/blenderbim/bim/module/material/ui.py @@ -258,6 +258,8 @@ class BIM_PT_object_material(Panel): if self.product_data["type"] == "IfcMaterialList": setattr(op, "list_item_set", self.material_set_id) setattr(op, self.set_item_name, item["id"]) + if hasattr(op, f"{self.set_item_name}_index"): + setattr(op, f"{self.set_item_name}_index", index) def draw_read_only_set_ui(self): if ( diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py index 6cafec8420..bbde25bd5d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py @@ -4,10 +4,11 @@ import ifcopenshell class Usecase: def __init__(self, file, **settings): self.file = file - self.settings = {"material_list": None, "material": None} + self.settings = {"material_list": None, "material_index": None} for key, value in settings.items(): self.settings[key] = value def execute(self): - materials = [m for m in self.settings["material_list"].Materials if m != self.settings["material"]] + materials = list(self.settings["material_list"].Materials) + materials.pop(self.settings["material_index"]) self.settings["material_list"].Materials = materials From 2cfa1d8bc06a339079f95f760bd9a878d385a761 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 26 Aug 2021 11:54:03 +1000 Subject: [PATCH 011/113] Only equipment and labour resources can now have time-based usages --- .../blenderbim/bim/module/resource/ui.py | 106 +++++++++--------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/resource/ui.py b/src/blenderbim/blenderbim/bim/module/resource/ui.py index ec10d592df..c9e383d628 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/ui.py +++ b/src/blenderbim/blenderbim/bim/module/resource/ui.py @@ -52,44 +52,7 @@ class BIM_PT_resources(Panel): if not self.props.is_editing: return - row = self.layout.row(align=True) - op = row.operator("bim.add_resource", text="Add SubContract", icon="TEXT") - op.ifc_class = "IfcSubContractResource" - op.resource = 0 - op = row.operator("bim.add_resource", text="Add Crew", icon="COMMUNITY") - op.ifc_class = "IfcCrewResource" - op.resource = 0 - - icon_map = { - "IfcSubContractResource": "TEXT", - "IfcConstructionEquipmentResource": "TOOL_SETTINGS", - "IfcLaborResource": "OUTLINER_OB_ARMATURE", - "IfcConstructionMaterialResource": "MATERIAL", - "IfcConstructionProductResource": "PACKAGE", - } - - total_resources = len(self.tprops.resources) - if ( - total_resources - and self.props.active_resource_index < total_resources - and Data.resources[self.tprops.resources[self.props.active_resource_index].ifc_definition_id]["type"] - != "IfcSubContractResource" - ): - ifc_definition_id = self.tprops.resources[self.props.active_resource_index].ifc_definition_id - - row = self.layout.row(align=True) - for ifc_class, icon in icon_map.items(): - label = ifc_class.replace("Ifc", "").replace("Construction", "").replace("Resource", "") - op = row.operator("bim.add_resource", text=label, icon=icon) - op.resource = ifc_definition_id - op.ifc_class = ifc_class - - row = self.layout.row(align=True) - row.alignment = "RIGHT" - - op = row.operator("bim.calculate_resource_work", text="", icon="TEMP") - op.resource = ifc_definition_id - + self.draw_resource_operators() self.layout.template_list( "BIM_UL_resources", "", @@ -104,6 +67,57 @@ class BIM_PT_resources(Panel): elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE": self.draw_editable_resource_time_attributes_ui() + def draw_resource_operators(self): + row = self.layout.row(align=True) + op = row.operator("bim.add_resource", text="Add SubContract", icon="TEXT") + op.ifc_class = "IfcSubContractResource" + op.resource = 0 + op = row.operator("bim.add_resource", text="Add Crew", icon="COMMUNITY") + op.ifc_class = "IfcCrewResource" + op.resource = 0 + + total_resources = len(self.tprops.resources) + if not total_resources or self.props.active_resource_index >= total_resources: + return + + ifc_definition_id = self.tprops.resources[self.props.active_resource_index].ifc_definition_id + resource = Data.resources[ifc_definition_id] + + if resource["type"] != "IfcSubContractResource": + icon_map = { + "IfcSubContractResource": "TEXT", + "IfcConstructionEquipmentResource": "TOOL_SETTINGS", + "IfcLaborResource": "OUTLINER_OB_ARMATURE", + "IfcConstructionMaterialResource": "MATERIAL", + "IfcConstructionProductResource": "PACKAGE", + } + row = self.layout.row(align=True) + for ifc_class, icon in icon_map.items(): + label = ifc_class.replace("Ifc", "").replace("Construction", "").replace("Resource", "") + op = row.operator("bim.add_resource", text=label, icon=icon) + op.resource = ifc_definition_id + op.ifc_class = ifc_class + + row = self.layout.row(align=True) + row.alignment = "RIGHT" + + if self.props.active_resource_id == ifc_definition_id and self.props.editing_resource_type == "ATTRIBUTES": + row.operator("bim.edit_resource", text="", icon="CHECKMARK") + row.operator("bim.disable_editing_resource", text="", icon="CANCEL") + elif self.props.active_resource_id == ifc_definition_id and self.props.editing_resource_type == "USAGE": + row.operator("bim.edit_resource_time", text="", icon="CHECKMARK") + row.operator("bim.disable_editing_resource_time", text="", icon="CANCEL") + elif self.props.active_resource_id: + row.operator("bim.add_resource", text="", icon="ADD").resource = ifc_definition_id + row.operator("bim.remove_resource", text="", icon="X").resource = ifc_definition_id + else: + if resource["type"] in ["IfcLaborResource", "IfcConstructionEquipmentResource"]: + op = row.operator("bim.calculate_resource_work", text="", icon="TEMP") + op.resource = ifc_definition_id + row.operator("bim.enable_editing_resource_time", text="", icon="TIME").resource = ifc_definition_id + row.operator("bim.enable_editing_resource", text="", icon="GREASEPENCIL").resource = ifc_definition_id + row.operator("bim.remove_resource", text="", icon="X").resource = ifc_definition_id + def draw_editable_resource_attributes_ui(self): blenderbim.bim.helper.draw_attributes(self.props.resource_attributes, self.layout) @@ -149,19 +163,3 @@ class BIM_UL_resources(UIList): else: op = row.operator("bim.assign_resource", text="", icon="KEYFRAME", emboss=False) op.resource = item.ifc_definition_id - - if props.active_resource_id == item.ifc_definition_id and props.editing_resource_type == "ATTRIBUTES": - row.operator("bim.edit_resource", text="", icon="CHECKMARK") - row.operator("bim.disable_editing_resource", text="", icon="CANCEL") - elif props.active_resource_id == item.ifc_definition_id and props.editing_resource_type == "USAGE": - row.operator("bim.edit_resource_time", text="", icon="CHECKMARK") - row.operator("bim.disable_editing_resource_time", text="", icon="CANCEL") - elif props.active_resource_id: - row.operator("bim.add_resource", text="", icon="ADD").resource = item.ifc_definition_id - row.operator("bim.remove_resource", text="", icon="X").resource = item.ifc_definition_id - else: - row.operator( - "bim.enable_editing_resource", text="", icon="GREASEPENCIL" - ).resource = item.ifc_definition_id - row.operator("bim.enable_editing_resource_time", text="", icon="TIME").resource = item.ifc_definition_id - row.operator("bim.remove_resource", text="", icon="X").resource = item.ifc_definition_id From 00f779fc498f640e5c56c231eb16e29d71b2906f Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 26 Aug 2021 15:48:04 +1000 Subject: [PATCH 012/113] You can now count elements, tasks, and resources parametrically for cost items --- src/blenderbim/blenderbim/bim/ifc.py | 2 +- .../blenderbim/bim/module/cost/operator.py | 12 +-- .../blenderbim/bim/module/cost/prop.py | 24 +++--- .../blenderbim/bim/module/cost/ui.py | 84 +++++++++++++------ .../api/cost/add_cost_item_quantity.py | 4 +- .../api/cost/assign_cost_item_quantity.py | 29 ++++++- .../api/cost/unassign_cost_item_quantity.py | 13 ++- 7 files changed, 119 insertions(+), 49 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/ifc.py b/src/blenderbim/blenderbim/bim/ifc.py index e779befc27..c5d9f65d78 100644 --- a/src/blenderbim/blenderbim/bim/ifc.py +++ b/src/blenderbim/blenderbim/bim/ifc.py @@ -183,7 +183,7 @@ class IfcStore: def commit_link_element(data): obj = bpy.data.objects.get(data["obj"]) IfcStore.id_map[data["id"]] = obj - if data["guid"]: + if "guid" in data: IfcStore.guid_map[data["guid"]] = obj blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback) blenderbim.bim.handler.subscribe_to(obj, "name", blenderbim.bim.handler.name_callback) diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index 96bd5cd595..a23f8caa23 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -637,10 +637,10 @@ class EnableEditingCostItemValue(bpy.types.Operator): data = Data.cost_values[self.cost_value] blenderbim.bim.helper.import_attributes( - data["type"], - self.props.cost_value_attributes, - data, - lambda name, prop, data: self.import_attributes(name, prop, data, context) + data["type"], + self.props.cost_value_attributes, + data, + lambda name, prop, data: self.import_attributes(name, prop, data, context), ) return {"FINISHED"} @@ -715,8 +715,8 @@ class EditCostValue(bpy.types.Operator): def _execute(self, context): props = context.scene.BIMCostProperties attributes = blenderbim.bim.helper.export_attributes( - props.cost_value_attributes, - lambda attributes, prop: self.export_attributes(attributes, prop, context)) + props.cost_value_attributes, lambda attributes, prop: self.export_attributes(attributes, prop, context) + ) self.file = IfcStore.get_file() ifcopenshell.api.run( "cost.edit_cost_value", diff --git a/src/blenderbim/blenderbim/bim/module/cost/prop.py b/src/blenderbim/blenderbim/bim/module/cost/prop.py index 94d05993b2..6530781763 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/prop.py +++ b/src/blenderbim/blenderbim/bim/module/cost/prop.py @@ -79,7 +79,7 @@ def update_schedule_of_rates(self, context): bpy.ops.bim.load_schedule_of_rates(cost_schedule=int(self.schedule_of_rates)) -def getQuantityTypes(self, context): +def get_quantity_types(self, context): global quantitytypes_enum if len(quantitytypes_enum) == 0 and IfcStore.get_schema(): quantitytypes_enum.extend( @@ -91,7 +91,7 @@ def getQuantityTypes(self, context): return quantitytypes_enum -def getProductQuantityNames(self, context): +def get_product_quantity_names(self, context): global productquantitynames_enum global productquantitynames_count ifc_file = IfcStore.get_file() @@ -115,7 +115,7 @@ def getProductQuantityNames(self, context): return productquantitynames_enum -def getProcessQuantityNames(self, context): +def get_process_quantity_names(self, context): global processquantitynames_enum global processquantitynames_id ifc_file = IfcStore.get_file() @@ -137,7 +137,7 @@ def getProcessQuantityNames(self, context): return processquantitynames_enum -def getResourceQuantityNames(self, context): +def get_resource_quantity_names(self, context): global resourcequantitynames_enum global resourcequantitynames_id ifc_file = IfcStore.get_file() @@ -166,7 +166,7 @@ def update_active_cost_item_index(self, context): bpy.ops.bim.load_cost_item_quantities() -def updateCostItemIdentification(self, context): +def update_cost_item_identification(self, context): props = context.scene.BIMCostProperties if not props.is_cost_update_enabled or self.identification == "XXX": return @@ -182,7 +182,7 @@ def updateCostItemIdentification(self, context): attribute.string_value = self.identification -def updateCostItemName(self, context): +def update_cost_item_name(self, context): props = context.scene.BIMCostProperties if not props.is_cost_update_enabled or self.name == "Unnamed": return @@ -199,8 +199,8 @@ def updateCostItemName(self, context): class CostItem(PropertyGroup): - name: StringProperty(name="Name", update=updateCostItemName) - identification: StringProperty(name="Identification", update=updateCostItemIdentification) + name: StringProperty(name="Name", update=update_cost_item_name) + identification: StringProperty(name="Identification", update=update_cost_item_identification) ifc_definition_id: IntProperty(name="IFC Definition ID") has_children: BoolProperty(name="Has Children") is_expanded: BoolProperty(name="Is Expanded") @@ -229,10 +229,10 @@ class BIMCostProperties(PropertyGroup): active_cost_item_index: IntProperty(name="Active Cost Item Index", update=update_active_cost_item_index) cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute) contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]") - quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types") - product_quantity_names: EnumProperty(items=getProductQuantityNames, name="Product Quantity Names") - process_quantity_names: EnumProperty(items=getProcessQuantityNames, name="Process Quantity Names") - resource_quantity_names: EnumProperty(items=getResourceQuantityNames, name="Resource Quantity Names") + quantity_types: EnumProperty(items=get_quantity_types, name="Quantity Types") + product_quantity_names: EnumProperty(items=get_product_quantity_names, name="Product Quantity Names") + process_quantity_names: EnumProperty(items=get_process_quantity_names, name="Process Quantity Names") + resource_quantity_names: EnumProperty(items=get_resource_quantity_names, name="Resource Quantity Names") active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id") quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute) cost_types: EnumProperty( diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index 12d5deaecc..18ea7cd3f6 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -373,11 +373,29 @@ class BIM_PT_cost_item_quantities(Panel): # Column1 col = grid.column() + has_quantity_names = CostProp.get_product_quantity_names(self, context) + row2 = col.row(align=True) row2.label(text="Elements") op = row2.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="") op.cost_item = cost_item.ifc_definition_id + if context.selected_objects: + if has_quantity_names: + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES") + op.related_object_type = "PRODUCT" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = self.props.product_quantity_names + + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") + op.related_object_type = "PRODUCT" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = "" + + op = row2.operator("bim.unassign_cost_item_quantity", text="", icon="REMOVE") + op.cost_item = cost_item.ifc_definition_id + op.related_object = 0 + row2 = col.row() row2.template_list( "BIM_UL_cost_item_quantities", @@ -388,23 +406,32 @@ class BIM_PT_cost_item_quantities(Panel): "active_cost_item_product_index", ) - row2 = col.row(align=True) - row2.prop(self.props, "product_quantity_names", text="") - op = row2.operator("bim.unassign_cost_item_quantity", text="", icon="REMOVE") - op.cost_item = cost_item.ifc_definition_id - op.related_object = 0 - if CostProp.productquantitynames_enum: - op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") - op.related_object_type = "PRODUCT" - op.cost_item = cost_item.ifc_definition_id - op.prop_name = self.props.product_quantity_names + if has_quantity_names: + row2 = col.row() + row2.prop(self.props, "product_quantity_names", text="") # Column2 col = grid.column() + has_quantity_names = CostProp.get_process_quantity_names(self, context) + row2 = col.row(align=True) row2.label(text="Tasks") + tprops = context.scene.BIMTaskTreeProperties + wprops = context.scene.BIMWorkScheduleProperties + if tprops.tasks and wprops.active_task_index < len(tprops.tasks): + if has_quantity_names: + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES") + op.related_object_type = "PROCESS" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = self.props.process_quantity_names + + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") + op.related_object_type = "PROCESS" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = "" + row2 = col.row() row2.template_list( "BIM_UL_cost_item_quantities", @@ -415,20 +442,32 @@ class BIM_PT_cost_item_quantities(Panel): "active_cost_item_process_index", ) - row2 = col.row(align=True) - row2.prop(self.props, "process_quantity_names", text="") - if CostProp.processquantitynames_enum: - op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") - op.related_object_type = "PROCESS" - op.cost_item = cost_item.ifc_definition_id - op.prop_name = self.props.process_quantity_names + if has_quantity_names: + row2 = col.row() + row2.prop(self.props, "process_quantity_names", text="") # Column3 col = grid.column() + has_quantity_names = CostProp.get_resource_quantity_names(self, context) + row2 = col.row(align=True) row2.label(text="Resources") + rtprops = context.scene.BIMResourceTreeProperties + rprops = context.scene.BIMResourceProperties + if rtprops.resources and rprops.active_resource_index < len(rtprops.resources): + if has_quantity_names: + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES") + op.related_object_type = "RESOURCE" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = self.props.resource_quantity_names + + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") + op.related_object_type = "RESOURCE" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = "" + row2 = col.row() row2.template_list( "BIM_UL_cost_item_quantities", @@ -439,14 +478,9 @@ class BIM_PT_cost_item_quantities(Panel): "active_cost_item_resource_index", ) - row2 = col.row(align=True) - row2.prop(self.props, "resource_quantity_names", text="") - if CostProp.resourcequantitynames_enum: - op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") - op.related_object_type = "RESOURCE" - op.cost_item = cost_item.ifc_definition_id - op.prop_name = self.props.resource_quantity_names - + if has_quantity_names: + row2 = col.row() + row2.prop(self.props, "resource_quantity_names", text="") class BIM_PT_cost_item_rates(Panel): bl_label = "IFC Cost Item Rates" diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py index dff2d825a0..744409687d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py @@ -14,8 +14,10 @@ class Usecase: # This is a bold assumption # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564 if self.settings["ifc_class"] == "IfcQuantityCount" and self.settings["cost_item"].Controls: + count = 0 for rel in self.settings["cost_item"].Controls: - quantity[3] += len(rel.RelatedObjects) + count += len(rel.RelatedObjects) + quantity[3] = count quantities = list(self.settings["cost_item"].CostQuantities or []) quantities.append(quantity) self.settings["cost_item"].CostQuantities = quantities diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py index 21f4447383..41518aa011 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py @@ -9,7 +9,8 @@ class Usecase: self.settings[key] = value def execute(self): - self.quantities = set(self.settings["cost_item"].CostQuantities or []) + if self.settings["prop_name"]: + self.quantities = set(self.settings["cost_item"].CostQuantities or []) for product in self.settings["products"]: ifcopenshell.api.run( "control.assign_control", @@ -17,8 +18,12 @@ class Usecase: related_object=product, relating_control=self.settings["cost_item"], ) - self.add_quantity_from_related_object(product) - self.settings["cost_item"].CostQuantities = list(self.quantities) + if self.settings["prop_name"]: + self.add_quantity_from_related_object(product) + if self.settings["prop_name"]: + self.settings["cost_item"].CostQuantities = list(self.quantities) + else: + self.update_cost_item_count() def add_quantity_from_related_object(self, element): if not element.is_a("IfcObject"): @@ -33,3 +38,21 @@ class Usecase: for prop in qto.Quantities: if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower(): self.quantities.add(prop) + + def update_cost_item_count(self): + # This is a bold assumption + # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564 + if not self.settings["cost_item"].CostQuantities: + return ifcopenshell.api.run( + "cost.add_cost_item_quantity", + self.file, + cost_item=self.settings["cost_item"], + ifc_class="IfcQuantityCount", + ) + if len(self.settings["cost_item"].CostQuantities) == 1: + quantity = self.settings["cost_item"].CostQuantities[0] + if quantity.is_a("IfcQuantityCount"): + count = 0 + for rel in self.settings["cost_item"].Controls: + count += len(rel.RelatedObjects) + quantity[3] = count diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py index ec652a0e10..ed7b555d77 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py @@ -19,7 +19,6 @@ class Usecase: if related_object in self.settings["products"]: self.quantities.remove(quantity) self.settings["cost_item"].CostQuantities = list(self.quantities) - for product in self.settings["products"]: ifcopenshell.api.run( "control.unassign_control", @@ -27,3 +26,15 @@ class Usecase: related_object=product, relating_control=self.settings["cost_item"], ) + self.update_cost_item_count() + + def update_cost_item_count(self): + # This is a bold assumption + # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564 + if len(self.settings["cost_item"].CostQuantities) == 1: + quantity = self.settings["cost_item"].CostQuantities[0] + if quantity.is_a("IfcQuantityCount"): + count = 0 + for rel in self.settings["cost_item"].Controls: + count += len(rel.RelatedObjects) + quantity[3] = count From 1ae7bd3a9afdf59c89cafe14595949b2f257957c Mon Sep 17 00:00:00 2001 From: Boris Brangeon Date: Thu, 26 Aug 2021 06:12:09 +0200 Subject: [PATCH 013/113] add (in() IfcGeomShapes --- src/ifcgeom/IfcGeomShapes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp index 18711fca83..4f1e8b0519 100644 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ b/src/ifcgeom/IfcGeomShapes.cpp @@ -1368,7 +1368,7 @@ namespace { // @todo this probably still does not work on a closed wire consisting of one (circular) edge. - while (sorted_edges.size() < num_edges && + while ((int) sorted_edges.size() < num_edges && (!v0.IsSame(v1) || ignore_first_equality_because_closed)) { ignore_first_equality_because_closed = false; From 6aea3361c186fae94db302c4f001303243c505b8 Mon Sep 17 00:00:00 2001 From: krande Date: Tue, 17 Aug 2021 16:03:57 +0200 Subject: [PATCH 014/113] Add proposal for CI workflow on PR and commits --- .github/workflows/cd.yml | 107 +++++++++++++++++++++++++++ .github/workflows/ci.yml | 152 ++++++++++++++++----------------------- 2 files changed, 169 insertions(+), 90 deletions(-) create mode 100644 .github/workflows/cd.yml diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000000..8de307ad0e --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,107 @@ +name: CD + +on: + push: + + +jobs: + + activate: + runs-on: ubuntu-latest + if: | + github.repository == 'IfcOpenShell/IfcOpenShell' && + !startsWith(github.event.head_commit.message, 'Release ') && + !contains(github.event.head_commit.message, 'ci skip') + steps: + - run: echo ok go + + build: + runs-on: ubuntu-20.04 + needs: activate + steps: + - uses: actions/checkout@v2 + + - name: Install dependencies + run: | + sudo apt update + sudo apt-get install --no-install-recommends \ + git cmake gcc g++ libboost1.67-all-dev python3-all-dev swig libpcre3-dev libxml2-dev \ + liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-dev + + - + name: ccache + uses: hendrikmuhs/ccache-action@v1 + + - + name: Build ifcopenshell + run: | + mkdir build && cd build + cmake \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DCMAKE_INSTALL_PREFIX=$PWD/install/ \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH=/usr \ + -DCMAKE_SYSTEM_PREFIX_PATH=/usr \ + -DBUILD_PACKAGE=On \ + -DOCC_INCLUDE_DIR=/usr/include/oce \ + -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \ + -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \ + -DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \ + -DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \ + -DCOLLADA_SUPPORT=Off \ + -DLIBXML2_INCLUDE_DIR=/usr/include/libxml2 \ + -DLIBXML2_LIBRARIES=/usr/lib/x86_64-linux-gnu/libxml2.so \ + ../cmake + make -j $(nproc) + make install + - + name: Package + run: | + make package + working-directory: build + - name: Upload + uses: actions/upload-artifact@v2 + with: + # Artifact name + name: ifcos-artifacts + # Directory containing files to upload + path: build/assets/Ifc* + + deliver: + runs-on: ubuntu-20.04 + needs: build + name: Docker Build, Tag, Push + + steps: + - uses: actions/checkout@v2 + with: + lfs: true + + - name: Download + uses: actions/download-artifact@v2 + with: + # Artifact name + name: ifcos-artifacts + path: artifacts/ + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to Dockerhub + uses: docker/login-action@v1 + with: + username: aecgeeks + password: ${{ secrets.DOCKER_HUB_TOKEN }} + - + name: Build container image + uses: docker/build-push-action@v2 + with: + context: artifacts + repository: aecgeeks/ifcopenshell + tags: aecgeeks/ifcopenshell:latest + file: ./Dockerfile + push: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 012146494d..668233032e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,107 +1,79 @@ -name: CI +name: ci -on: +on: push: - + paths: + - 'src/**' + - 'test/**' + - 'conda/**' + - 'cmake/**' + - '.github/workflows/ci.yml' + pull_request: jobs: - activate: runs-on: ubuntu-latest if: | github.repository == 'IfcOpenShell/IfcOpenShell' && - !startsWith(github.event.head_commit.message, 'Release ') && - !contains(github.event.head_commit.message, 'ci skip') + !contains(github.event.head_commit.message, 'skip ci') steps: - - run: echo ok go + - run: echo ok go build: runs-on: ubuntu-20.04 needs: activate steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2 + with: + submodules: recursive + - name: Install dependencies + run: | + sudo apt update + sudo apt-get install --no-install-recommends \ + git cmake gcc g++ \ + libboost-date-time-dev \ + libboost-filesystem-dev \ + libboost-iostreams-dev \ + libboost-program-options-dev \ + libboost-regex-dev \ + libboost-system-dev \ + libboost-thread-dev \ + python3-all-dev python3-pip \ + swig libpcre3-dev libxml2-dev \ + libtbb-dev nlohmann-json3-dev \ + liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-dev + - name: ccache + uses: hendrikmuhs/ccache-action@v1 - - name: Install dependencies - run: | - sudo apt update - sudo apt-get install --no-install-recommends \ - git cmake gcc g++ libboost1.67-all-dev python3-all-dev swig libpcre3-dev libxml2-dev \ - liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-dev + - name: Build ifcopenshell + run: | + mkdir build && cd build + cmake \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DCMAKE_INSTALL_PREFIX=$PWD/install/ \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH=/usr \ + -DCMAKE_SYSTEM_PREFIX_PATH=/usr \ + -DOCC_INCLUDE_DIR=/usr/include/oce \ + -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \ + -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \ + -DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \ + -DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \ + -DCOLLADA_SUPPORT=Off \ + "-DSCHEMA_VERSIONS=2x3;4" \ + -DGLTF_SUPPORT=On \ + -DJSON_INCLUDE_DIR=/usr/include \ + ../cmake + sudo make -j $(nproc) + sudo make install - - - name: ccache - uses: hendrikmuhs/ccache-action@v1 + - name: install xmlschema + run: | + sudo /usr/bin/python -m pip install -U pip + sudo /usr/bin/python -m pip install xmlschema - - - name: Build ifcopenshell - run: | - mkdir build && cd build - cmake \ - -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ - -DCMAKE_INSTALL_PREFIX=$PWD/install/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_PREFIX_PATH=/usr \ - -DCMAKE_SYSTEM_PREFIX_PATH=/usr \ - -DBUILD_PACKAGE=On \ - -DOCC_INCLUDE_DIR=/usr/include/oce \ - -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \ - -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \ - -DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \ - -DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \ - -DCOLLADA_SUPPORT=Off \ - -DLIBXML2_INCLUDE_DIR=/usr/include/libxml2 \ - -DLIBXML2_LIBRARIES=/usr/lib/x86_64-linux-gnu/libxml2.so \ - ../cmake - make -j $(nproc) - make install - - - name: Package - run: | - make package - working-directory: build - - name: Upload - uses: actions/upload-artifact@v2 - with: - # Artifact name - name: ifcos-artifacts - # Directory containing files to upload - path: build/assets/Ifc* - - deliver: - runs-on: ubuntu-20.04 - needs: build - name: Docker Build, Tag, Push - - steps: - - uses: actions/checkout@v2 - with: - lfs: true - - - name: Download - uses: actions/download-artifact@v2 - with: - # Artifact name - name: ifcos-artifacts - path: artifacts/ - - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - - name: Login to Dockerhub - uses: docker/login-action@v1 - with: - username: aecgeeks - password: ${{ secrets.DOCKER_HUB_TOKEN }} - - - name: Build container image - uses: docker/build-push-action@v2 - with: - context: artifacts - repository: aecgeeks/ifcopenshell - tags: aecgeeks/ifcopenshell:latest - file: ./Dockerfile - push: true + - name: Test + run: | + sudo /usr/bin/python test/tests.py + sudo /usr/bin/python src/ifcopenshell-python/ifcopenshell/test_ids.py \ No newline at end of file From cb0c1247453e134e70380a52cb4e200bb2255234 Mon Sep 17 00:00:00 2001 From: krande Date: Tue, 17 Aug 2021 16:44:23 +0200 Subject: [PATCH 015/113] cd into test folder before running tests --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 668233032e..27d36a1a3e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,5 +75,6 @@ jobs: - name: Test run: | + cd test sudo /usr/bin/python test/tests.py sudo /usr/bin/python src/ifcopenshell-python/ifcopenshell/test_ids.py \ No newline at end of file From a60c5367eebfdd297d28cf3bfb3bf38783bb7510 Mon Sep 17 00:00:00 2001 From: Kristoffer Andersen Date: Tue, 17 Aug 2021 20:41:12 +0200 Subject: [PATCH 016/113] Update rel path --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27d36a1a3e..e87f1a223f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,5 +76,5 @@ jobs: - name: Test run: | cd test - sudo /usr/bin/python test/tests.py - sudo /usr/bin/python src/ifcopenshell-python/ifcopenshell/test_ids.py \ No newline at end of file + sudo /usr/bin/python tests.py + sudo /usr/bin/python ../src/ifcopenshell-python/ifcopenshell/test_ids.py From 9c81418492bc928b2be55cd61de6e7b68f2ab9a5 Mon Sep 17 00:00:00 2001 From: krande Date: Tue, 24 Aug 2021 14:05:06 +0200 Subject: [PATCH 017/113] Add numpy as a dependency --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e87f1a223f..12a13a0514 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,7 +71,7 @@ jobs: - name: install xmlschema run: | sudo /usr/bin/python -m pip install -U pip - sudo /usr/bin/python -m pip install xmlschema + sudo /usr/bin/python -m pip install xmlschema numpy - name: Test run: | From d4ace423a3fc473d11344d51a20f06d285f23f35 Mon Sep 17 00:00:00 2001 From: krande Date: Wed, 25 Aug 2021 08:41:08 +0200 Subject: [PATCH 018/113] Prep BCF for pypi upload. * Refactored the BCF dir into a "src-layout" which I am more comfortable with. We can likely change this back if it is not compatible with other development * I added a few setup files for the bcf package (setup.cfg, pyproject.toml and a MANIFEST.in file) * Added two __init__.py files in the v2 and v3 subfolders for them to be picked up during package compilation --- .github/workflows/ci.yml | 3 +- src/bcf/MANIFEST.in | 2 + src/bcf/pyproject.toml | 12 ++++++ src/bcf/setup.cfg | 37 +++++++++++++++++++ src/bcf/{ => src}/bcf/__init__.py | 0 src/bcf/{ => src}/bcf/bcfxml.py | 0 src/bcf/src/bcf/v2/__init__.py | 0 src/bcf/{ => src}/bcf/v2/bcfxml.py | 0 src/bcf/{ => src}/bcf/v2/data.py | 0 src/bcf/{ => src}/bcf/v2/xsd/markup.xsd | 0 src/bcf/{ => src}/bcf/v2/xsd/project.xsd | 0 src/bcf/{ => src}/bcf/v2/xsd/version.xsd | 0 src/bcf/{ => src}/bcf/v2/xsd/visinfo.xsd | 0 src/bcf/src/bcf/v3/__init__.py | 0 src/bcf/{ => src}/bcf/v3/bcfapi.py | 0 src/bcf/{ => src}/bcf/v3/bcfxml.py | 0 src/bcf/{ => src}/bcf/v3/data.py | 0 src/bcf/{ => src}/bcf/v3/xsd/documents.xsd | 0 src/bcf/{ => src}/bcf/v3/xsd/extensions.xsd | 0 src/bcf/{ => src}/bcf/v3/xsd/markup.xsd | 0 src/bcf/{ => src}/bcf/v3/xsd/project.xsd | 0 src/bcf/{ => src}/bcf/v3/xsd/shared-types.xsd | 0 src/bcf/{ => src}/bcf/v3/xsd/version.xsd | 0 src/bcf/{ => src}/bcf/v3/xsd/visinfo.xsd | 0 24 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 src/bcf/MANIFEST.in create mode 100644 src/bcf/pyproject.toml create mode 100644 src/bcf/setup.cfg rename src/bcf/{ => src}/bcf/__init__.py (100%) rename src/bcf/{ => src}/bcf/bcfxml.py (100%) create mode 100644 src/bcf/src/bcf/v2/__init__.py rename src/bcf/{ => src}/bcf/v2/bcfxml.py (100%) rename src/bcf/{ => src}/bcf/v2/data.py (100%) rename src/bcf/{ => src}/bcf/v2/xsd/markup.xsd (100%) rename src/bcf/{ => src}/bcf/v2/xsd/project.xsd (100%) rename src/bcf/{ => src}/bcf/v2/xsd/version.xsd (100%) rename src/bcf/{ => src}/bcf/v2/xsd/visinfo.xsd (100%) create mode 100644 src/bcf/src/bcf/v3/__init__.py rename src/bcf/{ => src}/bcf/v3/bcfapi.py (100%) rename src/bcf/{ => src}/bcf/v3/bcfxml.py (100%) rename src/bcf/{ => src}/bcf/v3/data.py (100%) rename src/bcf/{ => src}/bcf/v3/xsd/documents.xsd (100%) rename src/bcf/{ => src}/bcf/v3/xsd/extensions.xsd (100%) rename src/bcf/{ => src}/bcf/v3/xsd/markup.xsd (100%) rename src/bcf/{ => src}/bcf/v3/xsd/project.xsd (100%) rename src/bcf/{ => src}/bcf/v3/xsd/shared-types.xsd (100%) rename src/bcf/{ => src}/bcf/v3/xsd/version.xsd (100%) rename src/bcf/{ => src}/bcf/v3/xsd/visinfo.xsd (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12a13a0514..8c0191cd62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,10 +68,11 @@ jobs: sudo make -j $(nproc) sudo make install - - name: install xmlschema + - name: install dependencies run: | sudo /usr/bin/python -m pip install -U pip sudo /usr/bin/python -m pip install xmlschema numpy + sudo /usr/bin/python -m pip install src/bcf - name: Test run: | diff --git a/src/bcf/MANIFEST.in b/src/bcf/MANIFEST.in new file mode 100644 index 0000000000..30feaf47bc --- /dev/null +++ b/src/bcf/MANIFEST.in @@ -0,0 +1,2 @@ +include src/bcf/v2/xsd/*.xsd +include src/bcf/v3/xsd/*.xsd \ No newline at end of file diff --git a/src/bcf/pyproject.toml b/src/bcf/pyproject.toml new file mode 100644 index 0000000000..dadda3ed02 --- /dev/null +++ b/src/bcf/pyproject.toml @@ -0,0 +1,12 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel" +] +build-backend = "setuptools.build_meta" + +[tool.black] +line-length = 120 + +[tool.isort] +profile = "black" \ No newline at end of file diff --git a/src/bcf/setup.cfg b/src/bcf/setup.cfg new file mode 100644 index 0000000000..3eba915750 --- /dev/null +++ b/src/bcf/setup.cfg @@ -0,0 +1,37 @@ +[metadata] +name=bcf-client +version=0.0.1 +author = Ifcopenshell +description = A simple Python implementation of BCF +url = https://github.com/IfcOpenShell/IfcOpenShell +project_urls = + Code=https://github.com/IfcOpenShell/IfcOpenShell + Issues=https://github.com/IfcOpenShell/IfcOpenShell/issues +long_description = file: README.md +long_description_content_type = text/markdown +classifiers = + License :: OSI Approved :: GNU General Public License v3 (GPLv3) + Operating System :: OS Independent + Programming Language :: Python :: 3 + Topic :: Scientific/Engineering + Topic :: Utilities +keywords = + Python + file formats + engineering + +[options] +package_dir = + = src +packages = find: +python_requires = >=3 +install_requires= + xmlschema +include_package_data = True + +[options.packages.find] +where = src + +[flake8] +max-line-length = 120 +ignore = E24, E121, E123, E126, E203, E226, E704, E741, W503, W504 diff --git a/src/bcf/bcf/__init__.py b/src/bcf/src/bcf/__init__.py similarity index 100% rename from src/bcf/bcf/__init__.py rename to src/bcf/src/bcf/__init__.py diff --git a/src/bcf/bcf/bcfxml.py b/src/bcf/src/bcf/bcfxml.py similarity index 100% rename from src/bcf/bcf/bcfxml.py rename to src/bcf/src/bcf/bcfxml.py diff --git a/src/bcf/src/bcf/v2/__init__.py b/src/bcf/src/bcf/v2/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/bcf/bcf/v2/bcfxml.py b/src/bcf/src/bcf/v2/bcfxml.py similarity index 100% rename from src/bcf/bcf/v2/bcfxml.py rename to src/bcf/src/bcf/v2/bcfxml.py diff --git a/src/bcf/bcf/v2/data.py b/src/bcf/src/bcf/v2/data.py similarity index 100% rename from src/bcf/bcf/v2/data.py rename to src/bcf/src/bcf/v2/data.py diff --git a/src/bcf/bcf/v2/xsd/markup.xsd b/src/bcf/src/bcf/v2/xsd/markup.xsd similarity index 100% rename from src/bcf/bcf/v2/xsd/markup.xsd rename to src/bcf/src/bcf/v2/xsd/markup.xsd diff --git a/src/bcf/bcf/v2/xsd/project.xsd b/src/bcf/src/bcf/v2/xsd/project.xsd similarity index 100% rename from src/bcf/bcf/v2/xsd/project.xsd rename to src/bcf/src/bcf/v2/xsd/project.xsd diff --git a/src/bcf/bcf/v2/xsd/version.xsd b/src/bcf/src/bcf/v2/xsd/version.xsd similarity index 100% rename from src/bcf/bcf/v2/xsd/version.xsd rename to src/bcf/src/bcf/v2/xsd/version.xsd diff --git a/src/bcf/bcf/v2/xsd/visinfo.xsd b/src/bcf/src/bcf/v2/xsd/visinfo.xsd similarity index 100% rename from src/bcf/bcf/v2/xsd/visinfo.xsd rename to src/bcf/src/bcf/v2/xsd/visinfo.xsd diff --git a/src/bcf/src/bcf/v3/__init__.py b/src/bcf/src/bcf/v3/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/bcf/bcf/v3/bcfapi.py b/src/bcf/src/bcf/v3/bcfapi.py similarity index 100% rename from src/bcf/bcf/v3/bcfapi.py rename to src/bcf/src/bcf/v3/bcfapi.py diff --git a/src/bcf/bcf/v3/bcfxml.py b/src/bcf/src/bcf/v3/bcfxml.py similarity index 100% rename from src/bcf/bcf/v3/bcfxml.py rename to src/bcf/src/bcf/v3/bcfxml.py diff --git a/src/bcf/bcf/v3/data.py b/src/bcf/src/bcf/v3/data.py similarity index 100% rename from src/bcf/bcf/v3/data.py rename to src/bcf/src/bcf/v3/data.py diff --git a/src/bcf/bcf/v3/xsd/documents.xsd b/src/bcf/src/bcf/v3/xsd/documents.xsd similarity index 100% rename from src/bcf/bcf/v3/xsd/documents.xsd rename to src/bcf/src/bcf/v3/xsd/documents.xsd diff --git a/src/bcf/bcf/v3/xsd/extensions.xsd b/src/bcf/src/bcf/v3/xsd/extensions.xsd similarity index 100% rename from src/bcf/bcf/v3/xsd/extensions.xsd rename to src/bcf/src/bcf/v3/xsd/extensions.xsd diff --git a/src/bcf/bcf/v3/xsd/markup.xsd b/src/bcf/src/bcf/v3/xsd/markup.xsd similarity index 100% rename from src/bcf/bcf/v3/xsd/markup.xsd rename to src/bcf/src/bcf/v3/xsd/markup.xsd diff --git a/src/bcf/bcf/v3/xsd/project.xsd b/src/bcf/src/bcf/v3/xsd/project.xsd similarity index 100% rename from src/bcf/bcf/v3/xsd/project.xsd rename to src/bcf/src/bcf/v3/xsd/project.xsd diff --git a/src/bcf/bcf/v3/xsd/shared-types.xsd b/src/bcf/src/bcf/v3/xsd/shared-types.xsd similarity index 100% rename from src/bcf/bcf/v3/xsd/shared-types.xsd rename to src/bcf/src/bcf/v3/xsd/shared-types.xsd diff --git a/src/bcf/bcf/v3/xsd/version.xsd b/src/bcf/src/bcf/v3/xsd/version.xsd similarity index 100% rename from src/bcf/bcf/v3/xsd/version.xsd rename to src/bcf/src/bcf/v3/xsd/version.xsd diff --git a/src/bcf/bcf/v3/xsd/visinfo.xsd b/src/bcf/src/bcf/v3/xsd/visinfo.xsd similarity index 100% rename from src/bcf/bcf/v3/xsd/visinfo.xsd rename to src/bcf/src/bcf/v3/xsd/visinfo.xsd From c96d1e6387a429df11eab66e919899a2a435b559 Mon Sep 17 00:00:00 2001 From: krande Date: Wed, 25 Aug 2021 09:00:52 +0200 Subject: [PATCH 019/113] add lxml package as another dep to be installed before running tests --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c0191cd62..9d93f0d2ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,7 +71,7 @@ jobs: - name: install dependencies run: | sudo /usr/bin/python -m pip install -U pip - sudo /usr/bin/python -m pip install xmlschema numpy + sudo /usr/bin/python -m pip install xmlschema numpy lxml sudo /usr/bin/python -m pip install src/bcf - name: Test From 5e1fbacef704a3fc1fc094bf4fde32d0cda410b1 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 26 Aug 2021 21:37:14 +1000 Subject: [PATCH 020/113] Cost values are now serialised and shown as formulas --- .../blenderbim/bim/module/cost/operator.py | 10 +- .../blenderbim/bim/module/cost/ui.py | 1 + .../ifcopenshell/api/cost/data.py | 3 + .../ifcopenshell/util/cost.py | 142 ++++++++++++++++++ 4 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 src/ifcopenshell-python/ifcopenshell/util/cost.py diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index a23f8caa23..243fc1f049 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -592,12 +592,15 @@ class AddCostValue(bpy.types.Operator): self.file = IfcStore.get_file() if self.cost_type == "FIXED": category = None + attributes = {"AppliedValue": 0.0} elif self.cost_type == "SUM": category = "*" + attributes = {"Category": category} elif self.cost_type == "CATEGORY": category = self.cost_category + attributes = {"Category": category} value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.file.by_id(self.parent)) - ifcopenshell.api.run("cost.edit_cost_value", self.file, cost_value=value, attributes={"Category": category}) + ifcopenshell.api.run("cost.edit_cost_value", self.file, cost_value=value, attributes=attributes) Data.load(self.file) return {"FINISHED"} @@ -646,8 +649,11 @@ class EnableEditingCostItemValue(bpy.types.Operator): def import_attributes(self, name, prop, data, context): if name == "AppliedValue": - # TODO: for now, only support simple values + # TODO: for now, only support simple IfcValues (which are effectively IfcMonetaryMeasure) + prop = self.props.cost_value_attributes.add() prop.data_type = "float" + prop.name = "AppliedValue" + prop.is_optional = True prop.float_value = 0.0 if prop.is_null else data[name] return True if ( diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index 18ea7cd3f6..c126582388 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -190,6 +190,7 @@ class BIM_PT_cost_schedules(Panel): for cost_value_id in Data.cost_items[self.props.active_cost_item_id]["CostValues"]: row = self.layout.row(align=True) + row.label(text=Data.cost_values[cost_value_id]["Formula"]) self.draw_readonly_cost_value_ui(row, cost_value_id) if self.props.active_cost_item_value_id: diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/data.py b/src/ifcopenshell-python/ifcopenshell/api/cost/data.py index fdd8d811ec..dfb3cc36d9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/data.py @@ -1,5 +1,6 @@ import ifcopenshell.util.date import ifcopenshell.util.unit +import ifcopenshell.util.cost class Data: @@ -148,6 +149,8 @@ class Data: cost_item_data["CategoryValues"].setdefault(cost_value.Category, 0) cost_item_data["CategoryValues"][cost_value.Category] += value_data["AppliedValue"] + value_data["Formula"] = ifcopenshell.util.cost.serialise_cost_value(cost_value) + cls.cost_values[cost_value.id()] = value_data for component in cost_value.Components or []: cls.load_cost_item_value(cost_item, cost_item_data, component) diff --git a/src/ifcopenshell-python/ifcopenshell/util/cost.py b/src/ifcopenshell-python/ifcopenshell/util/cost.py new file mode 100644 index 0000000000..acc57e9bdb --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/util/cost.py @@ -0,0 +1,142 @@ +import lark + + +arithmetic_operator_symbols = {"ADD": "+", "DIVIDE": "/", "MULTIPLY": "*", "SUBTRACT": "-"} +symbol_arithmetic_operators = {"+": "ADD", "/": "DIVIDE", "*": "MULTIPLY", "-": "SUBTRACT"} + + +def serialise_cost_value(cost_value): + result = _serialise_cost_value(cost_value) + if result and result[0] == "(" and result[-1] == ")": + return result[1:-1] + return result + + +def _serialise_cost_value(cost_value): + value = "" + if cost_value.ArithmeticOperator and cost_value.Components: + operator = arithmetic_operator_symbols[cost_value.ArithmeticOperator] + serialised_components = [] + for component in cost_value.Components: + serialised_components.append(_serialise_cost_value(component)) + value = operator.join(serialised_components) + elif cost_value.AppliedValue is not None: + value = serialise_applied_value(cost_value.AppliedValue) + + category = "" + if cost_value.Category == "*": + category = "SUM" + elif cost_value.Category: + category = cost_value.Category + + if not category and not value: + value = "0" + + if category: + return f"{category}({value})" + elif cost_value.Components: + return f"({value})" + return value + + +def serialise_applied_value(applied_value): + if applied_value.is_a("IfcMonetaryMeasure"): + return str(applied_value.wrappedValue) + return "?" + + +def unserialise_cost_value(formula, cost_value): + unserialiser = CostValueUnserialiser() + result = unserialiser.parse(formula) + def map_element_to_result(element, result): + result["ifc"] = element + for i, component in enumerate(result.get("Components", [])): + if element.Components and i < len(element.Components): + map_element_to_formula(element.Components[i], result["Components"][i]) + map_element_to_result(cost_value, result) + return result + + +class CostValueUnserialiser: + def parse(self, formula): + l = lark.Lark( + """start: formula + formula: operand (operator operand)* + operand: value | category "(" formula ")" + value: NUMBER? + category: WORD? + operator: add | divide | multiply | subtract + add: "+" + divide: "/" + multiply: "*" + subtract: "-" + + // Embed common.lark for packaging + DIGIT: "0".."9" + HEXDIGIT: "a".."f"|"A".."F"|DIGIT + INT: DIGIT+ + SIGNED_INT: ["+"|"-"] INT + DECIMAL: INT "." INT? | "." INT + _EXP: ("e"|"E") SIGNED_INT + FLOAT: INT _EXP | DECIMAL _EXP? + SIGNED_FLOAT: ["+"|"-"] FLOAT + NUMBER: FLOAT | INT + SIGNED_NUMBER: ["+"|"-"] NUMBER + _STRING_INNER: /.*?/ + _STRING_ESC_INNER: _STRING_INNER /(? Date: Thu, 26 Aug 2021 13:44:33 +0200 Subject: [PATCH 021/113] Apply object operators to all selected objects (#1696) * Remove container executes on all selected objects * Pass silently if assigning or removing container on non-bim object * Assign / Unassign Material executes on all selected objects * AddRepresentation executes on all selected objects * Assign / Unassign Constraint executes on all selected objects * Fix error when trying to assign representation to non-mesh object * Assign / Unassign Document executes on all selected objects * Code Cleanup & Consistency * Fix Attribute data type not being assigned when it makes sense * Revert changes to geometry operator * Revert "Fix Attribute data type not being assigned when it makes sense" This reverts commit 1117c8670c12bfe2dbd9ebb7138677ba3a241003. * Revert changes to material operator * Use attribute helper functions * Filter select entities in import_attributes * revert changes to attribute fetching * Revert "Filter select entities in import_attributes" This reverts commit 7f84c2ea631c9286b9f8ce3f123228aa92bf1270. * Enforce new attribute data type to string * Restrict import_attributes usecase * Remove trailing spaces * Revert changes to IfcProjectedCRS attributes This is done prior to the planned unit refactor --- .../bim/module/classification/operator.py | 2 + .../bim/module/constraint/operator.py | 48 +++++++++++-------- .../bim/module/document/operator.py | 48 +++++++++++-------- .../bim/module/georeference/operator.py | 23 ++++----- .../blenderbim/bim/module/group/operator.py | 12 +---- .../blenderbim/bim/module/layer/operator.py | 14 ++---- .../bim/module/material/operator.py | 14 +----- .../blenderbim/bim/module/spatial/operator.py | 46 +++++++++++------- .../blenderbim/bim/module/system/operator.py | 13 +---- 9 files changed, 106 insertions(+), 114 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/classification/operator.py b/src/blenderbim/blenderbim/bim/module/classification/operator.py index b1dd53b8fd..9163d3f06d 100644 --- a/src/blenderbim/blenderbim/bim/module/classification/operator.py +++ b/src/blenderbim/blenderbim/bim/module/classification/operator.py @@ -76,6 +76,7 @@ class EnableEditingClassification(bpy.types.Operator): new.name = attribute.name() new.is_null = classification_data[attribute.name()] is None new.is_optional = attribute.optional() + new.data_type = "string" if attribute.name() == "ReferenceTokens": new.string_value = "" if new.is_null else json.dumps(classification_data[attribute.name()]) else: @@ -162,6 +163,7 @@ class EnableEditingClassificationReference(bpy.types.Operator): new.name = attribute.name() new.is_null = reference_data[attribute.name()] is None new.is_optional = attribute.optional() + new.data_type = "string" new.string_value = "" if new.is_null else reference_data[attribute.name()] props.active_reference_id = self.reference return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/constraint/operator.py b/src/blenderbim/blenderbim/bim/module/constraint/operator.py index d7b409fa93..567d49b266 100644 --- a/src/blenderbim/blenderbim/bim/module/constraint/operator.py +++ b/src/blenderbim/blenderbim/bim/module/constraint/operator.py @@ -187,17 +187,21 @@ class AssignConstraint(bpy.types.Operator): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object self.file = IfcStore.get_file() - ifcopenshell.api.run( - "constraint.assign_constraint", - self.file, - **{ - "product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), - "constraint": self.file.by_id(self.constraint), - } - ) - Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id) + objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects + for obj in objs: + obj_id = obj.BIMObjectProperties.ifc_definition_id + if not obj_id: + continue + ifcopenshell.api.run( + "constraint.assign_constraint", + self.file, + **{ + "product": self.file.by_id(obj_id), + "constraint": self.file.by_id(self.constraint), + } + ) + Data.load(self.file, obj_id) return {"FINISHED"} @@ -212,15 +216,19 @@ class UnassignConstraint(bpy.types.Operator): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object self.file = IfcStore.get_file() - ifcopenshell.api.run( - "constraint.unassign_constraint", - self.file, - **{ - "product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), - "constraint": self.file.by_id(self.constraint), - } - ) - Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id) + objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects + for obj in objs: + obj_id = obj.BIMObjectProperties.ifc_definition_id + if not obj_id: + continue + ifcopenshell.api.run( + "constraint.unassign_constraint", + self.file, + **{ + "product": self.file.by_id(obj_id), + "constraint": self.file.by_id(self.constraint), + } + ) + Data.load(self.file, obj_id) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/document/operator.py b/src/blenderbim/blenderbim/bim/module/document/operator.py index 1d9ed2929d..ac13c55590 100644 --- a/src/blenderbim/blenderbim/bim/module/document/operator.py +++ b/src/blenderbim/blenderbim/bim/module/document/operator.py @@ -276,17 +276,21 @@ class AssignDocument(bpy.types.Operator): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object self.file = IfcStore.get_file() - ifcopenshell.api.run( - "document.assign_document", - self.file, - **{ - "product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), - "document": self.file.by_id(self.document), - } - ) - Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id) + objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects + for obj in objs: + obj_id = obj.BIMObjectProperties.ifc_definition_id + if not obj_id: + continue + ifcopenshell.api.run( + "document.assign_document", + self.file, + **{ + "product": self.file.by_id(obj_id), + "document": self.file.by_id(self.document), + } + ) + Data.load(self.file, obj_id) return {"FINISHED"} @@ -301,15 +305,19 @@ class UnassignDocument(bpy.types.Operator): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object self.file = IfcStore.get_file() - ifcopenshell.api.run( - "document.unassign_document", - self.file, - **{ - "product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), - "document": self.file.by_id(self.document), - } - ) - Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id) + objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects + for obj in objs: + obj_id = obj.BIMObjectProperties.ifc_definition_id + if not obj_id: + continue + ifcopenshell.api.run( + "document.unassign_document", + self.file, + **{ + "product": self.file.by_id(obj_id), + "document": self.file.by_id(self.document), + } + ) + Data.load(self.file, obj_id) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/georeference/operator.py b/src/blenderbim/blenderbim/bim/module/georeference/operator.py index 2026de99e4..8549912384 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/operator.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/operator.py @@ -39,7 +39,7 @@ class EnableEditingGeoreferencing(bpy.types.Operator): props = context.scene.BIMGeoreferenceProperties props.projected_crs.clear() - + for attribute in IfcStore.get_schema().declaration_by_name("IfcProjectedCRS").all_attributes(): data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) if data_type == "entity": @@ -69,18 +69,9 @@ class EnableEditingGeoreferencing(bpy.types.Operator): props.map_unit_imperial = Data.projected_crs["MapUnit"]["Name"] props.map_conversion.clear() - - for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes(): - data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) - if data_type == "entity" or data_type == "select": - 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() - # Enforce a string data type to prevent data loss in single-precision Blender props - new.data_type = "string" - new.string_value = "" if new.is_null else str(Data.map_conversion[attribute.name()]) + blenderbim.bim.helper.import_attributes( + "IfcMapConversion", props.map_conversion, Data.map_conversion, self.import_map_conversion_attributes + ) props.has_true_north = bool(Data.true_north) if Data.true_north: @@ -90,6 +81,12 @@ class EnableEditingGeoreferencing(bpy.types.Operator): props.is_editing = True return {"FINISHED"} + def import_map_conversion_attributes(self, name, prop, data): + if name not in ["SourceCRS", "TargetCRS"]: + # Enforce a string data type to prevent data loss in single-precision Blender props + prop.data_type = "string" + prop.string_value = "" if prop.is_null else str(data[name]) + return True class DisableEditingGeoreferencing(bpy.types.Operator): bl_idname = "bim.disable_editing_georeferencing" diff --git a/src/blenderbim/blenderbim/bim/module/group/operator.py b/src/blenderbim/blenderbim/bim/module/group/operator.py index 0485249733..885df4c2ae 100644 --- a/src/blenderbim/blenderbim/bim/module/group/operator.py +++ b/src/blenderbim/blenderbim/bim/module/group/operator.py @@ -20,6 +20,7 @@ import bpy import ifcopenshell.util.attribute import ifcopenshell.api +import blenderbim.bim.helper from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.group.data import Data @@ -121,17 +122,8 @@ class EnableEditingGroup(bpy.types.Operator): props = context.scene.BIMGroupProperties props.group_attributes.clear() - data = Data.groups[self.group] + blenderbim.bim.helper.import_attributes("IfcGroup", props.group_attributes, Data.groups[self.group]) - for attribute in IfcStore.get_schema().declaration_by_name("IfcGroup").all_attributes(): - data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) - if data_type == "entity": - continue - new = props.group_attributes.add() - new.name = attribute.name() - new.is_null = data[attribute.name()] is None - new.is_optional = attribute.optional() - new.string_value = "" if new.is_null else data[attribute.name()] props.active_group_id = self.group return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/layer/operator.py b/src/blenderbim/blenderbim/bim/module/layer/operator.py index 2ebd391809..2e90fcc583 100644 --- a/src/blenderbim/blenderbim/bim/module/layer/operator.py +++ b/src/blenderbim/blenderbim/bim/module/layer/operator.py @@ -21,6 +21,7 @@ import bpy import json import ifcopenshell.util.attribute import ifcopenshell.api +import blenderbim.bim.helper from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.layer.data import Data @@ -63,17 +64,10 @@ class EnableEditingLayer(bpy.types.Operator): props = context.scene.BIMLayerProperties props.layer_attributes.clear() - data = Data.layers[self.layer] + blenderbim.bim.helper.import_attributes( + "IfcPresentationLayerAssignment", props.layer_attributes, Data.layers[self.layer] + ) - for attribute in IfcStore.get_schema().declaration_by_name("IfcPresentationLayerAssignment").all_attributes(): - data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) - if data_type == "entity" or data_type == "select": - continue - new = props.layer_attributes.add() - new.name = attribute.name() - new.is_null = data[attribute.name()] is None - new.is_optional = attribute.optional() - new.string_value = "" if new.is_null else data[attribute.name()] props.active_layer_id = self.layer return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/material/operator.py b/src/blenderbim/blenderbim/bim/module/material/operator.py index dfea986a57..b9212cf120 100644 --- a/src/blenderbim/blenderbim/bim/module/material/operator.py +++ b/src/blenderbim/blenderbim/bim/module/material/operator.py @@ -430,14 +430,7 @@ class EnableEditingAssignedMaterial(bpy.types.Operator): props.material_set_attributes.clear() - for attribute in IfcStore.get_schema().declaration_by_name(material_set_class).all_attributes(): - if "" not in str(attribute.type_of_attribute): - continue - if attribute.name() in material_set_data: - new = props.material_set_attributes.add() - new.name = attribute.name() - new.is_null = material_set_data[attribute.name()] is None - new.string_value = "" if new.is_null else material_set_data[attribute.name()] + blenderbim.bim.helper.import_attributes(material_set_class, props.material_set_attributes, material_set_data) return {"FINISHED"} def import_attributes(self, name, prop, data): @@ -509,10 +502,7 @@ class EditAssignedMaterial(bpy.types.Operator): return {"FINISHED"} material_set = self.file.by_id(self.material_set) - - attributes = {} - for attribute in props.material_set_attributes: - attributes[attribute.name] = None if attribute.is_null else attribute.string_value + attributes = blenderbim.bim.helper.export_attributes(props.material_set_attributes) ifcopenshell.api.run( "material.edit_assigned_material", self.file, diff --git a/src/blenderbim/blenderbim/bim/module/spatial/operator.py b/src/blenderbim/blenderbim/bim/module/spatial/operator.py index 7f8fe7b3aa..5c01b0c357 100644 --- a/src/blenderbim/blenderbim/bim/module/spatial/operator.py +++ b/src/blenderbim/blenderbim/bim/module/spatial/operator.py @@ -48,7 +48,9 @@ class AssignContainer(bpy.types.Operator): self.relating_structure or sprops.spatial_elements[sprops.active_spatial_element_index].ifc_definition_id ) for related_element in related_elements: - oprops = related_element.BIMObjectProperties + oprops = related_element.BIMObjectProperties + if not oprops.ifc_definition_id: + continue props = related_element.BIMObjectSpatialProperties ifcopenshell.api.run( @@ -134,25 +136,33 @@ class RemoveContainer(bpy.types.Operator): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - obj = bpy.data.objects.get(self.obj, context.active_object) - oprops = obj.BIMObjectProperties + active_object = context.active_object self.file = IfcStore.get_file() - ifcopenshell.api.run( - "spatial.remove_container", self.file, **{"product": self.file.by_id(oprops.ifc_definition_id)} - ) - Data.load(IfcStore.get_file(), oprops.ifc_definition_id) + objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects + for obj in objs: + obj_id = obj.BIMObjectProperties.ifc_definition_id + if not obj_id: + continue + ifcopenshell.api.run( + "spatial.remove_container", + self.file, + **{ + "product": self.file.by_id(obj_id) + } + ) + Data.load(self.file, obj_id) - aggregate_collection = bpy.data.collections.get(obj.name) - if aggregate_collection: - self.remove_collection(context.scene.collection, aggregate_collection) - for collection in bpy.data.collections: - self.remove_collection(collection, spatial_collection) - context.scene.collection.children.link(aggregate_collection) - else: - for collection in obj.users_collection: - collection.objects.unlink(obj) - context.scene.collection.objects.link(obj) - context.view_layer.objects.active = obj + aggregate_collection = bpy.data.collections.get(obj.name) + if aggregate_collection: + self.remove_collection(context.scene.collection, aggregate_collection) + for collection in bpy.data.collections: + self.remove_collection(collection, spatial_collection) + context.scene.collection.children.link(aggregate_collection) + else: + for collection in obj.users_collection: + collection.objects.unlink(obj) + context.scene.collection.objects.link(obj) + context.view_layer.objects.active = active_object return {"FINISHED"} def remove_collection(self, parent, child): diff --git a/src/blenderbim/blenderbim/bim/module/system/operator.py b/src/blenderbim/blenderbim/bim/module/system/operator.py index 23ce075299..e9824cf14d 100644 --- a/src/blenderbim/blenderbim/bim/module/system/operator.py +++ b/src/blenderbim/blenderbim/bim/module/system/operator.py @@ -20,6 +20,7 @@ import bpy import ifcopenshell.util.attribute import ifcopenshell.api +import blenderbim.bim.helper from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.system.data import Data @@ -121,17 +122,7 @@ class EnableEditingSystem(bpy.types.Operator): props = context.scene.BIMSystemProperties props.system_attributes.clear() - data = Data.systems[self.system] - - for attribute in IfcStore.get_schema().declaration_by_name("IfcSystem").all_attributes(): - data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) - if data_type == "entity": - continue - new = props.system_attributes.add() - new.name = attribute.name() - new.is_null = data[attribute.name()] is None - new.is_optional = attribute.optional() - new.string_value = "" if new.is_null else data[attribute.name()] + blenderbim.bim.helper.import_attributes("IfcSystem", props.system_attributes, Data.systems[self.system]) props.active_system_id = self.system return {"FINISHED"} From 16b34f942a854a5eb564829cf67518411ec139f4 Mon Sep 17 00:00:00 2001 From: krande Date: Thu, 26 Aug 2021 14:37:24 +0200 Subject: [PATCH 022/113] Update path to bcf package --- src/blenderbim/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blenderbim/Makefile b/src/blenderbim/Makefile index 39d0b78b7a..12453aea8d 100644 --- a/src/blenderbim/Makefile +++ b/src/blenderbim/Makefile @@ -99,7 +99,7 @@ endif cp dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/entity_instance.py dist/blenderbim/libs/site/packages/ifcopenshell/ cp dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/file.py dist/blenderbim/libs/site/packages/ifcopenshell/ # Provides bcf functionality - cp -r dist/working/IfcOpenShell-0.6.0/src/bcf/bcf dist/blenderbim/libs/site/packages/ + cp -r dist/working/IfcOpenShell-0.6.0/src/bcf/src/bcf dist/blenderbim/libs/site/packages/ # Provides IFCClash functionality cp -r dist/working/IfcOpenShell-0.6.0/src/ifcclash/ifcclash dist/blenderbim/libs/site/packages/ # Provides BIMTester functionality From b99c5036e683fe5e840c3bb21b9e0a32b5265270 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 27 Aug 2021 14:44:54 +1000 Subject: [PATCH 023/113] You can now edit nested arithmetic cost values by using spreadsheet-like formulas --- .../blenderbim/bim/module/cost/__init__.py | 2 + .../blenderbim/bim/module/cost/operator.py | 41 ++++++++++++- .../blenderbim/bim/module/cost/prop.py | 2 + .../blenderbim/bim/module/cost/ui.py | 57 ++++++------------- .../api/cost/edit_cost_value_formula.py | 34 +++++++++++ .../ifcopenshell/util/cost.py | 7 ++- 6 files changed, 100 insertions(+), 43 deletions(-) create mode 100644 src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py diff --git a/src/blenderbim/blenderbim/bim/module/cost/__init__.py b/src/blenderbim/blenderbim/bim/module/cost/__init__.py index c768025826..068fe6d598 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/cost/__init__.py @@ -27,6 +27,7 @@ classes = ( operator.EditCostItem, operator.EditCostItemQuantity, operator.EditCostValue, + operator.EditCostValueFormula, operator.EnableEditingCostSchedule, operator.EnableEditingCostItems, operator.EnableEditingCostItem, @@ -34,6 +35,7 @@ classes = ( operator.EnableEditingCostItemQuantity, operator.EnableEditingCostItemValues, operator.EnableEditingCostItemValue, + operator.EnableEditingCostItemValueFormula, operator.DisableEditingCostItem, operator.DisableEditingCostSchedule, operator.DisableEditingCostItemQuantity, diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index 243fc1f049..efab5ff5f1 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -637,6 +637,7 @@ class EnableEditingCostItemValue(bpy.types.Operator): self.props = context.scene.BIMCostProperties self.props.cost_value_attributes.clear() self.props.active_cost_item_value_id = self.cost_value + self.props.cost_value_editing_type = "ATTRIBUTES" data = Data.cost_values[self.cost_value] blenderbim.bim.helper.import_attributes( @@ -706,12 +707,50 @@ class DisableEditingCostItemValue(bpy.types.Operator): def execute(self, context): props = context.scene.BIMCostProperties props.active_cost_item_value_id = 0 + props.cost_value_editing_type = "" + return {"FINISHED"} + + +class EnableEditingCostItemValueFormula(bpy.types.Operator): + bl_idname = "bim.enable_editing_cost_item_value_formula" + bl_label = "Enable Editing Cost Item Value Formula" + bl_options = {"REGISTER", "UNDO"} + cost_value: bpy.props.IntProperty() + + def execute(self, context): + self.props = context.scene.BIMCostProperties + self.props.cost_value_attributes.clear() + self.props.active_cost_item_value_id = self.cost_value + self.props.cost_value_editing_type = "FORMULA" + self.props.cost_value_formula = Data.cost_values[self.cost_value]["Formula"] + return {"FINISHED"} + + +class EditCostValueFormula(bpy.types.Operator): + bl_idname = "bim.edit_cost_value_formula" + bl_label = "Edit Cost Value Formula" + bl_options = {"REGISTER", "UNDO"} + cost_value: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + props = context.scene.BIMCostProperties + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "cost.edit_cost_value_formula", + self.file, + **{"cost_value": self.file.by_id(self.cost_value), "formula": props.cost_value_formula}, + ) + Data.load(IfcStore.get_file()) + bpy.ops.bim.disable_editing_cost_item_value() return {"FINISHED"} class EditCostValue(bpy.types.Operator): bl_idname = "bim.edit_cost_value" - bl_label = "Edit Cost Item Value" + bl_label = "Edit Cost Value" bl_options = {"REGISTER", "UNDO"} cost_value: bpy.props.IntProperty() diff --git a/src/blenderbim/blenderbim/bim/module/cost/prop.py b/src/blenderbim/blenderbim/bim/module/cost/prop.py index 6530781763..3d11601970 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/prop.py +++ b/src/blenderbim/blenderbim/bim/module/cost/prop.py @@ -245,7 +245,9 @@ class BIMCostProperties(PropertyGroup): ) cost_category: StringProperty(name="Cost Category") active_cost_item_value_id: IntProperty(name="Active Cost Item Value Id") + cost_value_editing_type: StringProperty(name="Cost Value Editing Type") cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute) + cost_value_formula: StringProperty(name="Cost Value Formula") cost_column: StringProperty(name="Cost Column") should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False) columns: CollectionProperty(name="Columns", type=StrProperty) diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index c126582388..c0542b40c0 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -190,65 +190,42 @@ class BIM_PT_cost_schedules(Panel): for cost_value_id in Data.cost_items[self.props.active_cost_item_id]["CostValues"]: row = self.layout.row(align=True) - row.label(text=Data.cost_values[cost_value_id]["Formula"]) self.draw_readonly_cost_value_ui(row, cost_value_id) - if self.props.active_cost_item_value_id: + if self.props.cost_value_editing_type == "ATTRIBUTES": box = self.layout.box() self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_item_value_id]) def draw_readonly_cost_value_ui(self, layout, cost_value_id): - # This UI is really poor. Delete and start again. cost_value = Data.cost_values[cost_value_id] - cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"]) - if cost_value["Category"]: - cost_value_label += " ({})".format(cost_value["Category"]) - layout.label(text="", icon="DISC") + + if self.props.active_cost_item_value_id == cost_value_id and self.props.cost_value_editing_type == "FORMULA": + layout.prop(self.props, "cost_value_formula", text="") + else: + cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"]) + cost_value_label += " = " + cost_value["Formula"] + layout.label(text=cost_value_label, icon="DISC") + self.draw_cost_value_operator_ui(layout, cost_value_id, self.props.active_cost_item_id) - layout.label(text=cost_value_label) - - for component_id in cost_value["Components"] or []: - self.draw_readonly_component_cost_value_ui(layout, component_id, cost_value["id"]) - - def draw_readonly_component_cost_value_ui(self, layout, cost_value_id, parent_id, level=1): - self.draw_cost_value_operator_ui(layout, cost_value_id, parent_id) - cost_value = Data.cost_values[cost_value_id] - cost_value_label = ">" * level - cost_value_label += "{0:.2f}".format(cost_value["AppliedValue"]) - if cost_value["Category"]: - cost_value_label += " ({})".format(cost_value["Category"]) - layout.label(text=cost_value_label) - - for component_id in cost_value["Components"] or []: - self.draw_readonly_component_cost_value_ui(layout, component_id, cost_value["id"], level + 1) def draw_cost_value_operator_ui(self, layout, cost_value_id, parent_id): if self.props.active_cost_item_value_id and self.props.active_cost_item_value_id == cost_value_id: - op = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK") - op.cost_value = cost_value_id - op = layout.operator("bim.add_cost_value", text="", icon="ADD") - op.parent = cost_value_id - op.cost_type = self.props.cost_types - if self.props.cost_types == "CATEGORY": - op.cost_category = self.props.cost_category + if self.props.cost_value_editing_type == "ATTRIBUTES": + op = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK") + op.cost_value = cost_value_id + elif self.props.cost_value_editing_type == "FORMULA": + op = layout.operator("bim.edit_cost_value_formula", text="", icon="CHECKMARK") + op.cost_value = cost_value_id layout.operator("bim.disable_editing_cost_item_value", text="", icon="CANCEL") elif self.props.active_cost_item_value_id: - op = layout.operator("bim.add_cost_value", text="", icon="ADD") - op.parent = cost_value_id - op.cost_type = self.props.cost_types - if self.props.cost_types == "CATEGORY": - op.cost_category = self.props.cost_category op = layout.operator("bim.remove_cost_item_value", text="", icon="X") op.parent = parent_id op.cost_value = cost_value_id else: + op = layout.operator("bim.enable_editing_cost_item_value_formula", text="", icon="CON_TRANSLIKE") + op.cost_value = cost_value_id op = layout.operator("bim.enable_editing_cost_item_value", text="", icon="GREASEPENCIL") op.cost_value = cost_value_id - op = layout.operator("bim.add_cost_value", text="", icon="ADD") - op.parent = cost_value_id - op.cost_type = self.props.cost_types - if self.props.cost_types == "CATEGORY": - op.cost_category = self.props.cost_category op = layout.operator("bim.remove_cost_item_value", text="", icon="X") op.parent = parent_id op.cost_value = cost_value_id diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py new file mode 100644 index 0000000000..057b7202fe --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py @@ -0,0 +1,34 @@ +import ifcopenshell +import ifcopenshell.util.cost +import ifcopenshell.util.unit +import ifcopenshell.util.element + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"cost_value": None, "formula": {}} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + try: + data = ifcopenshell.util.cost.unserialise_cost_value(self.settings["formula"], self.settings["cost_value"]) + except: + return + self.edit_cost_value(data) + + def edit_cost_value(self, data, parent=None): + ifc = data.get("ifc", None) + if not ifc: + ifc = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=parent) + if "AppliedValue" in data: + if data["AppliedValue"]: + ifc.AppliedValue = self.file.createIfcMonetaryMeasure(data["AppliedValue"]) + else: + ifc.AppliedValue = None + ifc.Category = data["Category"] if "Category" in data else None + ifc.ArithmeticOperator = data["ArithmeticOperator"] if "ArithmeticOperator" in data else None + if "Components" in data: + for component in data["Components"]: + self.edit_cost_value(component, ifc) diff --git a/src/ifcopenshell-python/ifcopenshell/util/cost.py b/src/ifcopenshell-python/ifcopenshell/util/cost.py index acc57e9bdb..f73d5ecedf 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/cost.py +++ b/src/ifcopenshell-python/ifcopenshell/util/cost.py @@ -52,7 +52,7 @@ def unserialise_cost_value(formula, cost_value): result["ifc"] = element for i, component in enumerate(result.get("Components", [])): if element.Components and i < len(element.Components): - map_element_to_formula(element.Components[i], result["Components"][i]) + map_element_to_result(element.Components[i], result["Components"][i]) map_element_to_result(cost_value, result) return result @@ -116,11 +116,14 @@ class CostValueUnserialiser: def get_operand(self, operand): child = operand.children[0] if child.data == "value": - return {"AppliedValue": self.get_value(child)} + value = self.get_value(child) + return {"AppliedValue": float(value) if value else None} elif child.data == "category": data = {} category = self.get_category(child) if category: + if category.lower() == "sum": + category = "*" data["Category"] = category formula = self.get_formula(operand.children[1]) if formula.get("Components"): From b272210a97755fc1226353894986552000d47788 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 27 Aug 2021 17:12:21 +1000 Subject: [PATCH 024/113] Resource base costs are now supported with all the features coming from the cost module. --- .../blenderbim/bim/module/cost/__init__.py | 4 +- .../blenderbim/bim/module/cost/operator.py | 31 ++- .../blenderbim/bim/module/cost/prop.py | 2 +- .../blenderbim/bim/module/cost/ui.py | 26 +- .../bim/module/resource/__init__.py | 6 + .../bim/module/resource/operator.py | 208 +++++++++++++- .../blenderbim/bim/module/resource/prop.py | 13 + .../blenderbim/bim/module/resource/ui.py | 66 ++++- .../ifcopenshell/api/cost/add_cost_value.py | 4 + .../ifcopenshell/api/cost/data.py | 253 +++++++++--------- ...ost_item_value.py => remove_cost_value.py} | 4 + .../ifcopenshell/api/resource/data.py | 12 +- 12 files changed, 468 insertions(+), 161 deletions(-) rename src/ifcopenshell-python/ifcopenshell/api/cost/{remove_cost_item_value.py => remove_cost_value.py} (78%) diff --git a/src/blenderbim/blenderbim/bim/module/cost/__init__.py b/src/blenderbim/blenderbim/bim/module/cost/__init__.py index 068fe6d598..b2188023b7 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/cost/__init__.py @@ -26,8 +26,8 @@ classes = ( operator.EditCostSchedule, operator.EditCostItem, operator.EditCostItemQuantity, - operator.EditCostValue, - operator.EditCostValueFormula, + operator.EditCostItemValue, + operator.EditCostItemValueFormula, operator.EnableEditingCostSchedule, operator.EnableEditingCostItems, operator.EnableEditingCostItem, diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index efab5ff5f1..c9807fb922 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -27,6 +27,7 @@ from blenderbim.bim.ifc import IfcStore from bpy_extras.io_utils import ImportHelper from ifcopenshell.api.cost.data import Data from ifcopenshell.api.unit.data import Data as UnitData +from ifcopenshell.api.resource.data import Data as ResourceData class AddCostSchedule(bpy.types.Operator): @@ -590,6 +591,7 @@ class AddCostValue(bpy.types.Operator): def _execute(self, context): self.file = IfcStore.get_file() + parent = self.file.by_id(self.parent) if self.cost_type == "FIXED": category = None attributes = {"AppliedValue": 0.0} @@ -599,14 +601,17 @@ class AddCostValue(bpy.types.Operator): elif self.cost_type == "CATEGORY": category = self.cost_category attributes = {"Category": category} - value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.file.by_id(self.parent)) + value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=parent) ifcopenshell.api.run("cost.edit_cost_value", self.file, cost_value=value, attributes=attributes) - Data.load(self.file) + if parent.is_a("IfcConstructionResource"): + ResourceData.load(self.file) + else: + Data.load(self.file) return {"FINISHED"} class RemoveCostItemValue(bpy.types.Operator): - bl_idname = "bim.remove_cost_item_value" + bl_idname = "bim.remove_cost_value" bl_label = "Add Cost Item Value" bl_options = {"REGISTER", "UNDO"} parent: bpy.props.IntProperty() @@ -617,13 +622,17 @@ class RemoveCostItemValue(bpy.types.Operator): def _execute(self, context): self.file = IfcStore.get_file() + parent = self.file.by_id(self.parent) ifcopenshell.api.run( - "cost.remove_cost_item_value", + "cost.remove_cost_value", self.file, - parent=self.file.by_id(self.parent), + parent=parent, cost_value=self.file.by_id(self.cost_value), ) - Data.load(self.file) + if parent.is_a("IfcConstructionResource"): + ResourceData.load(self.file) + else: + Data.load(self.file) return {"FINISHED"} @@ -636,7 +645,7 @@ class EnableEditingCostItemValue(bpy.types.Operator): def execute(self, context): self.props = context.scene.BIMCostProperties self.props.cost_value_attributes.clear() - self.props.active_cost_item_value_id = self.cost_value + self.props.active_cost_value_id = self.cost_value self.props.cost_value_editing_type = "ATTRIBUTES" data = Data.cost_values[self.cost_value] @@ -706,7 +715,7 @@ class DisableEditingCostItemValue(bpy.types.Operator): def execute(self, context): props = context.scene.BIMCostProperties - props.active_cost_item_value_id = 0 + props.active_cost_value_id = 0 props.cost_value_editing_type = "" return {"FINISHED"} @@ -720,13 +729,13 @@ class EnableEditingCostItemValueFormula(bpy.types.Operator): def execute(self, context): self.props = context.scene.BIMCostProperties self.props.cost_value_attributes.clear() - self.props.active_cost_item_value_id = self.cost_value + self.props.active_cost_value_id = self.cost_value self.props.cost_value_editing_type = "FORMULA" self.props.cost_value_formula = Data.cost_values[self.cost_value]["Formula"] return {"FINISHED"} -class EditCostValueFormula(bpy.types.Operator): +class EditCostItemValueFormula(bpy.types.Operator): bl_idname = "bim.edit_cost_value_formula" bl_label = "Edit Cost Value Formula" bl_options = {"REGISTER", "UNDO"} @@ -748,7 +757,7 @@ class EditCostValueFormula(bpy.types.Operator): return {"FINISHED"} -class EditCostValue(bpy.types.Operator): +class EditCostItemValue(bpy.types.Operator): bl_idname = "bim.edit_cost_value" bl_label = "Edit Cost Value" bl_options = {"REGISTER", "UNDO"} diff --git a/src/blenderbim/blenderbim/bim/module/cost/prop.py b/src/blenderbim/blenderbim/bim/module/cost/prop.py index 3d11601970..69ac4f825f 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/prop.py +++ b/src/blenderbim/blenderbim/bim/module/cost/prop.py @@ -244,7 +244,7 @@ class BIMCostProperties(PropertyGroup): name="Cost Types", ) cost_category: StringProperty(name="Cost Category") - active_cost_item_value_id: IntProperty(name="Active Cost Item Value Id") + active_cost_value_id: IntProperty(name="Active Cost Item Value Id") cost_value_editing_type: StringProperty(name="Cost Value Editing Type") cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute) cost_value_formula: StringProperty(name="Cost Value Formula") diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index c0542b40c0..d2062d98ad 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -16,10 +16,10 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +import blenderbim.bim.helper import blenderbim.bim.module.cost.prop as CostProp from bpy.types import Panel, UIList from blenderbim.bim.ifc import IfcStore -from blenderbim.bim.helper import draw_attributes from ifcopenshell.api.cost.data import Data from ifcopenshell.api.unit.data import Data as UnitData @@ -94,7 +94,7 @@ class BIM_PT_cost_schedules(Panel): self.layout.template_list("BIM_UL_cost_columns", "", self.props, "columns", self.props, "active_column_index") def draw_editable_cost_schedule_ui(self): - draw_attributes(self.props.cost_schedule_attributes, self.layout) + blenderbim.bim.helper.draw_attributes(self.props.cost_schedule_attributes, self.layout) def draw_editable_cost_item_ui(self, cost_schedule_id): row = self.layout.row(align=True) @@ -140,7 +140,7 @@ class BIM_PT_cost_schedules(Panel): self.draw_editable_cost_item_values_ui() def draw_editable_cost_item_attributes_ui(self): - draw_attributes(self.props.cost_item_attributes, self.layout) + blenderbim.bim.helper.draw_attributes(self.props.cost_item_attributes, self.layout) def draw_editable_cost_item_quantities_ui(self): row = self.layout.row(align=True) @@ -175,7 +175,7 @@ class BIM_PT_cost_schedules(Panel): self.draw_editable_cost_item_quantity_ui(box) def draw_editable_cost_item_quantity_ui(self, layout): - draw_attributes(self.props.quantity_attributes, self.layout) + blenderbim.bim.helper.draw_attributes(self.props.quantity_attributes, self.layout) def draw_editable_cost_item_values_ui(self): row = self.layout.row(align=True) @@ -194,12 +194,12 @@ class BIM_PT_cost_schedules(Panel): if self.props.cost_value_editing_type == "ATTRIBUTES": box = self.layout.box() - self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_item_value_id]) + self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_value_id]) def draw_readonly_cost_value_ui(self, layout, cost_value_id): cost_value = Data.cost_values[cost_value_id] - if self.props.active_cost_item_value_id == cost_value_id and self.props.cost_value_editing_type == "FORMULA": + if self.props.active_cost_value_id == cost_value_id and self.props.cost_value_editing_type == "FORMULA": layout.prop(self.props, "cost_value_formula", text="") else: cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"]) @@ -209,16 +209,16 @@ class BIM_PT_cost_schedules(Panel): self.draw_cost_value_operator_ui(layout, cost_value_id, self.props.active_cost_item_id) def draw_cost_value_operator_ui(self, layout, cost_value_id, parent_id): - if self.props.active_cost_item_value_id and self.props.active_cost_item_value_id == cost_value_id: + if self.props.active_cost_value_id and self.props.active_cost_value_id == cost_value_id: if self.props.cost_value_editing_type == "ATTRIBUTES": - op = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK") + op = layout.operator("bim.edit_cost_item_value", text="", icon="CHECKMARK") op.cost_value = cost_value_id elif self.props.cost_value_editing_type == "FORMULA": - op = layout.operator("bim.edit_cost_value_formula", text="", icon="CHECKMARK") + op = layout.operator("bim.edit_cost_item_value_formula", text="", icon="CHECKMARK") op.cost_value = cost_value_id layout.operator("bim.disable_editing_cost_item_value", text="", icon="CANCEL") - elif self.props.active_cost_item_value_id: - op = layout.operator("bim.remove_cost_item_value", text="", icon="X") + elif self.props.active_cost_value_id: + op = layout.operator("bim.remove_cost_value", text="", icon="X") op.parent = parent_id op.cost_value = cost_value_id else: @@ -226,12 +226,12 @@ class BIM_PT_cost_schedules(Panel): op.cost_value = cost_value_id op = layout.operator("bim.enable_editing_cost_item_value", text="", icon="GREASEPENCIL") op.cost_value = cost_value_id - op = layout.operator("bim.remove_cost_item_value", text="", icon="X") + op = layout.operator("bim.remove_cost_value", text="", icon="X") op.parent = parent_id op.cost_value = cost_value_id def draw_editable_cost_value_ui(self, layout, cost_value): - draw_attributes(self.props.cost_value_attributes, layout) + blenderbim.bim.helper.draw_attributes(self.props.cost_value_attributes, layout) class BIM_PT_cost_item_types(Panel): diff --git a/src/blenderbim/blenderbim/bim/module/resource/__init__.py b/src/blenderbim/blenderbim/bim/module/resource/__init__.py index cb2f056939..39a17db65c 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/resource/__init__.py @@ -34,8 +34,14 @@ classes = ( operator.AssignResource, operator.UnassignResource, operator.EnableEditingResourceTime, + operator.EnableEditingResourceCosts, + operator.EnableEditingResourceCostValueFormula, + operator.EnableEditingResourceCostValue, operator.EditResourceTime, + operator.EditResourceCostValue, + operator.EditResourceCostValueFormula, operator.DisableEditingResourceTime, + operator.DisableEditingResourceCostValue, operator.CalculateResourceWork, prop.Resource, prop.BIMResourceProperties, diff --git a/src/blenderbim/blenderbim/bim/module/resource/operator.py b/src/blenderbim/blenderbim/bim/module/resource/operator.py index 34dbdd12be..547dd64396 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/operator.py +++ b/src/blenderbim/blenderbim/bim/module/resource/operator.py @@ -1,4 +1,3 @@ - # BlenderBIM Add-on - OpenBIM Blender Add-on # Copyright (C) 2020, 2021 Dion Moult # @@ -19,14 +18,15 @@ import bpy import json +import time +import isodate import ifcopenshell.api -from blenderbim.bim.ifc import IfcStore -from ifcopenshell.api.resource.data import Data import blenderbim.bim.helper import blenderbim.bim.module.sequence.helper as helper -import time from datetime import datetime -import isodate +from blenderbim.bim.ifc import IfcStore +from ifcopenshell.api.resource.data import Data +from ifcopenshell.api.unit.data import Data as UnitData class LoadResources(bpy.types.Operator): @@ -95,7 +95,6 @@ class EnableEditingResource(bpy.types.Operator): new.enum_value = data[attribute.name()] - class LoadResourceProperties(bpy.types.Operator): bl_idname = "bim.load_resource_properties" bl_label = "Load Resource Properties" @@ -301,7 +300,9 @@ class EnableEditingResourceTime(bpy.types.Operator): data = Data.resource_times[resource_time_id] - blenderbim.bim.helper.import_attributes("IfcResourceTime", props.resource_time_attributes, data, self.import_attributes) + blenderbim.bim.helper.import_attributes( + "IfcResourceTime", props.resource_time_attributes, data, self.import_attributes + ) props.active_resource_time_id = resource_time_id props.active_resource_id = self.resource props.editing_resource_type = "USAGE" @@ -319,7 +320,9 @@ class EnableEditingResourceTime(bpy.types.Operator): return True def add_resource_time(self): - resource_time = ifcopenshell.api.run("resource.add_resource_time", self.file, resource=self.file.by_id(self.resource)) + resource_time = ifcopenshell.api.run( + "resource.add_resource_time", self.file, resource=self.file.by_id(self.resource) + ) Data.load(self.file) return resource_time @@ -345,7 +348,9 @@ class EditResourceTime(bpy.types.Operator): def _execute(self, context): self.props = context.scene.BIMResourceProperties - attributes = blenderbim.bim.helper.export_attributes(self.props.resource_time_attributes, self.export_attributes) + attributes = blenderbim.bim.helper.export_attributes( + self.props.resource_time_attributes, self.export_attributes + ) self.file = IfcStore.get_file() ifcopenshell.api.run( @@ -365,7 +370,7 @@ class EditResourceTime(bpy.types.Operator): return True attributes[prop.name] = helper.parse_datetime(prop.string_value) return True - elif prop.name =="LevelingDelay" or "Work" in prop.name: + elif prop.name == "LevelingDelay" or "Work" in prop.name: if prop.is_null: attributes[prop.name] = None return True @@ -385,3 +390,186 @@ class CalculateResourceWork(bpy.types.Operator): Data.load(self.file) bpy.ops.bim.load_resources() return {"FINISHED"} + + +class EnableEditingResourceCosts(bpy.types.Operator): + bl_idname = "bim.enable_editing_resource_costs" + bl_label = "Enable Editing Resource Costs" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMResourceProperties + props.active_resource_id = self.resource + props.editing_resource_type = "COSTS" + bpy.ops.bim.disable_editing_resource_cost_value() + return {"FINISHED"} + + +class DisableEditingResourceCostValue(bpy.types.Operator): + bl_idname = "bim.disable_editing_resource_cost_value" + bl_label = "Disable Editing Resource Cost Value" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + props = context.scene.BIMResourceProperties + props.active_cost_value_id = 0 + props.cost_value_editing_type = "" + return {"FINISHED"} + + +class DisableEditingResourceCostValue(bpy.types.Operator): + bl_idname = "bim.disable_editing_resource_cost_value" + bl_label = "Disable Editing Resource Cost Value" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + props = context.scene.BIMResourceProperties + props.active_cost_value_id = 0 + props.cost_value_editing_type = "" + return {"FINISHED"} + + +class EnableEditingResourceCostValueFormula(bpy.types.Operator): + bl_idname = "bim.enable_editing_resource_cost_value_formula" + bl_label = "Enable Editing Resource Cost Value Formula" + bl_options = {"REGISTER", "UNDO"} + cost_value: bpy.props.IntProperty() + + def execute(self, context): + self.props = context.scene.BIMResourceProperties + self.props.cost_value_attributes.clear() + self.props.active_cost_value_id = self.cost_value + self.props.cost_value_editing_type = "FORMULA" + self.props.cost_value_formula = Data.cost_values[self.cost_value]["Formula"] + return {"FINISHED"} + + +class EnableEditingResourceCostValue(bpy.types.Operator): + bl_idname = "bim.enable_editing_resource_cost_value" + bl_label = "Enable Editing Resource Cost Value" + bl_options = {"REGISTER", "UNDO"} + cost_value: bpy.props.IntProperty() + + def execute(self, context): + self.props = context.scene.BIMResourceProperties + self.props.cost_value_attributes.clear() + self.props.active_cost_value_id = self.cost_value + self.props.cost_value_editing_type = "ATTRIBUTES" + data = Data.cost_values[self.cost_value] + + blenderbim.bim.helper.import_attributes( + data["type"], + self.props.cost_value_attributes, + data, + lambda name, prop, data: self.import_attributes(name, prop, data, context), + ) + return {"FINISHED"} + + def import_attributes(self, name, prop, data, context): + if name == "AppliedValue": + # TODO: for now, only support simple IfcValues (which are effectively IfcMonetaryMeasure) + prop = self.props.cost_value_attributes.add() + prop.data_type = "float" + prop.name = "AppliedValue" + prop.is_optional = True + prop.float_value = 0.0 if prop.is_null else data[name] + return True + elif name == "UnitBasis": + prop = self.props.cost_value_attributes.add() + prop.name = "UnitBasisValue" + prop.data_type = "float" + prop.is_null = data["UnitBasis"] is None + prop.is_optional = True + if data["UnitBasis"]: + prop.float_value = data["UnitBasis"]["ValueComponent"] or 0 + else: + prop.float_value = 0 + + prop = self.props.cost_value_attributes.add() + prop.name = "UnitBasisUnit" + prop.data_type = "enum" + prop.is_null = prop.is_optional = False + units = {} + for unit_id, unit in UnitData.units.items(): + if unit.get("UnitType", None) in [ + "AREAUNIT", + "LENGTHUNIT", + "TIMEUNIT", + "VOLUMEUNIT", + "MASSUNIT", + "USERDEFINED", + ]: + if unit["type"] == "IfcContextDependentUnit": + units[unit_id] = f"{unit['UnitType']} / {unit['Name']}" + else: + name = unit["Name"] + if unit.get("Prefix", None): + name = f"(unit['Prefix']) {name}" + units[unit_id] = f"{unit['UnitType']} / {name}" + prop.enum_items = json.dumps(units) + if data["UnitBasis"] and data["UnitBasis"]["UnitComponent"]: + prop.enum_value = str(data["UnitBasis"]["UnitComponent"]) + return True + + +class EditResourceCostValueFormula(bpy.types.Operator): + bl_idname = "bim.edit_resource_cost_value_formula" + bl_label = "Edit Resource Cost Value Formula" + bl_options = {"REGISTER", "UNDO"} + cost_value: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + props = context.scene.BIMResourceProperties + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "cost.edit_cost_value_formula", + self.file, + **{"cost_value": self.file.by_id(self.cost_value), "formula": props.cost_value_formula}, + ) + Data.load(IfcStore.get_file()) + bpy.ops.bim.disable_editing_resource_cost_value() + return {"FINISHED"} + + +class EditResourceCostValue(bpy.types.Operator): + bl_idname = "bim.edit_resource_cost_value" + bl_label = "Edit Resource Cost Value" + bl_options = {"REGISTER", "UNDO"} + cost_value: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + props = context.scene.BIMResourceProperties + attributes = blenderbim.bim.helper.export_attributes( + props.cost_value_attributes, lambda attributes, prop: self.export_attributes(attributes, prop, context) + ) + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "cost.edit_cost_value", + self.file, + **{"cost_value": self.file.by_id(self.cost_value), "attributes": attributes}, + ) + Data.load(IfcStore.get_file()) + bpy.ops.bim.disable_editing_resource_cost_value() + return {"FINISHED"} + + def export_attributes(self, attributes, prop, context): + if prop.name == "UnitBasisValue": + if prop.is_null: + attributes["UnitBasis"] = None + return True + attributes["UnitBasis"] = { + "ValueComponent": prop.float_value or 1, + "UnitComponent": IfcStore.get_file().by_id( + int(context.scene.BIMResourceProperties.cost_value_attributes.get("UnitBasisUnit").enum_value) + ), + } + return True + if prop.name == "UnitBasisUnit": + return True diff --git a/src/blenderbim/blenderbim/bim/module/resource/prop.py b/src/blenderbim/blenderbim/bim/module/resource/prop.py index 5681deba24..f9f436f6f4 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/prop.py +++ b/src/blenderbim/blenderbim/bim/module/resource/prop.py @@ -74,3 +74,16 @@ class BIMResourceProperties(PropertyGroup): active_resource_time_id: IntProperty(name="Active Resource Usage Id") resource_time_attributes: CollectionProperty(name="Resource Usage Attributes", type=Attribute) editing_resource_type: StringProperty(name="Editing Resource Type") + cost_types: EnumProperty( + items=[ + ("FIXED", "Fixed", "The cost value is a fixed number"), + ("SUM", "Sum", "The cost value is automatically derived from the sum of all nested cost items"), + ("CATEGORY", "Category", "The cost value represents a single category"), + ], + name="Cost Types", + ) + cost_category: StringProperty(name="Cost Category") + active_cost_value_id: IntProperty(name="Active Resource Cost Value Id") + cost_value_editing_type: StringProperty(name="Cost Value Editing Type") + cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute) + cost_value_formula: StringProperty(name="Cost Value Formula") diff --git a/src/blenderbim/blenderbim/bim/module/resource/ui.py b/src/blenderbim/blenderbim/bim/module/resource/ui.py index c9e383d628..730133be72 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/ui.py +++ b/src/blenderbim/blenderbim/bim/module/resource/ui.py @@ -16,10 +16,10 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +import blenderbim.bim.helper from bpy.types import Panel, UIList from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.resource.data import Data -import blenderbim.bim.helper class BIM_PT_resources(Panel): @@ -61,9 +61,11 @@ class BIM_PT_resources(Panel): self.props, "active_resource_index", ) + if self.props.active_resource_id and self.props.editing_resource_type == "ATTRIBUTES": self.draw_editable_resource_attributes_ui() - + elif self.props.active_resource_id and self.props.editing_resource_type == "COSTS": + self.draw_editable_resource_costs_ui() elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE": self.draw_editable_resource_time_attributes_ui() @@ -107,6 +109,8 @@ class BIM_PT_resources(Panel): elif self.props.active_resource_id == ifc_definition_id and self.props.editing_resource_type == "USAGE": row.operator("bim.edit_resource_time", text="", icon="CHECKMARK") row.operator("bim.disable_editing_resource_time", text="", icon="CANCEL") + elif self.props.active_resource_id == ifc_definition_id and self.props.editing_resource_type == "COSTS": + row.operator("bim.disable_editing_resource", text="", icon="CANCEL") elif self.props.active_resource_id: row.operator("bim.add_resource", text="", icon="ADD").resource = ifc_definition_id row.operator("bim.remove_resource", text="", icon="X").resource = ifc_definition_id @@ -115,6 +119,8 @@ class BIM_PT_resources(Panel): op = row.operator("bim.calculate_resource_work", text="", icon="TEMP") op.resource = ifc_definition_id row.operator("bim.enable_editing_resource_time", text="", icon="TIME").resource = ifc_definition_id + op = row.operator("bim.enable_editing_resource_costs", text="", icon="DISC") + op.resource = ifc_definition_id row.operator("bim.enable_editing_resource", text="", icon="GREASEPENCIL").resource = ifc_definition_id row.operator("bim.remove_resource", text="", icon="X").resource = ifc_definition_id @@ -124,6 +130,62 @@ class BIM_PT_resources(Panel): def draw_editable_resource_time_attributes_ui(self): blenderbim.bim.helper.draw_attributes(self.props.resource_time_attributes, self.layout) + def draw_editable_resource_costs_ui(self): + row = self.layout.row(align=True) + row.prop(self.props, "cost_types", text="") + if self.props.cost_types == "CATEGORY": + row.prop(self.props, "cost_category", text="") + op = row.operator("bim.add_cost_value", text="", icon="ADD") + op.parent = self.props.active_resource_id + op.cost_type = self.props.cost_types + if self.props.cost_types == "CATEGORY": + op.cost_category = self.props.cost_category + + for cost_value_id in Data.resources[self.props.active_resource_id]["BaseCosts"] or []: + row = self.layout.row(align=True) + self.draw_readonly_cost_value_ui(row, cost_value_id) + + if self.props.cost_value_editing_type == "ATTRIBUTES": + box = self.layout.box() + self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_value_id]) + + def draw_readonly_cost_value_ui(self, layout, cost_value_id): + cost_value = Data.cost_values[cost_value_id] + + if self.props.active_cost_value_id == cost_value_id and self.props.cost_value_editing_type == "FORMULA": + layout.prop(self.props, "cost_value_formula", text="") + else: + cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"]) + cost_value_label += " = " + cost_value["Formula"] + layout.label(text=cost_value_label, icon="DISC") + + self.draw_cost_value_operator_ui(layout, cost_value_id, self.props.active_resource_id) + + def draw_cost_value_operator_ui(self, layout, cost_value_id, parent_id): + if self.props.active_cost_value_id and self.props.active_cost_value_id == cost_value_id: + if self.props.cost_value_editing_type == "ATTRIBUTES": + op = layout.operator("bim.edit_resource_cost_value", text="", icon="CHECKMARK") + op.cost_value = cost_value_id + elif self.props.cost_value_editing_type == "FORMULA": + op = layout.operator("bim.edit_resource_cost_value_formula", text="", icon="CHECKMARK") + op.cost_value = cost_value_id + layout.operator("bim.disable_editing_resource_cost_value", text="", icon="CANCEL") + elif self.props.active_cost_value_id: + op = layout.operator("bim.remove_cost_value", text="", icon="X") + op.parent = parent_id + op.cost_value = cost_value_id + else: + op = layout.operator("bim.enable_editing_resource_cost_value_formula", text="", icon="CON_TRANSLIKE") + op.cost_value = cost_value_id + op = layout.operator("bim.enable_editing_resource_cost_value", text="", icon="GREASEPENCIL") + op.cost_value = cost_value_id + op = layout.operator("bim.remove_cost_value", text="", icon="X") + op.parent = parent_id + op.cost_value = cost_value_id + + def draw_editable_cost_value_ui(self, layout, cost_value): + blenderbim.bim.helper.draw_attributes(self.props.cost_value_attributes, layout) + class BIM_UL_resources(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py index eb3c44f2c7..5eaaa80384 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py @@ -11,6 +11,10 @@ class Usecase: values = list(self.settings["parent"].CostValues or []) values.append(value) self.settings["parent"].CostValues = values + elif self.settings["parent"].is_a("IfcConstructionResource"): + values = list(self.settings["parent"].BaseCosts or []) + values.append(value) + self.settings["parent"].BaseCosts = values elif self.settings["parent"].is_a("IfcCostValue"): values = list(self.settings["parent"].Components or []) values.append(value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/data.py b/src/ifcopenshell-python/ifcopenshell/api/cost/data.py index dfb3cc36d9..d402ed423e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/data.py @@ -3,7 +3,137 @@ import ifcopenshell.util.unit import ifcopenshell.util.cost -class Data: +class CostValueTrait: + @classmethod + def load_cost_values(cls, root_element, data): + data["CostValues"] = [] + data["CategoryValues"] = {} + data["UnitBasisValueComponent"] = None + data["UnitBasisUnitSymbol"] = None + data["TotalAppliedValue"] = 0.0 + data["TotalCost"] = 0.0 + if root_element.is_a("IfcCostItem"): + values = root_element.CostValues + elif root_element.is_a("IfcConstructionResource"): + values = root_element.BaseCosts + for cost_value in values or []: + cls.load_cost_value(root_element, data, cost_value) + data["CostValues"].append(cost_value.id()) + data["TotalAppliedValue"] += cls.cost_values[cost_value.id()]["AppliedValue"] + if cost_value.UnitBasis: + cost_value_data = cls.cost_values[cost_value.id()] + data["UnitBasisValueComponent"] = cost_value_data["UnitBasis"]["ValueComponent"] + data["UnitBasisUnitSymbol"] = cost_value_data["UnitBasis"]["UnitSymbol"] + if data["UnitBasisValueComponent"]: + data["TotalCost"] = data["TotalCostQuantity"] / data["UnitBasisValueComponent"] * data["TotalAppliedValue"] + else: + data["TotalCost"] = data["TotalCostQuantity"] * data["TotalAppliedValue"] + + @classmethod + def load_cost_value(cls, root_element, root_element_data, cost_value): + value_data = cost_value.get_info() + del value_data["AppliedValue"] + if value_data["UnitBasis"]: + data = cost_value.UnitBasis.get_info() + data["ValueComponent"] = data["ValueComponent"].wrappedValue + data["UnitComponent"] = data["UnitComponent"].id() + data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(cost_value.UnitBasis.UnitComponent) + value_data["UnitBasis"] = data + if value_data["ApplicableDate"]: + value_data["ApplicableDate"] = ifcopenshell.util.date.ifc2datetime(value_data["ApplicableDate"]) + if value_data["FixedUntilDate"]: + value_data["FixedUntilDate"] = ifcopenshell.util.date.ifc2datetime(value_data["FixedUntilDate"]) + value_data["Components"] = [c.id() for c in value_data["Components"] or []] + value_data["AppliedValue"] = cls.calculate_applied_value(root_element, cost_value) + + if cost_value.Category not in [None, "*"]: + root_element_data["CategoryValues"].setdefault(cost_value.Category, 0) + root_element_data["CategoryValues"][cost_value.Category] += value_data["AppliedValue"] + + value_data["Formula"] = ifcopenshell.util.cost.serialise_cost_value(cost_value) + + cls.cost_values[cost_value.id()] = value_data + for component in cost_value.Components or []: + cls.load_cost_value(root_element, root_element_data, component) + + @classmethod + def calculate_applied_value(cls, root_element, cost_value, category_filter=None): + if cost_value.ArithmeticOperator and cost_value.Components: + component_values = [] + for component in cost_value.Components: + component_values.append(cls.calculate_applied_value(root_element, component, category_filter)) + if cost_value.ArithmeticOperator == "ADD": + return sum(component_values) + result = component_values.pop(0) + if cost_value.ArithmeticOperator == "DIVIDE": + for value in component_values: + try: + result /= value + except ZeroDivisionError: + pass + elif cost_value.ArithmeticOperator == "MULTIPLY": + for value in component_values: + result *= value + elif cost_value.ArithmeticOperator == "SUBTRACT": + for value in component_values: + result -= value + return result + if cost_value.Category is None: + return cls.get_primitive_applied_value(cost_value.AppliedValue) + elif cost_value.Category == "*": + if root_element.IsNestedBy: + return cls.sum_child_root_elements(root_element) + else: + return cls.get_primitive_applied_value(cost_value.AppliedValue) + elif cost_value.Category: + if root_element.IsNestedBy: + return cls.sum_child_root_elements(root_element, category_filter=cost_value.Category) + else: + return cls.get_primitive_applied_value(cost_value.AppliedValue) + return 0 + + @classmethod + def sum_child_root_elements(cls, root_element, category_filter=None): + result = 0 + for rel in root_element.IsNestedBy: + for child_root_element in rel.RelatedObjects: + if root_element.is_a("IfcCostItem"): + values = child_root_element.CostValues + elif root_element.is_a("IfcConstructionResource"): + values = child_root_element.BaseCosts + for child_cost_value in values or []: + if category_filter and child_cost_value.Category != category_filter: + continue + child_applied_value = cls.calculate_applied_value(child_root_element, child_cost_value) + child_quantity = cls.get_total_quantity(child_root_element) + if child_cost_value.UnitBasis: + value_component = child_cost_value.UnitBasis.ValueComponent.wrappedValue + result += child_quantity / value_component * child_applied_value + else: + result += child_quantity * child_applied_value + return result + + @classmethod + def get_total_quantity(cls, root_element): + if root_element.is_a("IfcCostItem"): + return sum([q[3] for q in root_element.CostQuantities or []]) or 1.0 + elif root_element.is_a("IfcConstructionResource"): + return root_element.BaseQuantity[3] if root_element.BaseQuantity else 1.0 + + @classmethod + def get_primitive_applied_value(cls, applied_value): + if not applied_value: + return 0.0 + elif isinstance(applied_value, float): + return applied_value + elif hasattr(applied_value, "wrappedValue") and isinstance(applied_value.wrappedValue, float): + return applied_value.wrappedValue + elif applied_value.is_a("IfcMeasureWithUnit"): + return applied_value.ValueComponent + assert False, "Applied value {applied_value} not implemented" + + +class Data(CostValueTrait): is_loaded = False cost_schedules = {} cost_items = {} @@ -63,7 +193,7 @@ class Data: parametric_quantities.extend(quantities) cls.cost_items[cost_item.id()] = data cls.load_cost_item_quantities(cost_item, data, parametric_quantities) - cls.load_cost_item_values(cost_item, data) + cls.load_cost_values(cost_item, data) cls.is_loaded = True @classmethod @@ -104,122 +234,3 @@ class Data: else: data["Unit"] = None data["UnitSymbol"] = None - - @classmethod - def load_cost_item_values(cls, cost_item, data): - data["CostValues"] = [] - data["CategoryValues"] = {} - data["UnitBasisValueComponent"] = None - data["UnitBasisUnitSymbol"] = None - data["TotalAppliedValue"] = 0.0 - data["TotalCost"] = 0.0 - for cost_value in cost_item.CostValues or []: - cls.load_cost_item_value(cost_item, data, cost_value) - data["CostValues"].append(cost_value.id()) - data["TotalAppliedValue"] += cls.cost_values[cost_value.id()]["AppliedValue"] - if cost_value.UnitBasis: - cost_value_data = cls.cost_values[cost_value.id()] - data["UnitBasisValueComponent"] = cost_value_data["UnitBasis"]["ValueComponent"] - data["UnitBasisUnitSymbol"] = cost_value_data["UnitBasis"]["UnitSymbol"] - if data["UnitBasisValueComponent"]: - data["TotalCost"] = ( - data["TotalCostQuantity"] / data["UnitBasisValueComponent"] * data["TotalAppliedValue"] - ) - else: - data["TotalCost"] = data["TotalCostQuantity"] * data["TotalAppliedValue"] - - @classmethod - def load_cost_item_value(cls, cost_item, cost_item_data, cost_value): - value_data = cost_value.get_info() - del value_data["AppliedValue"] - if value_data["UnitBasis"]: - data = cost_value.UnitBasis.get_info() - data["ValueComponent"] = data["ValueComponent"].wrappedValue - data["UnitComponent"] = data["UnitComponent"].id() - data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(cost_value.UnitBasis.UnitComponent) - value_data["UnitBasis"] = data - if value_data["ApplicableDate"]: - value_data["ApplicableDate"] = ifcopenshell.util.date.ifc2datetime(value_data["ApplicableDate"]) - if value_data["FixedUntilDate"]: - value_data["FixedUntilDate"] = ifcopenshell.util.date.ifc2datetime(value_data["FixedUntilDate"]) - value_data["Components"] = [c.id() for c in value_data["Components"] or []] - value_data["AppliedValue"] = cls.calculate_applied_value(cost_item, cost_value) - - if cost_value.Category not in [None, "*"]: - cost_item_data["CategoryValues"].setdefault(cost_value.Category, 0) - cost_item_data["CategoryValues"][cost_value.Category] += value_data["AppliedValue"] - - value_data["Formula"] = ifcopenshell.util.cost.serialise_cost_value(cost_value) - - cls.cost_values[cost_value.id()] = value_data - for component in cost_value.Components or []: - cls.load_cost_item_value(cost_item, cost_item_data, component) - - @classmethod - def calculate_applied_value(cls, cost_item, cost_value, category_filter=None): - if cost_value.ArithmeticOperator and cost_value.Components: - component_values = [] - for component in cost_value.Components: - component_values.append(cls.calculate_applied_value(cost_item, component, category_filter)) - if cost_value.ArithmeticOperator == "ADD": - return sum(component_values) - result = component_values.pop(0) - if cost_value.ArithmeticOperator == "DIVIDE": - for value in component_values: - try: - result /= value - except ZeroDivisionError: - pass - elif cost_value.ArithmeticOperator == "MULTIPLY": - for value in component_values: - result *= value - elif cost_value.ArithmeticOperator == "SUBTRACT": - for value in component_values: - result -= value - return result - if cost_value.Category is None: - return cls.get_primitive_applied_value(cost_value.AppliedValue) - elif cost_value.Category == "*": - if cost_item.IsNestedBy: - return cls.sum_child_cost_items(cost_item) - else: - return cls.get_primitive_applied_value(cost_value.AppliedValue) - elif cost_value.Category: - if cost_item.IsNestedBy: - return cls.sum_child_cost_items(cost_item, category_filter=cost_value.Category) - else: - return cls.get_primitive_applied_value(cost_value.AppliedValue) - return 0 - - @classmethod - def sum_child_cost_items(cls, cost_item, category_filter=None): - result = 0 - for rel in cost_item.IsNestedBy: - for child_cost_item in rel.RelatedObjects: - for child_cost_value in child_cost_item.CostValues or []: - if category_filter and child_cost_value.Category != category_filter: - continue - child_applied_value = cls.calculate_applied_value(child_cost_item, child_cost_value) - child_quantity = cls.get_total_quantity(child_cost_item) - if child_cost_value.UnitBasis: - value_component = child_cost_value.UnitBasis.ValueComponent.wrappedValue - result += child_quantity / value_component * child_applied_value - else: - result += child_quantity * child_applied_value - return result - - @classmethod - def get_total_quantity(cls, cost_item): - return sum([q[3] for q in cost_item.CostQuantities or []]) or 1.0 - - @classmethod - def get_primitive_applied_value(cls, applied_value): - if not applied_value: - return 0.0 - elif isinstance(applied_value, float): - return applied_value - elif hasattr(applied_value, "wrappedValue") and isinstance(applied_value.wrappedValue, float): - return applied_value.wrappedValue - elif applied_value.is_a("IfcMeasureWithUnit"): - return applied_value.ValueComponent - assert False, "Applied value {applied_value} not implemented" diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py similarity index 78% rename from src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_value.py rename to src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py index 58eb0b9675..e3cab8dc5f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py @@ -13,6 +13,10 @@ class Usecase: values = list(self.settings["parent"].CostValues) values.remove(self.settings["cost_value"]) self.settings["parent"].CostValues = values if values else None + elif self.settings["parent"].is_a("IfcConstructionResource"): + values = list(self.settings["parent"].BaseCosts) + values.remove(self.settings["cost_value"]) + self.settings["parent"].BaseCosts = values if values else None elif self.settings["parent"].is_a("IfcCostValue"): components = list(self.settings["parent"].Components) components.remove(self.settings["cost_value"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/data.py b/src/ifcopenshell-python/ifcopenshell/api/resource/data.py index b917e18920..fae5c37779 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/data.py @@ -1,17 +1,20 @@ import ifcopenshell import ifcopenshell.util.date +from ifcopenshell.api.cost.data import CostValueTrait -class Data: +class Data(CostValueTrait): is_loaded = False resources = {} resource_times = {} + cost_values = {} @classmethod def purge(cls): cls.is_loaded = False cls.resources = {} cls.resource_times = {} + cls.cost_values = {} @classmethod def load(cls, file): @@ -25,6 +28,7 @@ class Data: @classmethod def load_resources(cls): cls.resources = {} + cls.cost_values = {} for resource in cls._file.by_type("IfcResource"): data = resource.get_info() del data["OwnerHistory"] @@ -40,6 +44,12 @@ class Data: data["HasContext"] = resource.HasContext[0].RelatingContext.id() if resource.HasContext else None if resource.Usage: data["Usage"] = data["Usage"].id() + data["TotalCostQuantity"] = cls.get_total_quantity(resource) + if resource.BaseQuantity: + data["BaseQuantity"] = resource.BaseQuantity.id() + if resource.BaseCosts: + data["BaseCosts"] = [e.id() for e in resource.BaseCosts] + cls.load_cost_values(resource, data) cls.resources[resource.id()] = data @classmethod From dd0a3d0421503ff48e666ea834017781dfbfbf1e Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 27 Aug 2021 17:24:42 +1000 Subject: [PATCH 025/113] Minor fix. --- src/blenderbim/blenderbim/bim/module/unit/operator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blenderbim/blenderbim/bim/module/unit/operator.py b/src/blenderbim/blenderbim/bim/module/unit/operator.py index d55c0cd3e6..cc59fdf295 100644 --- a/src/blenderbim/blenderbim/bim/module/unit/operator.py +++ b/src/blenderbim/blenderbim/bim/module/unit/operator.py @@ -243,7 +243,7 @@ class EnableEditingUnit(bpy.types.Operator): return {"FINISHED"} def import_attributes(self, name, prop, data, context): - if name == "Dimensions": + if name == "Dimensions" and data["type"] != "IfcSIUnit": new = context.scene.BIMUnitProperties.unit_attributes.add() new.name = name new.is_null = data[name] is None From 8d5970f002d23fcb4cf602a17516524cec020a64 Mon Sep 17 00:00:00 2001 From: Gorgious56 Date: Fri, 27 Aug 2021 10:07:51 +0200 Subject: [PATCH 026/113] Display project units instead of hardcoded ones (#1699) * Display project units instead of hardcoded ones * Treat MapUnit as a regular attribute * Remove left over attributes * Refactor operator for MapUnit change * Fix unit fetch failing because of wrong id type --- .../bim/module/georeference/operator.py | 72 +++++++++---------- .../bim/module/georeference/prop.py | 16 ----- .../blenderbim/bim/module/georeference/ui.py | 8 --- 3 files changed, 33 insertions(+), 63 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/georeference/operator.py b/src/blenderbim/blenderbim/bim/module/georeference/operator.py index 8549912384..9c2f64f0eb 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/operator.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/operator.py @@ -26,6 +26,7 @@ import ifcopenshell.api import blenderbim.bim.helper from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.georeference.data import Data +from ifcopenshell.api.unit.data import Data as UnitData from math import radians, degrees, atan, tan, cos, sin @@ -37,36 +38,15 @@ class EnableEditingGeoreferencing(bpy.types.Operator): def execute(self, context): self.file = IfcStore.get_file() props = context.scene.BIMGeoreferenceProperties + self.props = props props.projected_crs.clear() - - for attribute in IfcStore.get_schema().declaration_by_name("IfcProjectedCRS").all_attributes(): - data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) - if data_type == "entity": - 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() - new.data_type = data_type - if data_type == "string": - new.string_value = "" if new.is_null else Data.projected_crs[attribute.name()] - elif data_type == "float": - new.float_value = 0.0 if new.is_null else Data.projected_crs[attribute.name()] - elif data_type == "integer": - new.int_value = 0 if new.is_null else Data.projected_crs[attribute.name()] - elif data_type == "boolean": - new.bool_value = False if new.is_null else Data.projected_crs[attribute.name()] - 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"] + blenderbim.bim.helper.import_attributes( + "IfcProjectedCRS", + props.projected_crs, + Data.projected_crs, + self.import_projected_crs_attributes) props.map_conversion.clear() blenderbim.bim.helper.import_attributes( @@ -81,6 +61,20 @@ class EnableEditingGeoreferencing(bpy.types.Operator): props.is_editing = True return {"FINISHED"} + def import_projected_crs_attributes(self, name, prop, data): + if name == "MapUnit": + new = self.props.projected_crs.add() + new.name = name + new.data_type = "enum" + new.is_null = data[name] is None + new.is_optional = True + new.enum_items = json.dumps({ + u["id"]: u["Name"] + for u in UnitData.units.values() + if u["UnitType"] == "LENGTHUNIT" + }) + return True + def import_map_conversion_attributes(self, name, prop, data): if name not in ["SourceCRS", "TargetCRS"]: # Enforce a string data type to prevent data loss in single-precision Blender props @@ -88,6 +82,7 @@ class EnableEditingGeoreferencing(bpy.types.Operator): prop.string_value = "" if prop.is_null else str(data[name]) return True + class DisableEditingGeoreferencing(bpy.types.Operator): bl_idname = "bim.disable_editing_georeferencing" bl_label = "Disable Editing Georeferencing" @@ -111,20 +106,15 @@ class EditGeoreferencing(bpy.types.Operator): self.file = IfcStore.get_file() props = context.scene.BIMGeoreferenceProperties - projected_crs = blenderbim.bim.helper.export_attributes(props.projected_crs) - - 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 - - map_conversion = blenderbim.bim.helper.export_attributes(props.map_conversion, self.export_attributes) + projected_crs = blenderbim.bim.helper.export_attributes(props.projected_crs, self.export_crs_attributes) + map_conversion = blenderbim.bim.helper.export_attributes(props.map_conversion, self.export_map_attributes) true_north = None if props.has_true_north: try: true_north = [float(props.true_north_abscissa), float(props.true_north_ordinate)] - except: - pass + except ValueError: + self.report({"ERROR"}, "True North Abscissa and Ordinate expect a number") ifcopenshell.api.run( "georeference.edit_georeferencing", @@ -132,20 +122,24 @@ class EditGeoreferencing(bpy.types.Operator): **{ "map_conversion": map_conversion, "projected_crs": projected_crs, - "map_unit": map_unit, "true_north": true_north, } ) - Data.load(IfcStore.get_file()) + Data.load(self.file) bpy.ops.bim.disable_editing_georeferencing() return {"FINISHED"} - def export_attributes(self, attributes, prop): + def export_map_attributes(self, attributes, prop): if not prop.is_null and prop.data_type == "string": # We store our floats as string to prevent single precision data loss attributes[prop.name] = float(prop.string_value) return True + def export_crs_attributes(self, attributes, prop): + if not prop.is_null and prop.name == "MapUnit": + attributes[prop.name] = self.file.by_id(int(prop.enum_value)) + return True + class SetBlenderGridNorth(bpy.types.Operator): bl_idname = "bim.set_blender_grid_north" diff --git a/src/blenderbim/blenderbim/bim/module/georeference/prop.py b/src/blenderbim/blenderbim/bim/module/georeference/prop.py index 900a1bdaaa..00421994de 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/prop.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/prop.py @@ -36,22 +36,6 @@ 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", description="Formatted \"x,y,z\" (without quotes)") coordinate_output: StringProperty(name="Coordinate Output", description="Formatted \"x,y,z\" (without quotes)") has_blender_offset: BoolProperty(name="Has Blender Offset") diff --git a/src/blenderbim/blenderbim/bim/module/georeference/ui.py b/src/blenderbim/blenderbim/bim/module/georeference/ui.py index cf5a9e8481..9e895d36c1 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/ui.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/ui.py @@ -56,14 +56,6 @@ class BIM_PT_gis(Panel): draw_attributes(props.projected_crs, self.layout) - 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="") - row = self.layout.row() row.label(text="Map Conversion", icon="GRID") From 75a8e4af026e5dc20766763754bfc2700df64618 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 27 Aug 2021 18:21:11 +1000 Subject: [PATCH 027/113] Minor fix for #1699 --- .../bim/module/georeference/operator.py | 22 ++++++------- .../api/georeference/edit_georeferencing.py | 31 ------------------- 2 files changed, 9 insertions(+), 44 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/georeference/operator.py b/src/blenderbim/blenderbim/bim/module/georeference/operator.py index 9c2f64f0eb..5b3e09037d 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/operator.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/operator.py @@ -1,4 +1,3 @@ - # BlenderBIM Add-on - OpenBIM Blender Add-on # Copyright (C) 2020, 2021 Dion Moult # @@ -43,10 +42,8 @@ class EnableEditingGeoreferencing(bpy.types.Operator): props.projected_crs.clear() blenderbim.bim.helper.import_attributes( - "IfcProjectedCRS", - props.projected_crs, - Data.projected_crs, - self.import_projected_crs_attributes) + "IfcProjectedCRS", props.projected_crs, Data.projected_crs, self.import_projected_crs_attributes + ) props.map_conversion.clear() blenderbim.bim.helper.import_attributes( @@ -68,15 +65,15 @@ class EnableEditingGeoreferencing(bpy.types.Operator): new.data_type = "enum" new.is_null = data[name] is None new.is_optional = True - new.enum_items = json.dumps({ - u["id"]: u["Name"] - for u in UnitData.units.values() - if u["UnitType"] == "LENGTHUNIT" - }) + new.enum_items = json.dumps( + {u["id"]: u["Name"] for u in UnitData.units.values() if u["UnitType"] == "LENGTHUNIT"} + ) + if data["MapUnit"]: + new.enum_value = str(data["MapUnit"]["id"]) return True def import_map_conversion_attributes(self, name, prop, data): - if name not in ["SourceCRS", "TargetCRS"]: + if name not in ["SourceCRS", "TargetCRS"]: # Enforce a string data type to prevent data loss in single-precision Blender props prop.data_type = "string" prop.string_value = "" if prop.is_null else str(data[name]) @@ -285,8 +282,7 @@ class ConvertGlobalToLocal(bpy.types.Operator): def poll(cls, context): file = IfcStore.get_file() props = context.scene.BIMGeoreferenceProperties - return file and file.by_type("IfcUnitAssignment") \ - and props.coordinate_input.count(",") == 2 + return file and file.by_type("IfcUnitAssignment") and props.coordinate_input.count(",") == 2 def execute(self, context): if not Data.is_loaded: diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py index e4ac84221a..f1806b4496 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py @@ -20,39 +20,8 @@ class Usecase: 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) self.set_true_north() - 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), - ) - def set_true_north(self): if self.settings["true_north"] == []: return From a4b4c7b37cde41fa5f895ca76af54eb50b5a6722 Mon Sep 17 00:00:00 2001 From: Gorgious Date: Fri, 27 Aug 2021 10:38:19 +0200 Subject: [PATCH 028/113] Minor fix for https://github.com/IfcOpenShell/IfcOpenShell/pull/1699 --- .../ifcopenshell/api/georeference/edit_georeferencing.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py index f1806b4496..92048230d1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py @@ -1,4 +1,3 @@ -import ifcopenshell.util.unit class Usecase: @@ -8,7 +7,6 @@ class Usecase: "map_conversion": {}, "projected_crs": {}, "true_north": [], - "map_unit": "", } for key, value in settings.items(): self.settings[key] = value From 126bac462d9bbb77da8c6d00c51c95d9766fb6e1 Mon Sep 17 00:00:00 2001 From: Kristoffer Andersen Date: Fri, 27 Aug 2021 12:30:39 +0200 Subject: [PATCH 029/113] Add basic BCF pypi upload CI action. (#1701) * Add basic pypi upload CI action. For now I propose a simple trigger using a commit tag "[bcf release]". I think this is just as well since we do not (yet) have an automatic versioning for the bcf package * add correct subfolder (point to bcf) to ci recipe --- .github/workflows/ci-bcf.yml | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/ci-bcf.yml diff --git a/.github/workflows/ci-bcf.yml b/.github/workflows/ci-bcf.yml new file mode 100644 index 0000000000..8d689cc2fd --- /dev/null +++ b/.github/workflows/ci-bcf.yml @@ -0,0 +1,38 @@ +name: ci-bcf + +on: + push: + +jobs: + activate: + runs-on: ubuntu-latest + if: | + github.repository == 'IfcOpenShell/IfcOpenShell' && + contains(github.event.head_commit.message, '[bcf release]') + steps: + - run: echo ok go + upload: + needs: activate + name: Upload BCF package to Pypi + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Build package + run: | + cd src/bcf + pip install build + python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_TOKEN }} + packages_dir: src/bcf/dist + + + + + From faaeef92de4d6f301992e28734e0c2a4d0613ccb Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 28 Aug 2021 14:38:10 +1000 Subject: [PATCH 030/113] You can now edit resource base quantity --- .../bim/module/resource/__init__.py | 6 + .../bim/module/resource/operator.py | 104 ++++++++++++++++++ .../blenderbim/bim/module/resource/prop.py | 24 +++- .../blenderbim/bim/module/resource/ui.py | 36 ++++++ .../api/resource/add_resource_quantity.py | 18 +++ .../ifcopenshell/api/resource/data.py | 3 +- .../api/resource/edit_resource_quantity.py | 10 ++ .../api/resource/remove_resource_quantity.py | 15 +++ 8 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py create mode 100644 src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py create mode 100644 src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py diff --git a/src/blenderbim/blenderbim/bim/module/resource/__init__.py b/src/blenderbim/blenderbim/bim/module/resource/__init__.py index 39a17db65c..a1a7adda0f 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/resource/__init__.py @@ -26,21 +26,27 @@ classes = ( operator.EnableEditingResource, operator.LoadResources, operator.AddResource, + operator.AddResourceQuantity, operator.EditResource, operator.RemoveResource, + operator.RemoveResourceQuantity, operator.LoadResourceProperties, operator.ExpandResource, operator.ContractResource, operator.AssignResource, operator.UnassignResource, operator.EnableEditingResourceTime, + operator.EnableEditingResourceQuantity, + operator.EnableEditingResourceBaseQuantity, operator.EnableEditingResourceCosts, operator.EnableEditingResourceCostValueFormula, operator.EnableEditingResourceCostValue, operator.EditResourceTime, + operator.EditResourceQuantity, operator.EditResourceCostValue, operator.EditResourceCostValueFormula, operator.DisableEditingResourceTime, + operator.DisableEditingResourceQuantity, operator.DisableEditingResourceCostValue, operator.CalculateResourceWork, prop.Resource, diff --git a/src/blenderbim/blenderbim/bim/module/resource/operator.py b/src/blenderbim/blenderbim/bim/module/resource/operator.py index 547dd64396..412101ef71 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/operator.py +++ b/src/blenderbim/blenderbim/bim/module/resource/operator.py @@ -573,3 +573,107 @@ class EditResourceCostValue(bpy.types.Operator): return True if prop.name == "UnitBasisUnit": return True + + +class EnableEditingResourceBaseQuantity(bpy.types.Operator): + bl_idname = "bim.enable_editing_resource_base_quantity" + bl_label = "Enable Editing Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMResourceProperties + props.active_resource_id = self.resource + props.editing_resource_type = "QUANTITY" + return {"FINISHED"} + + +class AddResourceQuantity(bpy.types.Operator): + bl_idname = "bim.add_resource_quantity" + bl_label = "Add Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + ifc_class: bpy.props.StringProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "resource.add_resource_quantity", + self.file, + resource=self.file.by_id(self.resource), + ifc_class=self.ifc_class, + ) + Data.load(self.file) + return {"FINISHED"} + + +class RemoveResourceQuantity(bpy.types.Operator): + bl_idname = "bim.remove_resource_quantity" + bl_label = "Remove Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "resource.remove_resource_quantity", + self.file, + resource=self.file.by_id(self.resource), + ) + Data.load(self.file) + return {"FINISHED"} + + +class EnableEditingResourceQuantity(bpy.types.Operator): + bl_idname = "bim.enable_editing_resource_quantity" + bl_label = "Enable Editing Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + + def execute(self, context): + self.props = context.scene.BIMResourceProperties + self.props.quantity_attributes.clear() + self.props.is_editing_quantity = True + data = Data.resources[self.resource]["BaseQuantity"] + blenderbim.bim.helper.import_attributes(data["type"], self.props.quantity_attributes, data) + return {"FINISHED"} + + +class DisableEditingResourceQuantity(bpy.types.Operator): + bl_idname = "bim.disable_editing_resource_quantity" + bl_label = "Disable Editing Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + props = context.scene.BIMResourceProperties + props.is_editing_quantity = False + return {"FINISHED"} + + +class EditResourceQuantity(bpy.types.Operator): + bl_idname = "bim.edit_resource_quantity" + bl_label = "Edit Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + physical_quantity: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + props = context.scene.BIMResourceProperties + attributes = blenderbim.bim.helper.export_attributes(props.quantity_attributes) + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "resource.edit_resource_quantity", + self.file, + **{"physical_quantity": self.file.by_id(self.physical_quantity), "attributes": attributes}, + ) + Data.load(IfcStore.get_file()) + bpy.ops.bim.disable_editing_resource_quantity() + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/resource/prop.py b/src/blenderbim/blenderbim/bim/module/resource/prop.py index f9f436f6f4..178171cd37 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/prop.py +++ b/src/blenderbim/blenderbim/bim/module/resource/prop.py @@ -1,4 +1,3 @@ - # BlenderBIM Add-on - OpenBIM Blender Add-on # Copyright (C) 2020, 2021 Dion Moult # @@ -35,6 +34,14 @@ from bpy.props import ( ) +quantitytypes_enum = [] + + +def purge(): + global quantitytypes_enum + quantitytypes_enum = [] + + def updateResourceName(self, context): props = context.scene.BIMResourceProperties if not props.is_resource_update_enabled or self.name == "Unnamed": @@ -51,6 +58,18 @@ def updateResourceName(self, context): attribute.string_value = self.name +def get_quantity_types(self, context): + global quantitytypes_enum + if len(quantitytypes_enum) == 0 and IfcStore.get_schema(): + quantitytypes_enum.extend( + [ + (t.name(), t.name(), "") + for t in IfcStore.get_schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes() + ] + ) + return quantitytypes_enum + + class Resource(PropertyGroup): name: StringProperty(name="Name", update=updateResourceName) ifc_definition_id: IntProperty(name="IFC Definition ID") @@ -87,3 +106,6 @@ class BIMResourceProperties(PropertyGroup): cost_value_editing_type: StringProperty(name="Cost Value Editing Type") cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute) cost_value_formula: StringProperty(name="Cost Value Formula") + quantity_types: EnumProperty(items=get_quantity_types, name="Quantity Types") + is_editing_quantity: BoolProperty(name="Is Editing Quantity") + quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute) diff --git a/src/blenderbim/blenderbim/bim/module/resource/ui.py b/src/blenderbim/blenderbim/bim/module/resource/ui.py index 730133be72..6a73a7cf0a 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/ui.py +++ b/src/blenderbim/blenderbim/bim/module/resource/ui.py @@ -64,6 +64,8 @@ class BIM_PT_resources(Panel): if self.props.active_resource_id and self.props.editing_resource_type == "ATTRIBUTES": self.draw_editable_resource_attributes_ui() + elif self.props.active_resource_id and self.props.editing_resource_type == "QUANTITY": + self.draw_editable_resource_quantity_ui() elif self.props.active_resource_id and self.props.editing_resource_type == "COSTS": self.draw_editable_resource_costs_ui() elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE": @@ -111,6 +113,8 @@ class BIM_PT_resources(Panel): row.operator("bim.disable_editing_resource_time", text="", icon="CANCEL") elif self.props.active_resource_id == ifc_definition_id and self.props.editing_resource_type == "COSTS": row.operator("bim.disable_editing_resource", text="", icon="CANCEL") + elif self.props.active_resource_id == ifc_definition_id and self.props.editing_resource_type == "QUANTITY": + row.operator("bim.disable_editing_resource", text="", icon="CANCEL") elif self.props.active_resource_id: row.operator("bim.add_resource", text="", icon="ADD").resource = ifc_definition_id row.operator("bim.remove_resource", text="", icon="X").resource = ifc_definition_id @@ -119,6 +123,8 @@ class BIM_PT_resources(Panel): op = row.operator("bim.calculate_resource_work", text="", icon="TEMP") op.resource = ifc_definition_id row.operator("bim.enable_editing_resource_time", text="", icon="TIME").resource = ifc_definition_id + op = row.operator("bim.enable_editing_resource_base_quantity", text="", icon="PROPERTIES") + op.resource = ifc_definition_id op = row.operator("bim.enable_editing_resource_costs", text="", icon="DISC") op.resource = ifc_definition_id row.operator("bim.enable_editing_resource", text="", icon="GREASEPENCIL").resource = ifc_definition_id @@ -130,6 +136,36 @@ class BIM_PT_resources(Panel): def draw_editable_resource_time_attributes_ui(self): blenderbim.bim.helper.draw_attributes(self.props.resource_time_attributes, self.layout) + def draw_editable_resource_quantity_ui(self): + resource = Data.resources[self.props.active_resource_id] + + if resource["BaseQuantity"]: + quantity = resource["BaseQuantity"] + value = quantity[[k for k in quantity.keys() if "Value" in k][0]] + row = self.layout.row(align=True) + row.label(text=quantity["Name"]) + row.label(text="{0:.2f}".format(value)) + if self.props.is_editing_quantity: + op = row.operator("bim.edit_resource_quantity", text="", icon="CHECKMARK") + op.physical_quantity = quantity["id"] + row.operator("bim.disable_editing_resource_quantity", text="", icon="CANCEL") + else: + op = row.operator("bim.enable_editing_resource_quantity", text="", icon="GREASEPENCIL") + op.resource = self.props.active_resource_id + op = row.operator("bim.remove_resource_quantity", text="", icon="X") + op.resource = self.props.active_resource_id + + if self.props.is_editing_quantity: + box = self.layout.box() + blenderbim.bim.helper.draw_attributes(self.props.quantity_attributes, box) + else: + row = self.layout.row(align=True) + row.prop(self.props, "quantity_types", text="") + op = row.operator("bim.add_resource_quantity", text="", icon="ADD") + op.resource = self.props.active_resource_id + op.ifc_class = self.props.quantity_types + + def draw_editable_resource_costs_ui(self): row = self.layout.row(align=True) row.prop(self.props, "cost_types", text="") diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py new file mode 100644 index 0000000000..6549e6d92c --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py @@ -0,0 +1,18 @@ +import ifcopenshell.util.element + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"resource": None, "ifc_class": "IfcQuantityCount"} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + quantity = self.file.create_entity(self.settings["ifc_class"], Name="Unnamed") + quantity[3] = 0.0 + old_quantity = self.settings["resource"].BaseQuantity + self.settings["resource"].BaseQuantity = quantity + if old_quantity: + ifcopenshell.util.element.remove_deep(self.file, old_quantity) + return quantity diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/data.py b/src/ifcopenshell-python/ifcopenshell/api/resource/data.py index fae5c37779..462e310840 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/data.py @@ -46,7 +46,8 @@ class Data(CostValueTrait): data["Usage"] = data["Usage"].id() data["TotalCostQuantity"] = cls.get_total_quantity(resource) if resource.BaseQuantity: - data["BaseQuantity"] = resource.BaseQuantity.id() + data["BaseQuantity"] = resource.BaseQuantity.get_info() + del data["BaseQuantity"]["Unit"] if resource.BaseCosts: data["BaseCosts"] = [e.id() for e in resource.BaseCosts] cls.load_cost_values(resource, data) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py new file mode 100644 index 0000000000..bbf845d1f7 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py @@ -0,0 +1,10 @@ +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"physical_quantity": 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["physical_quantity"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py new file mode 100644 index 0000000000..ef16cc5669 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py @@ -0,0 +1,15 @@ +import ifcopenshell.util.element + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"resource": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + old_quantity = self.settings["resource"].BaseQuantity + self.settings["resource"].BaseQuantity = None + if old_quantity: + ifcopenshell.util.element.remove_deep(self.file, old_quantity) From c726e8feb995f1de95316008b19c6d0ae04effad Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 28 Aug 2021 18:21:22 +1000 Subject: [PATCH 031/113] You can now calculate costs from direct resources and indirect task-based resources --- .../blenderbim/bim/module/cost/__init__.py | 1 + .../blenderbim/bim/module/cost/operator.py | 18 ++++++ .../blenderbim/bim/module/cost/ui.py | 3 + .../calculate_cost_item_resource_value.py | 56 +++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py diff --git a/src/blenderbim/blenderbim/bim/module/cost/__init__.py b/src/blenderbim/blenderbim/bim/module/cost/__init__.py index b2188023b7..fce0105e0a 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/cost/__init__.py @@ -65,6 +65,7 @@ classes = ( operator.LoadScheduleOfRates, operator.ExpandCostItemRate, operator.ContractCostItemRate, + operator.CalculateCostItemResourceValue, prop.CostItem, prop.CostItemQuantity, prop.CostItemType, diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index c9807fb922..4d08f98010 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -1054,3 +1054,21 @@ class ContractCostItemRate(bpy.types.Operator): props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates) bpy.ops.bim.load_schedule_of_rates(cost_schedule=int(props.schedule_of_rates)) return {"FINISHED"} + + +class CalculateCostItemResourceValue(bpy.types.Operator): + bl_idname = "bim.calculate_cost_item_resource_value" + bl_label = "Calculate Cost Item Resource Value" + bl_options = {"REGISTER", "UNDO"} + cost_item: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "cost.calculate_cost_item_resource_value", self.file, cost_item=self.file.by_id(self.cost_item) + ) + Data.load(self.file) + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index d2062d98ad..c6512fdbbc 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -432,6 +432,9 @@ class BIM_PT_cost_item_quantities(Panel): row2 = col.row(align=True) row2.label(text="Resources") + op = row2.operator("bim.calculate_cost_item_resource_value", text="", icon="DISC") + op.cost_item = cost_item.ifc_definition_id + rtprops = context.scene.BIMResourceTreeProperties rprops = context.scene.BIMResourceProperties if rtprops.resources and rprops.active_resource_index < len(rtprops.resources): diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py new file mode 100644 index 0000000000..98423308f7 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py @@ -0,0 +1,56 @@ +import ifcopenshell.api +import ifcopenshell.util.date + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"cost_item": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + for cost_value in self.settings["cost_item"].CostValues or []: + ifcopenshell.api.run( + "cost.remove_cost_value", self.file, parent=self.settings["cost_item"], cost_value=cost_value + ) + + resources = [] + for rel in self.settings["cost_item"].Controls or []: + for related_object in rel.RelatedObjects: + if related_object.is_a("IfcConstructionResource"): + resources.append(related_object) + elif related_object.is_a("IfcTask"): + for rel2 in related_object.OperatesOn or []: + for related_object2 in rel2.RelatedObjects: + if related_object2.is_a("IfcConstructionResource"): + resources.append(related_object2) + + total_cost = 0 + for resource in resources: + cost = self.get_cost(resource) + quantity = self.get_quantity(resource) + if not cost or not quantity: + continue + total_cost += cost * quantity + + if total_cost: + cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.settings["cost_item"]) + cost_value.AppliedValue = self.file.createIfcMonetaryMeasure(total_cost) + + def get_cost(self, resource): + total = 0 + for cost_value in resource.BaseCosts or []: + total += cost_value.AppliedValue.wrappedValue if cost_value.AppliedValue else 0 + return total + + def get_quantity(self, resource): + total = 0 + if resource.BaseQuantity: + return resource.BaseQuantity[3] + if resource.Usage and resource.Usage.ScheduleWork: + # For now we assume either hourly or daily depending on how duration is stored + duration = ifcopenshell.util.date.ifc2datetime(resource.Usage.ScheduleWork) + if duration.days: + return duration.days + return duration.seconds / 60 / 60 From 99d7eaaf70635d6f9c109d91d7a96769a6a3f153 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 28 Aug 2021 19:00:32 +1000 Subject: [PATCH 032/113] You can now insert doors, and we distribute a sample demo and site library --- .../blenderbim/bim/module/model/product.py | 6 +- src/blenderbim/demo-library.blend | Bin 868072 -> 907208 bytes src/blenderbim/generate_demo_library.py | 61 ++-- .../libraries/blenderbim-demo-library.ifc | 196 +++++++++++ .../libraries/blenderbim-site-library.ifc | 310 ++++++++++++++++++ 5 files changed, 542 insertions(+), 31 deletions(-) create mode 100644 src/blenderbim/libraries/blenderbim-demo-library.ifc create mode 100644 src/blenderbim/libraries/blenderbim-site-library.ifc diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index c03062d007..18d075c257 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -121,11 +121,13 @@ class AddTypeInstance(bpy.types.Operator): bpy.ops.bim.assign_type(relating_type=int(tprops.relating_type), related_object=obj.name) if building_obj: - if instance_class == "IfcWindow": - # TODO For now we are hardcoding windows as a prototype + if instance_class in ["IfcWindow", "IfcDoor"]: + # TODO For now we are hardcoding windows and doors as a prototype bpy.ops.bim.add_element_opening( voided_building_element=building_obj.name, filling_building_element=obj.name ) + if instance_class == "IfcDoor": + obj.location[2] = building_obj.location[2] - min([v[2] for v in obj.bound_box]) else: if collection_obj and collection_obj.BIMObjectProperties.ifc_definition_id: obj.location[2] = collection_obj.location[2] - min([v[2] for v in obj.bound_box]) diff --git a/src/blenderbim/demo-library.blend b/src/blenderbim/demo-library.blend index 06132f34b144548d17ea379b55bcb712336bb542..2bd0073cc2da5ac19f99ca279d2b29bc57517e57 100644 GIT binary patch delta 127681 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 C>>r{4 delta 107938 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 V`YF`aeShMlY-!@8LD$~;{{dLfb{YTx diff --git a/src/blenderbim/generate_demo_library.py b/src/blenderbim/generate_demo_library.py index 2911a5fae9..24a1794683 100644 --- a/src/blenderbim/generate_demo_library.py +++ b/src/blenderbim/generate_demo_library.py @@ -33,9 +33,11 @@ class LibraryGenerator: ) ifcopenshell.api.run("unit.assign_unit", self.file, length={"is_metric": True, "raw": "METERS"}) ifcopenshell.api.run("context.add_context", self.file, context="Model") - self.body = ifcopenshell.api.run( - "context.add_context", self.file, context="Model", subcontext="Body", target_view="MODEL_VIEW" - ) + self.representations = { + "body": ifcopenshell.api.run( + "context.add_context", self.file, context="Model", subcontext="Body", target_view="MODEL_VIEW" + ) + } ifcopenshell.api.run("context.add_context", self.file, context="Plan") self.annotation = ifcopenshell.api.run( "context.add_context", self.file, context="Model", subcontext="Annotation", target_view="PLAN_VIEW" @@ -109,7 +111,8 @@ class LibraryGenerator: ) self.create_profile_type("IfcBeamType", "DEMO2", profile) - self.create_window_type("IfcWindowType", "DEMO1") + self.create_type("IfcWindowType", "DEMO1", {"body": "Window"}) + self.create_type("IfcDoorType", "DEMO1", {"body": "Door"}) self.file.write("blenderbim-demo-library.ifc") @@ -132,32 +135,32 @@ class LibraryGenerator: ifcopenshell.api.run("material.assign_profile", self.file, material_profile=material_profile, profile=profile) ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.project) - def create_window_type(self, ifc_class, name): + def create_type(self, ifc_class, name, representations): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class, name=name) - obj = bpy.data.objects.get("Window") - representation = ifcopenshell.api.run( - "geometry.add_representation", - self.file, - context=self.body, - blender_object=obj, - geometry=obj.data, - total_items=max(1, len(obj.material_slots)), - ) - - ifcopenshell.api.run( - "style.assign_representation_styles", - self.file, - **{ - "shape_representation": representation, - "styles": [ - ifcopenshell.api.run("style.add_style", self.file, **self.get_style_settings(s.material)) - for s in obj.material_slots - ], - }, - ) - ifcopenshell.api.run( - "geometry.assign_representation", self.file, product=element, representation=representation - ) + for rep_name, obj_name in representations.items(): + obj = bpy.data.objects.get(obj_name) + representation = ifcopenshell.api.run( + "geometry.add_representation", + self.file, + context=self.representations[rep_name], + blender_object=obj, + geometry=obj.data, + total_items=max(1, len(obj.material_slots)), + ) + ifcopenshell.api.run( + "style.assign_representation_styles", + self.file, + **{ + "shape_representation": representation, + "styles": [ + ifcopenshell.api.run("style.add_style", self.file, **self.get_style_settings(s.material)) + for s in obj.material_slots + ], + }, + ) + ifcopenshell.api.run( + "geometry.assign_representation", self.file, product=element, representation=representation + ) ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.project) def get_style_settings(self, material): diff --git a/src/blenderbim/libraries/blenderbim-demo-library.ifc b/src/blenderbim/libraries/blenderbim-demo-library.ifc new file mode 100644 index 0000000000..85ab063e39 --- /dev/null +++ b/src/blenderbim/libraries/blenderbim-demo-library.ifc @@ -0,0 +1,196 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1'); +FILE_NAME('/dev/null','2021-08-28T18:41:40+10:00',(),(),'IfcOpenShell 0.6.0b0','IfcOpenShell 0.6.0b0','Nobody'); +FILE_SCHEMA(('IFC4')); +ENDSEC; +DATA; +#1=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$); +#2=IFCTELECOMADDRESS(.USERDEFINED.,'The main webpage of the software collection.','WEBPAGE',$,$,$,$,'https://ifcopenshell.org',$); +#3=IFCTELECOMADDRESS(.USERDEFINED.,'The BlenderBIM Add-on webpage of the software collection.','WEBPAGE',$,$,$,$,'https://blenderbim.org',$); +#4=IFCTELECOMADDRESS(.USERDEFINED.,'The source code repository of the software collection.','REPOSITORY',$,$,$,$,'https://github.com/IfcOpenShell/IfcOpenShell.git',$); +#5=IFCORGANIZATION($,'IfcOpenShell','IfcOpenShell is an open source (LGPL) software library that helps users and software developers to work with the IFC file format.',(#1),(#2,#3,#4)); +#6=IFCAPPLICATION(#5,'0.0.210605','BlenderBIM Add-on','BlenderBIM'); +#7=IFCPROJECTLIBRARY('0H_Sa4feT0KveBrtGY7p5V',$,'BlenderBIM Demo Library',$,$,$,$,(#16,#22),#11); +#8=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.); +#9=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.); +#10=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.); +#11=IFCUNITASSIGNMENT((#9,#10,#8)); +#12=IFCCARTESIANPOINT((0.,0.,0.)); +#13=IFCDIRECTION((0.,0.,1.)); +#14=IFCDIRECTION((1.,0.,0.)); +#15=IFCAXIS2PLACEMENT3D(#12,#13,#14); +#16=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#15,$); +#17=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#16,$,.MODEL_VIEW.,$); +#18=IFCCARTESIANPOINT((0.,0.,0.)); +#19=IFCDIRECTION((0.,0.,1.)); +#20=IFCDIRECTION((1.,0.,0.)); +#21=IFCAXIS2PLACEMENT3D(#18,#19,#20); +#22=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',2,1.E-05,#21,$); +#23=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Model',*,*,*,*,#16,$,.PLAN_VIEW.,$); +#24=IFCMATERIAL('Unknown',$,$); +#25=IFCWALLTYPE('2uk$uXokb3GBZVA$vNH_SU',$,'DEMO50',$,$,$,$,$,$,$); +#26=IFCMATERIALLAYERSET((#28),$,$); +#27=IFCRELASSOCIATESMATERIAL('0jpC5URcX7OPayuV7$ohu_',$,$,$,(#25),#26); +#28=IFCMATERIALLAYER(#24,0.05,$,$,$,$,$); +#29=IFCRELDECLARES('1ucSMOcOHFfeZjk8uPttNq',$,$,$,#7,(#52,#30,#58,#91,#46,#86,#63,#25,#42,#142,#100,#38,#71,#96,#34,#67,#81,#76)); +#30=IFCWALLTYPE('14PXQtM7T5J8vNA1dD1dhd',$,'DEMO100',$,$,$,$,$,$,$); +#31=IFCMATERIALLAYERSET((#33),$,$); +#32=IFCRELASSOCIATESMATERIAL('1i8cpFKY1Cz97HIemrGWnd',$,$,$,(#30),#31); +#33=IFCMATERIALLAYER(#24,0.1,$,$,$,$,$); +#34=IFCWALLTYPE('1xhMMJsBf7Tez7_CYWD587',$,'DEMO200',$,$,$,$,$,$,$); +#35=IFCMATERIALLAYERSET((#37),$,$); +#36=IFCRELASSOCIATESMATERIAL('2cwT38GFjB7v7Pqc4KZhZt',$,$,$,(#34),#35); +#37=IFCMATERIALLAYER(#24,0.2,$,$,$,$,$); +#38=IFCWALLTYPE('2r37t_GPTBk9laaiX_nWH1',$,'DEMO300',$,$,$,$,$,$,$); +#39=IFCMATERIALLAYERSET((#41),$,$); +#40=IFCRELASSOCIATESMATERIAL('0qtVmmTMr3yAGWXRPSQqGd',$,$,$,(#38),#39); +#41=IFCMATERIALLAYER(#24,0.3,$,$,$,$,$); +#42=IFCCOVERINGTYPE('3Uk6T5mFn0kg8XYa1At3cI',$,'DEMO10',$,$,$,$,$,$,$); +#43=IFCMATERIALLAYERSET((#45),$,$); +#44=IFCRELASSOCIATESMATERIAL('3569eB$mr0CfLG8NM_SkmT',$,$,$,(#42),#43); +#45=IFCMATERIALLAYER(#24,0.01,$,$,$,$,$); +#46=IFCCOVERINGTYPE('0vHGihDTfAmxoNxodTI6YE',$,'DEMO20',$,$,(#50),$,$,$,$); +#47=IFCMATERIALLAYERSET((#49),$,$); +#48=IFCRELASSOCIATESMATERIAL('1z6TWtxTbDNurEHgAbI44H',$,$,$,(#46),#47); +#49=IFCMATERIALLAYER(#24,0.02,$,$,$,$,$); +#50=IFCPROPERTYSET('07OrawIjLFcBuc8HuMhP34',$,'EPset_Parametric',$,(#51)); +#51=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS2'),$); +#52=IFCCOVERINGTYPE('2S3nmtZdf0TeCEHilgLucE',$,'DEMO30',$,$,(#56),$,$,$,$); +#53=IFCMATERIALLAYERSET((#55),$,$); +#54=IFCRELASSOCIATESMATERIAL('1sBRKybo9BIPbWz0egldJq',$,$,$,(#52),#53); +#55=IFCMATERIALLAYER(#24,0.03,$,$,$,$,$); +#56=IFCPROPERTYSET('3snYH$8sX4o8S5bvtPkDM1',$,'EPset_Parametric',$,(#57)); +#57=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS3'),$); +#58=IFCRAMPTYPE('0v_rACbQDASuK_tBy6uIph',$,'DEMO200',$,$,$,$,$,$,$); +#59=IFCMATERIALLAYERSET((#61),$,$); +#60=IFCRELASSOCIATESMATERIAL('3RVidt7sP7lABIbzFBstcC',$,$,$,(#58),#59); +#61=IFCMATERIALLAYER(#24,0.2,$,$,$,$,$); +#62=IFCCIRCLEPROFILEDEF(.AREA.,$,$,0.3); +#63=IFCPILETYPE('3GVp8yh5X8yeT0TyygKAZO',$,'DEMO1',$,$,$,$,$,$,$); +#64=IFCMATERIALPROFILESET($,$,(#66),$); +#65=IFCRELASSOCIATESMATERIAL('1xWyh_Dfz5R9Kn_odeGRUs',$,$,$,(#63),#64); +#66=IFCMATERIALPROFILE($,$,#24,#62,$,$); +#67=IFCSLABTYPE('0ax0t_7Z12XgneinEO826H',$,'DEMO150',$,$,$,$,$,$,$); +#68=IFCMATERIALLAYERSET((#70),$,$); +#69=IFCRELASSOCIATESMATERIAL('0AdABIDlT3pASYvQRkSeTv',$,$,$,(#67),#68); +#70=IFCMATERIALLAYER(#24,0.2,$,$,$,$,$); +#71=IFCSLABTYPE('0anFF9jm56dv0b2EGcvUI4',$,'DEMO250',$,$,$,$,$,$,$); +#72=IFCMATERIALLAYERSET((#74),$,$); +#73=IFCRELASSOCIATESMATERIAL('2gsu_uFxL8$gABCB9hvaZv',$,$,$,(#71),#72); +#74=IFCMATERIALLAYER(#24,0.3,$,$,$,$,$); +#75=IFCRECTANGLEPROFILEDEF(.AREA.,$,$,0.5,0.6); +#76=IFCCOLUMNTYPE('2teCQbo7591w3zzKEgTGw6',$,'DEMO1',$,$,$,$,$,$,$); +#77=IFCMATERIALPROFILESET($,$,(#79),$); +#78=IFCRELASSOCIATESMATERIAL('3gYaAwqj99fRfLHu900POL',$,$,$,(#76),#77); +#79=IFCMATERIALPROFILE($,$,#24,#75,$,$); +#80=IFCCIRCLEHOLLOWPROFILEDEF(.AREA.,$,$,0.25,0.005); +#81=IFCCOLUMNTYPE('2C46Af2Rf5UR1u2jPH6Hrs',$,'DEMO2',$,$,$,$,$,$,$); +#82=IFCMATERIALPROFILESET($,$,(#84),$); +#83=IFCRELASSOCIATESMATERIAL('1GHUeK7ojBVAHw_J9tytsw',$,$,$,(#81),#82); +#84=IFCMATERIALPROFILE($,$,#24,#80,$,$); +#85=IFCRECTANGLEHOLLOWPROFILEDEF(.AREA.,$,$,0.075,0.15,0.005,0.005,0.005); +#86=IFCCOLUMNTYPE('2nd9Gq0rH5PvqcdIn0o5GE',$,'DEMO3',$,$,$,$,$,$,$); +#87=IFCMATERIALPROFILESET($,$,(#89),$); +#88=IFCRELASSOCIATESMATERIAL('3O8tdK1q9Dc80NeoGA2mc2',$,$,$,(#86),#87); +#89=IFCMATERIALPROFILE($,$,#24,#85,$,$); +#90=IFCISHAPEPROFILEDEF(.AREA.,'DEMO-I',$,0.1,0.2,0.005,0.01,0.005,$,$); +#91=IFCBEAMTYPE('1Qq2BHV_zDHOeQJzV2NFtQ',$,'DEMO1',$,$,$,$,$,$,$); +#92=IFCMATERIALPROFILESET($,$,(#94),$); +#93=IFCRELASSOCIATESMATERIAL('0wKsOvs$jArRBcVacJEmdU',$,$,$,(#91),#92); +#94=IFCMATERIALPROFILE($,$,#24,#90,$,$); +#95=IFCCSHAPEPROFILEDEF(.AREA.,'DEMO-C',$,0.2,0.1,0.0015,0.03,0.005); +#96=IFCBEAMTYPE('0FR2DbWM991Q0wEwhXUtao',$,'DEMO2',$,$,$,$,$,$,$); +#97=IFCMATERIALPROFILESET($,$,(#99),$); +#98=IFCRELASSOCIATESMATERIAL('2P6qEKsav618SE2wpnLhpb',$,$,$,(#96),#97); +#99=IFCMATERIALPROFILE($,$,#24,#95,$,$); +#100=IFCWINDOWTYPE('1lq$4g2j11RfmHPOxojjsF',$,'DEMO1',$,$,$,(#141),$,$,$,$,$,$); +#101=IFCINDEXEDPOLYGONALFACE((13,17,18,14)); +#102=IFCINDEXEDPOLYGONALFACE((5,6,3,4)); +#103=IFCINDEXEDPOLYGONALFACE((7,8,2,1)); +#104=IFCINDEXEDPOLYGONALFACE((6,7,1,3)); +#105=IFCINDEXEDPOLYGONALFACE((8,5,4,2)); +#106=IFCINDEXEDPOLYGONALFACE((12,11,10,9)); +#107=IFCINDEXEDPOLYGONALFACE((15,19,20,16)); +#108=IFCINDEXEDPOLYGONALFACE((14,18,19,15)); +#109=IFCINDEXEDPOLYGONALFACE((16,20,17,13)); +#110=IFCINDEXEDPOLYGONALFACE((4,17,20,2)); +#111=IFCINDEXEDPOLYGONALFACE((2,20,19,1)); +#112=IFCINDEXEDPOLYGONALFACE((8,16,13,5)); +#113=IFCINDEXEDPOLYGONALFACE((7,15,16,8)); +#114=IFCINDEXEDPOLYGONALFACE((1,19,18,3)); +#115=IFCINDEXEDPOLYGONALFACE((3,18,17,4)); +#116=IFCINDEXEDPOLYGONALFACE((6,14,15,7)); +#117=IFCINDEXEDPOLYGONALFACE((5,13,14,6)); +#118=IFCINDEXEDPOLYGONALFACE((24,21,22,23)); +#119=IFCINDEXEDPOLYGONALFACE((11,23,22,10)); +#120=IFCINDEXEDPOLYGONALFACE((10,22,21,9)); +#121=IFCINDEXEDPOLYGONALFACE((9,21,24,12)); +#122=IFCINDEXEDPOLYGONALFACE((12,24,23,11)); +#123=IFCCARTESIANPOINTLIST3D(((0.899999976158142,0.,1.20000004768372),(0.899999976158142,0.,0.),(0.,0.,1.20000004768372),(0.,0.,0.),(0.0999999940395355,0.,0.0999999940395355),(0.0999999940395355,0.,1.10000002384186),(0.800000011920929,0.,1.10000002384186),(0.800000011920929,0.,0.0999999940395355),(0.0999999940395355,0.0199999995529652,0.0999999940395355),(0.0999999940395355,0.0199999995529652,1.10000002384186),(0.800000011920929,0.0199999995529652,1.10000002384186),(0.800000011920929,0.0199999995529652,0.0999999940395355),(0.0999999940395355,0.0500000007450581,0.0999999940395355),(0.0999999940395355,0.0500000007450581,1.10000002384186),(0.800000011920929,0.0500000007450581,1.10000002384186),(0.800000011920929,0.0500000007450581,0.0999999940395355),(0.,0.0500000007450581,0.),(0.,0.0500000007450581,1.20000004768372),(0.899999976158142,0.0500000007450581,1.20000004768372),(0.899999976158142,0.0500000007450581,0.),(0.0999999940395355,0.0299999993294477,0.0999999940395355),(0.0999999940395355,0.0299999993294477,1.10000002384186),(0.800000011920929,0.0299999993294477,1.10000002384186),(0.800000011920929,0.0299999993294477,0.0999999940395355))); +#124=IFCPOLYGONALFACESET(#123,$,(#101,#102,#103,#104,#105,#107,#108,#109,#110,#111,#112,#113,#114,#115,#116,#117),$); +#125=IFCPOLYGONALFACESET(#123,$,(#106,#118,#119,#120,#121,#122),$); +#126=IFCSHAPEREPRESENTATION(#17,'Body','Tessellation',(#124,#125)); +#127=IFCCOLOURRGB($,0.0429765619337559,0.0429765619337559,0.0429765619337559); +#128=IFCCOLOURRGB($,0.0429765619337559,0.0429765619337559,0.0429765619337559); +#129=IFCSURFACESTYLERENDERING(#127,0.,#128,$,$,$,$,$,.NOTDEFINED.); +#130=IFCSURFACESTYLE('Frame',.BOTH.,(#129)); +#131=IFCCOLOURRGB($,0.800000011920929,1.,1.); +#132=IFCCOLOURRGB($,0.800000011920929,1.,1.); +#133=IFCSURFACESTYLERENDERING(#131,0.799999997019768,#132,$,$,$,$,$,.NOTDEFINED.); +#134=IFCSURFACESTYLE('Glass',.BOTH.,(#133)); +#135=IFCSTYLEDITEM(#124,(#130),'Frame'); +#136=IFCSTYLEDITEM(#125,(#134),'Glass'); +#137=IFCCARTESIANPOINT((0.,0.,0.)); +#138=IFCDIRECTION((1.,0.,0.)); +#139=IFCDIRECTION((0.,0.,1.)); +#140=IFCAXIS2PLACEMENT3D(#137,#139,#138); +#141=IFCREPRESENTATIONMAP(#140,#126); +#142=IFCDOORTYPE('02Vmf$GHjA3xWMpsR7HwPP',$,'DEMO1',$,$,$,(#187),$,$,$,$,$,$); +#143=IFCINDEXEDPOLYGONALFACE((17,16,15,14)); +#144=IFCINDEXEDPOLYGONALFACE((2,3,29,28)); +#145=IFCINDEXEDPOLYGONALFACE((27,28,29,30,32,31)); +#146=IFCINDEXEDPOLYGONALFACE((7,6,5,3)); +#147=IFCINDEXEDPOLYGONALFACE((8,7,3,2)); +#148=IFCINDEXEDPOLYGONALFACE((23,24,6,7)); +#149=IFCINDEXEDPOLYGONALFACE((21,20,4,24,23,22)); +#150=IFCINDEXEDPOLYGONALFACE((12,13,14,15)); +#151=IFCINDEXEDPOLYGONALFACE((16,19,12,15)); +#152=IFCINDEXEDPOLYGONALFACE((11,10,25,26)); +#153=IFCINDEXEDPOLYGONALFACE((25,1,27,31)); +#154=IFCINDEXEDPOLYGONALFACE((24,4,11,6)); +#155=IFCINDEXEDPOLYGONALFACE((20,21,9,10)); +#156=IFCINDEXEDPOLYGONALFACE((9,8,2,1)); +#157=IFCINDEXEDPOLYGONALFACE((10,9,1,25)); +#158=IFCINDEXEDPOLYGONALFACE((22,23,7,8)); +#159=IFCINDEXEDPOLYGONALFACE((4,20,10,11)); +#160=IFCINDEXEDPOLYGONALFACE((19,16,17,18)); +#161=IFCINDEXEDPOLYGONALFACE((19,18,13,12)); +#162=IFCINDEXEDPOLYGONALFACE((21,22,8,9)); +#163=IFCINDEXEDPOLYGONALFACE((6,11,26,5)); +#164=IFCINDEXEDPOLYGONALFACE((18,17,14,13)); +#165=IFCINDEXEDPOLYGONALFACE((5,26,32,30)); +#166=IFCINDEXEDPOLYGONALFACE((1,2,28,27)); +#167=IFCINDEXEDPOLYGONALFACE((26,25,31,32)); +#168=IFCINDEXEDPOLYGONALFACE((3,5,30,29)); +#169=IFCCARTESIANPOINTLIST3D(((0.955000162124634,0.0999999940395355,2.09000015258789),(0.955000162124634,0.0450000055134296,2.09000015258789),(0.970000028610229,0.0450000017881393,2.10500001907349),(0.,0.,0.),(0.970000028610229,0.,2.10500001907349),(0.0399999991059303,0.,2.10500001907349),(0.0399999991059303,0.0450000017881393,2.10500001907349),(0.0549999997019768,0.0450000055134296,2.09000015258789),(0.0549999997019768,0.0999999940395355,2.09000015258789),(0.,0.0999999940395355,2.14500021934509),(0.,-3.72529029846191E-09,2.14500021934509),(0.044999998062849,0.,2.09999990463257),(0.044999998062849,0.0399999991059303,2.09999990463257),(0.965000033378601,0.0399999991059303,2.09999990463257),(0.965000033378601,0.,2.09999990463257),(0.965000033378601,0.,0.),(0.965000033378601,0.0399999991059303,0.),(0.044999998062849,0.0399999991059303,0.),(0.044999998062849,0.,0.),(0.,0.0999999940395355,0.),(0.0549999997019768,0.0999999940395355,0.),(0.0549999997019768,0.0450000017881393,0.),(0.0399999991059303,0.0450000017881393,0.),(0.0399999991059303,0.,0.),(1.01000034809113,0.0999999940395355,2.14500021934509),(1.01000034809113,-3.72529029846191E-09,2.14500021934509),(0.955000162124634,0.0999999940395355,0.),(0.955000162124634,0.0450000055134296,0.),(0.970000028610229,0.0450000017881393,0.),(0.970000028610229,0.,0.),(1.01000034809113,0.0999999940395355,0.),(1.01000034809113,-3.72529029846191E-09,0.))); +#170=IFCPOLYGONALFACESET(#169,$,(#144,#145,#146,#147,#148,#149,#152,#153,#154,#155,#156,#157,#158,#159,#162,#163,#165,#166,#167,#168),$); +#171=IFCPOLYGONALFACESET(#169,$,(#143,#150,#151,#160,#161,#164),$); +#172=IFCSHAPEREPRESENTATION(#17,'Body','Tessellation',(#170,#171)); +#173=IFCCOLOURRGB($,0.0429765619337559,0.0429765619337559,0.0429765619337559); +#174=IFCCOLOURRGB($,0.0429765619337559,0.0429765619337559,0.0429765619337559); +#175=IFCSURFACESTYLERENDERING(#173,0.,#174,$,$,$,$,$,.NOTDEFINED.); +#176=IFCSURFACESTYLE('Frame',.BOTH.,(#175)); +#177=IFCCOLOURRGB($,0.184475064277649,0.184475019574165,0.184475019574165); +#178=IFCCOLOURRGB($,0.184475064277649,0.184475019574165,0.184475019574165); +#179=IFCSURFACESTYLERENDERING(#177,0.,#178,$,$,$,$,$,.NOTDEFINED.); +#180=IFCSURFACESTYLE('Panel',.BOTH.,(#179)); +#181=IFCSTYLEDITEM(#170,(#176),'Frame'); +#182=IFCSTYLEDITEM(#171,(#180),'Panel'); +#183=IFCCARTESIANPOINT((0.,0.,0.)); +#184=IFCDIRECTION((1.,0.,0.)); +#185=IFCDIRECTION((0.,0.,1.)); +#186=IFCAXIS2PLACEMENT3D(#183,#185,#184); +#187=IFCREPRESENTATIONMAP(#186,#172); +ENDSEC; +END-ISO-10303-21; diff --git a/src/blenderbim/libraries/blenderbim-site-library.ifc b/src/blenderbim/libraries/blenderbim-site-library.ifc new file mode 100644 index 0000000000..ff730286b5 --- /dev/null +++ b/src/blenderbim/libraries/blenderbim-site-library.ifc @@ -0,0 +1,310 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1'); +FILE_NAME('/dev/null','2021-08-19T09:05:27+10:00',(),(),'IfcOpenShell 0.6.0b0','IfcOpenShell 0.6.0b0','Nobody'); +FILE_SCHEMA(('IFC4')); +ENDSEC; +DATA; +#1=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$); +#2=IFCTELECOMADDRESS(.USERDEFINED.,'The main webpage of the software collection.','WEBPAGE',$,$,$,$,'https://ifcopenshell.org',$); +#3=IFCTELECOMADDRESS(.USERDEFINED.,'The BlenderBIM Add-on webpage of the software collection.','WEBPAGE',$,$,$,$,'https://blenderbim.org',$); +#4=IFCTELECOMADDRESS(.USERDEFINED.,'The source code repository of the software collection.','REPOSITORY',$,$,$,$,'https://github.com/IfcOpenShell/IfcOpenShell.git',$); +#5=IFCORGANIZATION($,'IfcOpenShell','IfcOpenShell is an open source (LGPL) software library that helps users and software developers to work with the IFC file format.',(#1),(#2,#3,#4)); +#6=IFCAPPLICATION(#5,'0.0.210605','BlenderBIM Add-on','BlenderBIM'); +#7=IFCPROJECTLIBRARY('0mpkadAGT22uMl6KAQpRSr',$,'BlenderBIM Demo Library',$,$,$,$,(#16,#23),#11); +#8=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.); +#9=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.); +#10=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.); +#11=IFCUNITASSIGNMENT((#10,#8,#9)); +#12=IFCCARTESIANPOINT((0.,0.,0.)); +#13=IFCDIRECTION((0.,0.,1.)); +#14=IFCDIRECTION((1.,0.,0.)); +#15=IFCAXIS2PLACEMENT3D(#12,#13,#14); +#16=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#15,$); +#17=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#16,$,.MODEL_VIEW.,$); +#18=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Clearance','Model',*,*,*,*,#16,$,.MODEL_VIEW.,$); +#19=IFCCARTESIANPOINT((0.,0.,0.)); +#20=IFCDIRECTION((0.,0.,1.)); +#21=IFCDIRECTION((1.,0.,0.)); +#22=IFCAXIS2PLACEMENT3D(#19,#20,#21); +#23=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',2,1.E-05,#22,$); +#24=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Model',*,*,*,*,#16,$,.PLAN_VIEW.,$); +#25=IFCBUILDINGELEMENTPROXYTYPE('37jYCioI947vRuedg_RiPP',$,'Site Shed 3x6m',$,$,$,(#56),$,$,$); +#26=IFCINDEXEDPOLYGONALFACE((11,5,7,9)); +#27=IFCINDEXEDPOLYGONALFACE((13,7,5,15)); +#28=IFCINDEXEDPOLYGONALFACE((12,1,3,14)); +#29=IFCINDEXEDPOLYGONALFACE((6,2,4,10,20,8)); +#30=IFCINDEXEDPOLYGONALFACE((1,11,9,3)); +#31=IFCINDEXEDPOLYGONALFACE((6,15,16,12,2)); +#32=IFCINDEXEDPOLYGONALFACE((8,13,15,6)); +#33=IFCINDEXEDPOLYGONALFACE((14,3,9,7,13,17)); +#34=IFCINDEXEDPOLYGONALFACE((4,14,17,13,8,20,19,18,10)); +#35=IFCINDEXEDPOLYGONALFACE((10,18,19,20)); +#36=IFCINDEXEDPOLYGONALFACE((15,5,11,1,12,16)); +#37=IFCINDEXEDPOLYGONALFACE((2,12,14,4)); +#38=IFCCARTESIANPOINTLIST3D(((6.,3.,2.60000014305115),(6.,3.,0.),(6.,0.,2.60000014305115),(6.,0.,0.),(0.,3.,2.60000014305115),(0.,3.,0.),(0.,0.,2.60000014305115),(0.,0.,0.),(3.,0.,2.70000004768372),(1.92000007629395,0.,0.),(3.,3.,2.70000004768372),(6.,3.,2.39861273765564),(0.,0.,2.39861273765564),(6.,0.,2.39861273765564),(0.,3.,2.39861273765564),(3.,3.,2.49861264228821),(3.,0.,2.49861264228821),(1.92000007629395,0.,2.09999990463257),(1.,0.,2.09999990463257),(1.,0.,0.))); +#39=IFCPOLYGONALFACESET(#38,$,(#29,#31,#32,#34,#37),$); +#40=IFCPOLYGONALFACESET(#38,$,(#26,#27,#28,#30,#33,#35,#36),$); +#41=IFCSHAPEREPRESENTATION(#17,'Body','Tessellation',(#39,#40)); +#42=IFCCOLOURRGB($,1.,1.,1.); +#43=IFCCOLOURRGB($,1.,1.,1.); +#44=IFCSURFACESTYLERENDERING(#42,0.,#43,$,$,$,$,$,.NOTDEFINED.); +#45=IFCSURFACESTYLE('Site Shed White',.BOTH.,(#44)); +#46=IFCCOLOURRGB($,0.0500000007450581,0.100000001490116,0.129999995231628); +#47=IFCCOLOURRGB($,0.0500000007450581,0.100000001490116,0.129999995231628); +#48=IFCSURFACESTYLERENDERING(#46,0.,#47,$,$,$,$,$,.NOTDEFINED.); +#49=IFCSURFACESTYLE('Site Shed Grey',.BOTH.,(#48)); +#50=IFCSTYLEDITEM(#39,(#45),'Site Shed White'); +#51=IFCSTYLEDITEM(#40,(#49),'Site Shed Grey'); +#52=IFCCARTESIANPOINT((0.,0.,0.)); +#53=IFCDIRECTION((1.,0.,0.)); +#54=IFCDIRECTION((0.,0.,1.)); +#55=IFCAXIS2PLACEMENT3D(#52,#54,#53); +#56=IFCREPRESENTATIONMAP(#55,#41); +#57=IFCRELDECLARES('2lk0Gj9bT7jxjNfkXFFnbZ',$,$,$,#7,(#90,#25,#58)); +#58=IFCBUILDINGELEMENTPROXYTYPE('1RHGC2F1vD2PG0DPCm4zwr',$,'Site Shed 3x12m',$,$,$,(#89),$,$,$); +#59=IFCINDEXEDPOLYGONALFACE((11,5,7,9)); +#60=IFCINDEXEDPOLYGONALFACE((13,7,5,15)); +#61=IFCINDEXEDPOLYGONALFACE((12,1,3,14)); +#62=IFCINDEXEDPOLYGONALFACE((6,2,4,10,20,8)); +#63=IFCINDEXEDPOLYGONALFACE((1,11,9,3)); +#64=IFCINDEXEDPOLYGONALFACE((6,15,16,12,2)); +#65=IFCINDEXEDPOLYGONALFACE((8,13,15,6)); +#66=IFCINDEXEDPOLYGONALFACE((14,3,9,7,13,17)); +#67=IFCINDEXEDPOLYGONALFACE((4,14,17,13,8,20,19,18,10)); +#68=IFCINDEXEDPOLYGONALFACE((10,18,19,20)); +#69=IFCINDEXEDPOLYGONALFACE((15,5,11,1,12,16)); +#70=IFCINDEXEDPOLYGONALFACE((2,12,14,4)); +#71=IFCCARTESIANPOINTLIST3D(((12.,3.,2.60000014305115),(12.,3.,0.),(12.,0.,2.60000014305115),(12.,0.,0.),(0.,3.,2.60000014305115),(0.,3.,0.),(0.,0.,2.60000014305115),(0.,0.,0.),(6.,0.,2.70000004768372),(1.92000007629395,0.,0.),(6.,3.,2.70000004768372),(12.,3.,2.39861273765564),(0.,0.,2.39861273765564),(12.,0.,2.39861273765564),(0.,3.,2.39861273765564),(6.,3.,2.49861264228821),(6.,0.,2.49861264228821),(1.92000007629395,0.,2.09999990463257),(1.,0.,2.09999990463257),(1.,0.,0.))); +#72=IFCPOLYGONALFACESET(#71,$,(#62,#64,#65,#67,#70),$); +#73=IFCPOLYGONALFACESET(#71,$,(#59,#60,#61,#63,#66,#68,#69),$); +#74=IFCSHAPEREPRESENTATION(#17,'Body','Tessellation',(#72,#73)); +#75=IFCCOLOURRGB($,1.,1.,1.); +#76=IFCCOLOURRGB($,1.,1.,1.); +#77=IFCSURFACESTYLERENDERING(#75,0.,#76,$,$,$,$,$,.NOTDEFINED.); +#78=IFCSURFACESTYLE('Site Shed White',.BOTH.,(#77)); +#79=IFCCOLOURRGB($,0.0500000007450581,0.100000001490116,0.129999995231628); +#80=IFCCOLOURRGB($,0.0500000007450581,0.100000001490116,0.129999995231628); +#81=IFCSURFACESTYLERENDERING(#79,0.,#80,$,$,$,$,$,.NOTDEFINED.); +#82=IFCSURFACESTYLE('Site Shed Grey',.BOTH.,(#81)); +#83=IFCSTYLEDITEM(#72,(#78),'Site Shed White'); +#84=IFCSTYLEDITEM(#73,(#82),'Site Shed Grey'); +#85=IFCCARTESIANPOINT((0.,0.,0.)); +#86=IFCDIRECTION((1.,0.,0.)); +#87=IFCDIRECTION((0.,0.,1.)); +#88=IFCAXIS2PLACEMENT3D(#85,#87,#86); +#89=IFCREPRESENTATIONMAP(#88,#74); +#90=IFCBUILDINGELEMENTPROXYTYPE('3gJAB4wo97_eDnssTgTwrK',$,'Mobile Crane 50T',$,$,$,(#226,#301),$,$,$); +#91=IFCINDEXEDPOLYGONALFACE((1,167,166,2)); +#92=IFCINDEXEDPOLYGONALFACE((28,29,30,31,32,33,34,35,36,37,38,27)); +#93=IFCINDEXEDPOLYGONALFACE((16,17,18,19,20,21,22,23,24,25,26,15)); +#94=IFCINDEXEDPOLYGONALFACE((4,5,6,7,8,9,10,11,12,13,14,3)); +#95=IFCINDEXEDPOLYGONALFACE((77,78,80,79)); +#96=IFCINDEXEDPOLYGONALFACE((81,82,84,83)); +#97=IFCINDEXEDPOLYGONALFACE((85,87,88,86)); +#98=IFCINDEXEDPOLYGONALFACE((89,91,92,90)); +#99=IFCINDEXEDPOLYGONALFACE((57,169,168,67,68,69,70,71,64,65)); +#100=IFCINDEXEDPOLYGONALFACE((67,168,60,61,62,40,39,63,64,71,72)); +#101=IFCINDEXEDPOLYGONALFACE((68,67,72,71,70,69)); +#102=IFCINDEXEDPOLYGONALFACE((42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,65,64,63,41)); +#103=IFCINDEXEDPOLYGONALFACE((112,119,118,124,123,122,121,120,170,171)); +#104=IFCINDEXEDPOLYGONALFACE((120,125,124,118,95,93,94,117,116,115,170)); +#105=IFCINDEXEDPOLYGONALFACE((121,122,123,124,125,120)); +#106=IFCINDEXEDPOLYGONALFACE((96,97,95,118,119,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98)); +#107=IFCINDEXEDPOLYGONALFACE((45,100,101,46)); +#108=IFCINDEXEDPOLYGONALFACE((60,115,116,61)); +#109=IFCINDEXEDPOLYGONALFACE((53,108,109,54)); +#110=IFCINDEXEDPOLYGONALFACE((46,101,102,47)); +#111=IFCINDEXEDPOLYGONALFACE((61,116,117,62)); +#112=IFCINDEXEDPOLYGONALFACE((40,94,93,39)); +#113=IFCINDEXEDPOLYGONALFACE((54,109,110,55)); +#114=IFCINDEXEDPOLYGONALFACE((47,102,103,48)); +#115=IFCINDEXEDPOLYGONALFACE((62,117,94,40)); +#116=IFCINDEXEDPOLYGONALFACE((39,93,95,63)); +#117=IFCINDEXEDPOLYGONALFACE((55,110,111,56)); +#118=IFCINDEXEDPOLYGONALFACE((48,103,104,49)); +#119=IFCINDEXEDPOLYGONALFACE((63,95,97,41)); +#120=IFCINDEXEDPOLYGONALFACE((41,97,96,42)); +#121=IFCINDEXEDPOLYGONALFACE((56,111,112,57)); +#122=IFCINDEXEDPOLYGONALFACE((49,104,105,50)); +#123=IFCINDEXEDPOLYGONALFACE((42,96,98,43)); +#124=IFCINDEXEDPOLYGONALFACE((57,112,171,169)); +#125=IFCINDEXEDPOLYGONALFACE((50,105,106,51)); +#126=IFCINDEXEDPOLYGONALFACE((43,98,99,44)); +#127=IFCINDEXEDPOLYGONALFACE((58,113,114,59)); +#128=IFCINDEXEDPOLYGONALFACE((51,106,107,52)); +#129=IFCINDEXEDPOLYGONALFACE((44,99,100,45)); +#130=IFCINDEXEDPOLYGONALFACE((168,170,115,60)); +#131=IFCINDEXEDPOLYGONALFACE((52,107,108,53)); +#132=IFCINDEXEDPOLYGONALFACE((151,150,161,160,159,158,157,156,155,154,153,152)); +#133=IFCINDEXEDPOLYGONALFACE((139,138,149,148,147,146,145,144,143,142,141,140)); +#134=IFCINDEXEDPOLYGONALFACE((127,126,137,136,135,134,133,132,131,130,129,128)); +#135=IFCINDEXEDPOLYGONALFACE((162,163,164,165)); +#136=IFCINDEXEDPOLYGONALFACE((163,2,166,164)); +#137=IFCINDEXEDPOLYGONALFACE((162,1,2,163)); +#138=IFCINDEXEDPOLYGONALFACE((165,167,1,162)); +#139=IFCINDEXEDPOLYGONALFACE((164,166,167,165)); +#140=IFCINDEXEDPOLYGONALFACE((59,168,169)); +#141=IFCINDEXEDPOLYGONALFACE((113,171,170,114)); +#142=IFCINDEXEDPOLYGONALFACE((73,66,76,75,74)); +#143=IFCINDEXEDPOLYGONALFACE((173,174,175,176,172)); +#144=IFCINDEXEDPOLYGONALFACE((74,174,173,73)); +#145=IFCINDEXEDPOLYGONALFACE((66,172,176,76)); +#146=IFCINDEXEDPOLYGONALFACE((75,175,174,74)); +#147=IFCINDEXEDPOLYGONALFACE((73,173,172,66)); +#148=IFCINDEXEDPOLYGONALFACE((76,176,175,75)); +#149=IFCINDEXEDPOLYGONALFACE((177,178,180,179)); +#150=IFCINDEXEDPOLYGONALFACE((181,183,184,182)); +#151=IFCINDEXEDPOLYGONALFACE((178,182,184,180)); +#152=IFCINDEXEDPOLYGONALFACE((177,181,182,178)); +#153=IFCINDEXEDPOLYGONALFACE((179,183,181,177)); +#154=IFCINDEXEDPOLYGONALFACE((180,184,183,179)); +#155=IFCINDEXEDPOLYGONALFACE((186,185,188,187)); +#156=IFCINDEXEDPOLYGONALFACE((190,189,192,191)); +#157=IFCINDEXEDPOLYGONALFACE((92,91,191,192)); +#158=IFCINDEXEDPOLYGONALFACE((87,85,186,187)); +#159=IFCINDEXEDPOLYGONALFACE((88,87,187,188)); +#160=IFCINDEXEDPOLYGONALFACE((90,92,192,189)); +#161=IFCINDEXEDPOLYGONALFACE((89,90,189,190)); +#162=IFCINDEXEDPOLYGONALFACE((86,88,188,185)); +#163=IFCINDEXEDPOLYGONALFACE((85,86,185,186)); +#164=IFCINDEXEDPOLYGONALFACE((91,89,190,191)); +#165=IFCINDEXEDPOLYGONALFACE((193,195,196,194)); +#166=IFCINDEXEDPOLYGONALFACE((198,199,200,197)); +#167=IFCINDEXEDPOLYGONALFACE((78,194,196,80)); +#168=IFCINDEXEDPOLYGONALFACE((77,193,194,78)); +#169=IFCINDEXEDPOLYGONALFACE((83,199,198,81)); +#170=IFCINDEXEDPOLYGONALFACE((84,200,199,83)); +#171=IFCINDEXEDPOLYGONALFACE((79,195,193,77)); +#172=IFCINDEXEDPOLYGONALFACE((80,196,195,79)); +#173=IFCINDEXEDPOLYGONALFACE((82,197,200,84)); +#174=IFCINDEXEDPOLYGONALFACE((81,198,197,82)); +#175=IFCINDEXEDPOLYGONALFACE((202,203,204,201)); +#176=IFCINDEXEDPOLYGONALFACE((206,205,208,207)); +#177=IFCINDEXEDPOLYGONALFACE((201,204,208,205)); +#178=IFCINDEXEDPOLYGONALFACE((202,201,205,206)); +#179=IFCINDEXEDPOLYGONALFACE((203,202,206,207)); +#180=IFCINDEXEDPOLYGONALFACE((204,203,207,208)); +#181=IFCINDEXEDPOLYGONALFACE((210,211,212,209)); +#182=IFCINDEXEDPOLYGONALFACE((214,213,216,215)); +#183=IFCINDEXEDPOLYGONALFACE((209,212,216,213)); +#184=IFCINDEXEDPOLYGONALFACE((210,209,213,214)); +#185=IFCINDEXEDPOLYGONALFACE((211,210,214,215)); +#186=IFCINDEXEDPOLYGONALFACE((212,211,215,216)); +#187=IFCINDEXEDPOLYGONALFACE((218,219,220,217)); +#188=IFCINDEXEDPOLYGONALFACE((222,221,224,223)); +#189=IFCINDEXEDPOLYGONALFACE((217,220,224,221)); +#190=IFCINDEXEDPOLYGONALFACE((218,217,221,222)); +#191=IFCINDEXEDPOLYGONALFACE((219,218,222,223)); +#192=IFCINDEXEDPOLYGONALFACE((220,219,223,224)); +#193=IFCINDEXEDPOLYGONALFACE((226,227,228,225)); +#194=IFCINDEXEDPOLYGONALFACE((230,229,232,231)); +#195=IFCINDEXEDPOLYGONALFACE((225,228,232,229)); +#196=IFCINDEXEDPOLYGONALFACE((226,225,229,230)); +#197=IFCINDEXEDPOLYGONALFACE((227,226,230,231)); +#198=IFCINDEXEDPOLYGONALFACE((228,227,231,232)); +#199=IFCINDEXEDPOLYGONALFACE((59,114,170)); +#200=IFCINDEXEDPOLYGONALFACE((169,171,113)); +#201=IFCINDEXEDPOLYGONALFACE((169,113,58)); +#202=IFCINDEXEDPOLYGONALFACE((59,170,168)); +#203=IFCINDEXEDPOLYGONALFACE((169,58,59)); +#204=IFCCARTESIANPOINTLIST3D(((-7.20000171661377,0.400000512599945,3.80000019073486),(-7.20000171661377,0.400000780820847,3.20000028610229),(-4.40000152587891,-1.29999935626984,1.40000033378601),(-4.75000190734863,-1.29999923706055,1.30621814727783),(-5.0062198638916,-1.29999923706055,1.05000030994415),(-5.1000018119812,-1.29999923706055,0.70000022649765),(-5.00621938705444,-1.29999923706055,0.350000262260437),(-4.75000190734863,-1.29999923706055,0.0937825441360474),(-4.40000152587891,-1.29999923706055,3.00803094432922E-07),(-4.0500020980835,-1.29999923706055,0.0937825441360474),(-3.79378390312195,-1.29999923706055,0.350000321865082),(-3.70000171661377,-1.29999923706055,0.700000286102295),(-3.79378390312195,-1.29999923706055,1.05000030994415),(-4.05000162124634,-1.29999923706055,1.30621814727783),(-2.80000162124634,-1.29999935626984,1.40000033378601),(-3.15000176429749,-1.29999923706055,1.30621814727783),(-3.40621948242188,-1.29999923706055,1.05000030994415),(-3.50000190734863,-1.29999923706055,0.70000022649765),(-3.40621948242188,-1.29999923706055,0.350000262260437),(-3.15000176429749,-1.29999923706055,0.0937825441360474),(-2.80000162124634,-1.29999923706055,3.00803094432922E-07),(-2.45000171661377,-1.29999923706055,0.0937825441360474),(-2.1937837600708,-1.29999923706055,0.350000321865082),(-2.1000018119812,-1.29999923706055,0.700000286102295),(-2.1937837600708,-1.29999923706055,1.05000030994415),(-2.45000171661377,-1.29999923706055,1.30621814727783),(-1.9073486328125E-06,-1.29999935626984,1.40000033378601),(-0.350001811981201,-1.29999923706055,1.30621814727783),(-0.606219291687012,-1.29999923706055,1.05000030994415),(-0.70000171661377,-1.29999923706055,0.70000022649765),(-0.606219291687012,-1.29999923706055,0.350000262260437),(-0.350001811981201,-1.29999923706055,0.0937825441360474),(-1.9073486328125E-06,-1.29999923706055,3.00803094432922E-07),(0.349998474121094,-1.29999923706055,0.0937825441360474),(0.606216430664062,-1.29999923706055,0.350000321865082),(0.699997901916504,-1.29999923706055,0.700000286102295),(0.606216430664062,-1.29999923706055,1.05000030994415),(0.349998474121094,-1.29999923706055,1.30621814727783),(-6.40000152587891,-1.29999923706055,1.80000019073486),(-1.20000123977661,-1.29999923706055,1.80000019073486),(-6.40000152587891,-1.29999923706055,0.500000059604645),(-5.30000162124634,-1.29999923706055,0.500000059604645),(-5.30000162124634,-1.29999923706055,1.10000014305115),(-4.6000018119812,-1.29999923706055,1.50000011920929),(-2.70000171661377,-1.29999923706055,1.50000011920929),(-2.00000190734863,-1.29999923706055,1.10000014305115),(-2.00000190734863,-1.29999923706055,0.500000059604645),(-0.800002098083496,-1.29999923706055,0.500000059604645),(-0.800002098083496,-1.29999923706055,1.10000014305115),(-0.100002288818359,-1.29999923706055,1.50000011920929),(0.0999975204467773,-1.29999923706055,1.50000011920929),(0.799997329711914,-1.29999923706055,1.10000014305115),(0.799997329711914,-1.29999923706055,0.500000059604645),(1.59999752044678,-1.29999923706055,0.500000059604645),(1.59999752044678,-1.29999923706055,0.700000047683716),(2.09999752044678,-1.29999923706055,0.700000047683716),(2.79999828338623,-1.29999923706055,1.20000016689301),(2.72572040557861,-1.09999930858612,1.98569548130035),(2.47427654266357,-1.09999930858612,2.6143045425415),(0.0999984741210938,-1.29999923706055,2.80000019073486),(-0.300001621246338,-1.29999923706055,2.30000019073486),(-0.900001525878906,-1.29999923706055,2.30000019073486),(-6.40000152587891,-1.29999923706055,1.60000014305115),(1.09999847412109,-1.29999923706055,1.60000014305115),(1.49999856948853,-1.29999923706055,1.20000016689301),(-3.70000171661377,-1.29999923706055,1.80000019073486),(2.29999828338623,-1.29999923706055,2.59999990463257),(2.49999809265137,-1.29999923706055,2.09999990463257),(2.49999809265137,-1.29999923706055,1.90000009536743),(1.6999979019165,-1.29999923706055,1.90000009536743),(1.49999809265137,-1.29999923706055,2.09999990463257),(1.49999809265137,-1.29999923706055,2.59999990463257),(-3.70000171661377,-1.29999923706055,3.60000038146973),(-2.20000171661377,-1.29999923706055,3.60000038146973),(-1.70000171661377,-1.29999923706055,2.80000019073486),(-1.70000171661377,-1.29999923706055,1.80000030994415),(-5.90000152587891,1.30000066757202,0.5),(-5.6000018119812,1.30000066757202,0.5),(-5.90000152587891,1.30000066757202,0.800000190734863),(-5.6000018119812,1.30000066757202,0.800000190734863),(1.29999876022339,1.30000066757202,0.5),(1.59999847412109,1.30000066757202,0.5),(1.29999876022339,1.30000066757202,0.800000190734863),(1.59999847412109,1.30000066757202,0.800000190734863),(-6.20000171661377,-1.29999923706055,0.5),(-5.90000152587891,-1.29999923706055,0.5),(-6.20000171661377,-1.29999923706055,0.800000190734863),(-5.90000152587891,-1.29999923706055,0.800000190734863),(0.999998569488525,-1.29999923706055,0.5),(1.29999876022339,-1.29999923706055,0.5),(0.999998569488525,-1.29999923706055,0.800000190734863),(1.29999876022339,-1.29999923706055,0.800000190734863),(-6.40000152587891,1.30000054836273,1.80000007152557),(-1.20000123977661,1.30000054836273,1.80000007152557),(-6.40000152587891,1.30000054836273,1.60000002384186),(-5.30000162124634,1.30000066757202,0.500000059604645),(-6.40000152587891,1.30000066757202,0.500000059604645),(-5.30000162124634,1.30000066757202,1.10000002384186),(-4.6000018119812,1.30000054836273,1.5),(-2.70000171661377,1.30000054836273,1.5),(-2.00000190734863,1.30000066757202,1.10000002384186),(-2.00000190734863,1.30000066757202,0.500000059604645),(-0.800002098083496,1.30000066757202,0.500000059604645),(-0.800002098083496,1.30000066757202,1.10000002384186),(-0.100002288818359,1.30000054836273,1.5),(0.0999975204467773,1.30000054836273,1.5),(0.799997329711914,1.30000066757202,1.10000002384186),(0.799997329711914,1.30000066757202,0.500000059604645),(1.59999752044678,1.30000066757202,0.500000059604645),(1.59999752044678,1.30000066757202,0.700000047683716),(2.09999752044678,1.30000066757202,0.700000047683716),(2.79999828338623,1.30000066757202,1.20000004768372),(2.72572040557861,1.10000050067902,1.98569536209106),(2.47427654266357,1.1000007390976,2.6143045425415),(0.0999984741210938,1.30000078678131,2.79999995231628),(-0.300001621246338,1.30000054836273,2.29999995231628),(-0.900001525878906,1.30000054836273,2.29999995231628),(1.09999847412109,1.30000054836273,1.60000002384186),(1.49999856948853,1.30000066757202,1.20000004768372),(2.29999828338623,1.30000054836273,2.59999990463257),(2.49999809265137,1.30000054836273,2.09999990463257),(2.49999809265137,1.30000054836273,1.89999985694885),(1.6999979019165,1.30000054836273,1.89999985694885),(1.49999809265137,1.30000054836273,2.09999990463257),(1.49999809265137,1.30000054836273,2.59999990463257),(-4.40000152587891,1.30000054836273,1.40000021457672),(-4.75000190734863,1.30000066757202,1.30621790885925),(-5.0062198638916,1.30000066757202,1.05000019073486),(-5.1000018119812,1.30000066757202,0.70000022649765),(-5.00621938705444,1.30000066757202,0.350000262260437),(-4.75000190734863,1.30000066757202,0.0937825441360474),(-4.40000152587891,1.30000066757202,2.95243410164403E-07),(-4.0500020980835,1.30000066757202,0.0937825441360474),(-3.79378390312195,1.30000066757202,0.350000321865082),(-3.70000171661377,1.30000066757202,0.700000286102295),(-3.79378390312195,1.30000066757202,1.05000019073486),(-4.05000162124634,1.30000066757202,1.30621790885925),(-2.80000162124634,1.30000054836273,1.40000021457672),(-3.15000176429749,1.30000066757202,1.30621790885925),(-3.40621948242188,1.30000066757202,1.05000019073486),(-3.50000190734863,1.30000066757202,0.70000022649765),(-3.40621948242188,1.30000066757202,0.350000262260437),(-3.15000176429749,1.30000066757202,0.0937825441360474),(-2.80000162124634,1.30000066757202,2.95243410164403E-07),(-2.45000171661377,1.30000066757202,0.0937825441360474),(-2.1937837600708,1.30000066757202,0.350000321865082),(-2.1000018119812,1.30000066757202,0.700000286102295),(-2.1937837600708,1.30000066757202,1.05000019073486),(-2.45000171661377,1.30000066757202,1.30621790885925),(-1.9073486328125E-06,1.30000054836273,1.40000021457672),(-0.350001811981201,1.30000066757202,1.30621790885925),(-0.606219291687012,1.30000066757202,1.05000019073486),(-0.70000171661377,1.30000066757202,0.70000022649765),(-0.606219291687012,1.30000066757202,0.350000262260437),(-0.350001811981201,1.30000066757202,0.0937825441360474),(-1.9073486328125E-06,1.30000066757202,2.95243410164403E-07),(0.349998474121094,1.30000066757202,0.0937825441360474),(0.606216430664062,1.30000066757202,0.350000321865082),(0.699997901916504,1.30000066757202,0.700000286102295),(0.606216430664062,1.30000066757202,1.05000019073486),(0.349998474121094,1.30000066757202,1.30621790885925),(-7.20000171661377,-0.39999932050705,3.80000019073486),(-7.20000171661377,-0.399999290704727,3.20000028610229),(4.99999809265137,-0.399999290704727,3.20000028610229),(4.99999809265137,-0.39999932050705,3.80000019073486),(4.99999809265137,0.400000780820847,3.20000028610229),(4.99999809265137,0.400000512599945,3.80000019073486),(2.39999866485596,-1.29999923706055,2.80000019073486),(2.79999828338623,-1.29999923706055,1.80000019073486),(2.39999866485596,1.30000078678131,2.79999995231628),(2.79999828338623,1.30000054836273,1.79999995231628),(-3.70000171661377,-0.399999350309372,1.80000019073486),(-3.70000171661377,-0.399999290704727,3.60000038146973),(-2.20000171661377,-0.399999290704727,3.60000038146973),(-1.70000171661377,-0.399999380111694,2.80000019073486),(-1.70000171661377,-0.399999350309372,1.80000019073486),(-3.70000171661377,-0.399999350309372,1.80000019073486),(-1.70000171661377,-0.399999350309372,1.80000019073486),(-3.70000171661377,-0.39999932050705,2.80000019073486),(-1.70000171661377,-0.39999932050705,2.80000019073486),(-3.70000171661377,0.400000721216202,1.80000007152557),(-1.70000171661377,0.400000721216202,1.80000019073486),(-3.70000171661377,0.400000691413879,2.80000019073486),(-1.70000171661377,0.400000691413879,2.80000019073486),(-5.90000152587891,-3.29999923706055,0.500000059604645),(-6.20000171661377,-3.29999923706055,0.500000059604645),(-6.20000171661377,-3.29999923706055,0.800000190734863),(-5.90000152587891,-3.29999923706055,0.800000190734863),(1.29999876022339,-3.29999923706055,0.500000059604645),(0.999998569488525,-3.29999923706055,0.500000059604645),(0.999998569488525,-3.29999923706055,0.800000190734863),(1.29999876022339,-3.29999923706055,0.800000190734863),(-5.90000152587891,3.30000066757202,0.500000059604645),(-5.6000018119812,3.30000066757202,0.500000059604645),(-5.90000152587891,3.30000066757202,0.800000250339508),(-5.6000018119812,3.30000066757202,0.800000250339508),(1.59999847412109,3.30000066757202,0.500000059604645),(1.29999876022339,3.30000066757202,0.500000059604645),(1.29999876022339,3.30000066757202,0.800000250339508),(1.59999847412109,3.30000066757202,0.800000250339508),(1.39999866485596,-2.9499990940094,0.500000059604645),(0.899998664855957,-2.9499990940094,0.500000059604645),(0.899998664855957,-3.44999933242798,0.5),(1.39999866485596,-3.44999933242798,0.5),(1.39999866485596,-2.9499990940094,0.300000071525574),(0.899998664855957,-2.9499990940094,0.300000071525574),(0.899998664855957,-3.44999933242798,0.300000041723251),(1.39999866485596,-3.44999933242798,0.300000041723251),(-5.80000162124634,-2.9499990940094,0.500000059604645),(-6.30000162124634,-2.9499990940094,0.500000059604645),(-6.30000162124634,-3.44999933242798,0.5),(-5.80000162124634,-3.44999933242798,0.5),(-5.80000162124634,-2.9499990940094,0.300000071525574),(-6.30000162124634,-2.9499990940094,0.300000071525574),(-6.30000162124634,-3.44999933242798,0.300000041723251),(-5.80000162124634,-3.44999933242798,0.300000041723251),(1.69999885559082,3.45000076293945,0.500000059604645),(1.19999885559082,3.45000076293945,0.500000059604645),(1.19999885559082,2.95000076293945,0.5),(1.69999885559082,2.95000076293945,0.5),(1.69999885559082,3.45000076293945,0.300000041723251),(1.19999885559082,3.45000076293945,0.300000041723251),(1.19999885559082,2.95000076293945,0.300000011920929),(1.69999885559082,2.95000076293945,0.300000011920929),(-5.50000143051147,3.45000076293945,0.500000059604645),(-6.00000143051147,3.45000076293945,0.500000059604645),(-6.00000143051147,2.95000076293945,0.5),(-5.50000143051147,2.95000076293945,0.5),(-5.50000143051147,3.45000076293945,0.300000041723251),(-6.00000143051147,3.45000076293945,0.300000041723251),(-6.00000143051147,2.95000076293945,0.300000011920929),(-5.50000143051147,2.95000076293945,0.300000011920929),(-1.9073486328125E-06,8.34465026855469E-07,1.19209332183345E-07))); +#205=IFCPOLYGONALFACESET(#204,$,(#91,#99,#100,#103,#104,#108,#111,#112,#115,#116,#124,#130,#135,#136,#137,#138,#139,#140,#141,#142,#143,#144,#145,#146,#147,#148,#149,#150,#151,#152,#153,#154,#199,#200,#201,#202,#203),$); +#206=IFCPOLYGONALFACESET(#204,$,(#92,#93,#94,#95,#96,#97,#98,#101,#102,#105,#106,#107,#109,#110,#113,#114,#117,#118,#119,#120,#121,#122,#123,#125,#126,#127,#128,#129,#131,#132,#133,#134,#155,#156,#157,#158,#159,#160,#161,#162,#163,#164,#165,#166,#167,#168,#169,#170,#171,#172,#173,#174,#175,#176,#177,#178,#179,#180,#181,#182,#183,#184,#185,#186,#187,#188,#189,#190,#191,#192,#193,#194,#195,#196,#197,#198),$); +#207=IFCSHAPEREPRESENTATION(#17,'Body','Tessellation',(#205,#206)); +#208=IFCCOLOURRGB($,0.600000023841858,0.449999988079071,0.); +#209=IFCCOLOURRGB($,0.600000023841858,0.449999988079071,0.); +#210=IFCSURFACESTYLERENDERING(#208,0.,#209,$,$,$,$,$,.NOTDEFINED.); +#211=IFCSURFACESTYLE('Mobile Crane Yellow',.BOTH.,(#210)); +#212=IFCCOLOURRGB($,0.100000001490116,0.100000001490116,0.100000001490116); +#213=IFCCOLOURRGB($,0.100000001490116,0.100000001490116,0.100000001490116); +#214=IFCSURFACESTYLERENDERING(#212,0.,#213,$,$,$,$,$,.NOTDEFINED.); +#215=IFCSURFACESTYLE('Mobile Crane Grey',.BOTH.,(#214)); +#216=IFCCOLOURRGB($,1.,0.,0.); +#217=IFCCOLOURRGB($,1.,0.,0.); +#218=IFCSURFACESTYLERENDERING(#216,0.899999998509884,#217,$,$,$,$,$,.NOTDEFINED.); +#219=IFCSURFACESTYLE('Mobile Crane Clearance',.BOTH.,(#218)); +#220=IFCSTYLEDITEM(#205,(#211),'Mobile Crane Yellow'); +#221=IFCSTYLEDITEM(#206,(#215),'Mobile Crane Grey'); +#222=IFCCARTESIANPOINT((0.,0.,0.)); +#223=IFCDIRECTION((1.,0.,0.)); +#224=IFCDIRECTION((0.,0.,1.)); +#225=IFCAXIS2PLACEMENT3D(#222,#224,#223); +#226=IFCREPRESENTATIONMAP(#225,#207); +#227=IFCINDEXEDPOLYGONALFACE((15,7,3)); +#228=IFCINDEXEDPOLYGONALFACE((4,23,3)); +#229=IFCINDEXEDPOLYGONALFACE((18,39,38)); +#230=IFCINDEXEDPOLYGONALFACE((12,31,11)); +#231=IFCINDEXEDPOLYGONALFACE((5,24,4)); +#232=IFCINDEXEDPOLYGONALFACE((19,20,40,39)); +#233=IFCINDEXEDPOLYGONALFACE((13,32,12)); +#234=IFCINDEXEDPOLYGONALFACE((6,25,5)); +#235=IFCINDEXEDPOLYGONALFACE((1,40,20)); +#236=IFCINDEXEDPOLYGONALFACE((14,33,13)); +#237=IFCINDEXEDPOLYGONALFACE((7,26,6)); +#238=IFCINDEXEDPOLYGONALFACE((15,34,14)); +#239=IFCINDEXEDPOLYGONALFACE((7,8,28,27)); +#240=IFCINDEXEDPOLYGONALFACE((16,35,15)); +#241=IFCINDEXEDPOLYGONALFACE((8,9,29,28)); +#242=IFCINDEXEDPOLYGONALFACE((2,21,1)); +#243=IFCINDEXEDPOLYGONALFACE((17,36,16)); +#244=IFCINDEXEDPOLYGONALFACE((10,29,9)); +#245=IFCINDEXEDPOLYGONALFACE((2,3,23,22)); +#246=IFCINDEXEDPOLYGONALFACE((18,37,17)); +#247=IFCINDEXEDPOLYGONALFACE((11,30,10)); +#248=IFCINDEXEDPOLYGONALFACE((3,2,1)); +#249=IFCINDEXEDPOLYGONALFACE((1,20,19)); +#250=IFCINDEXEDPOLYGONALFACE((19,18,15)); +#251=IFCINDEXEDPOLYGONALFACE((18,17,15)); +#252=IFCINDEXEDPOLYGONALFACE((17,16,15)); +#253=IFCINDEXEDPOLYGONALFACE((15,14,13)); +#254=IFCINDEXEDPOLYGONALFACE((13,12,11)); +#255=IFCINDEXEDPOLYGONALFACE((11,10,7)); +#256=IFCINDEXEDPOLYGONALFACE((10,9,7)); +#257=IFCINDEXEDPOLYGONALFACE((9,8,7)); +#258=IFCINDEXEDPOLYGONALFACE((7,6,5)); +#259=IFCINDEXEDPOLYGONALFACE((5,4,3)); +#260=IFCINDEXEDPOLYGONALFACE((3,1,19)); +#261=IFCINDEXEDPOLYGONALFACE((15,13,11)); +#262=IFCINDEXEDPOLYGONALFACE((7,5,3)); +#263=IFCINDEXEDPOLYGONALFACE((3,19,15)); +#264=IFCINDEXEDPOLYGONALFACE((15,11,7)); +#265=IFCINDEXEDPOLYGONALFACE((4,24,23)); +#266=IFCINDEXEDPOLYGONALFACE((18,19,39)); +#267=IFCINDEXEDPOLYGONALFACE((12,32,31)); +#268=IFCINDEXEDPOLYGONALFACE((5,25,24)); +#269=IFCINDEXEDPOLYGONALFACE((13,33,32)); +#270=IFCINDEXEDPOLYGONALFACE((6,26,25)); +#271=IFCINDEXEDPOLYGONALFACE((1,21,40)); +#272=IFCINDEXEDPOLYGONALFACE((14,34,33)); +#273=IFCINDEXEDPOLYGONALFACE((7,27,26)); +#274=IFCINDEXEDPOLYGONALFACE((15,35,34)); +#275=IFCINDEXEDPOLYGONALFACE((16,36,35)); +#276=IFCINDEXEDPOLYGONALFACE((2,22,21)); +#277=IFCINDEXEDPOLYGONALFACE((17,37,36)); +#278=IFCINDEXEDPOLYGONALFACE((10,30,29)); +#279=IFCINDEXEDPOLYGONALFACE((18,38,37)); +#280=IFCINDEXEDPOLYGONALFACE((11,31,30)); +#281=IFCCARTESIANPOINTLIST3D(((-1.9073486328125E-06,44.,54.),(-13.5967502593994,41.8464889526367,53.9999961853027),(-25.8625526428223,35.5967483520508,54.),(-35.5967483520508,25.8625507354736,54.),(-41.8464889526367,13.5967473983765,54.),(-44.,-1.04911282505782E-06,54.),(-41.8464851379395,-13.5967483520508,54.),(-35.5967483520508,-25.862548828125,54.),(-25.862548828125,-35.596752166748,54.),(-13.5967512130737,-41.8464851379395,54.),(1.9073486328125E-06,-44.,53.9999961853027),(13.5967435836792,-41.8464889526367,54.),(25.8625564575195,-35.5967445373535,54.),(35.596752166748,-25.8625431060791,54.),(41.8464851379395,-13.5967512130737,54.),(44.,1.33507296595781E-06,54.),(41.8464851379395,13.5967540740967,54.),(35.596752166748,25.8625431060791,54.),(25.8625526428223,35.5967445373535,54.),(13.5967435836792,41.8464889526367,53.9999961853027),(-1.9073486328125E-06,44.,-1.53376822709106E-06),(-13.5967502593994,41.8464889526367,-1.62790126978507E-06),(-25.8625526428223,35.5967483520508,1.91361118595523E-06),(-35.5967483520508,25.8625507354736,1.48811591316189E-06),(-41.8464889526367,13.5967473983765,9.51960601014434E-07),(-44.,-1.07288360595703E-06,3.57627811808925E-07),(-41.8464851379395,-13.5967483520508,-2.36704863709747E-07),(-35.5967483520508,-25.862548828125,-7.72860062170366E-07),(-25.862548828125,-35.596752166748,-1.19835556233738E-06),(-13.5967512130737,-41.8464851379395,-1.4715401448484E-06),(1.9073486328125E-06,-44.,-1.56567330122925E-06),(13.5967435836792,-41.8464889526367,-1.47154025853524E-06),(25.8625564575195,-35.5967445373535,-1.19835522127687E-06),(35.596752166748,-25.8625431060791,-7.72859834796691E-07),(41.8464851379395,-13.5967512130737,-2.36704977396585E-07),(44.,1.31130218505859E-06,3.57627925495763E-07),(41.8464851379395,13.5967540740967,9.51960885231529E-07),(35.596752166748,25.8625431060791,1.48811557210138E-06),(25.8625526428223,35.5967445373535,1.91361095858156E-06),(13.5967435836792,41.8464889526367,-1.62790126978507E-06))); +#282=IFCPOLYGONALFACESET(#281,$,(#227,#228,#229,#230,#231,#232,#233,#234,#235,#236,#237,#238,#239,#240,#241,#242,#243,#244,#245,#246,#247,#248,#249,#250,#251,#252,#253,#254,#255,#256,#257,#258,#259,#260,#261,#262,#263,#264,#265,#266,#267,#268,#269,#270,#271,#272,#273,#274,#275,#276,#277,#278,#279,#280),$); +#283=IFCSHAPEREPRESENTATION(#18,'Clearance','Tessellation',(#282)); +#284=IFCCOLOURRGB($,0.600000023841858,0.449999988079071,0.); +#285=IFCCOLOURRGB($,0.600000023841858,0.449999988079071,0.); +#286=IFCSURFACESTYLERENDERING(#284,0.,#285,$,$,$,$,$,.NOTDEFINED.); +#287=IFCSURFACESTYLE('Mobile Crane Yellow',.BOTH.,(#286)); +#288=IFCCOLOURRGB($,0.100000001490116,0.100000001490116,0.100000001490116); +#289=IFCCOLOURRGB($,0.100000001490116,0.100000001490116,0.100000001490116); +#290=IFCSURFACESTYLERENDERING(#288,0.,#289,$,$,$,$,$,.NOTDEFINED.); +#291=IFCSURFACESTYLE('Mobile Crane Grey',.BOTH.,(#290)); +#292=IFCCOLOURRGB($,1.,0.,0.); +#293=IFCCOLOURRGB($,1.,0.,0.); +#294=IFCSURFACESTYLERENDERING(#292,0.899999998509884,#293,$,$,$,$,$,.NOTDEFINED.); +#295=IFCSURFACESTYLE('Mobile Crane Clearance',.BOTH.,(#294)); +#296=IFCSTYLEDITEM(#282,(#287),'Mobile Crane Yellow'); +#297=IFCCARTESIANPOINT((0.,0.,0.)); +#298=IFCDIRECTION((1.,0.,0.)); +#299=IFCDIRECTION((0.,0.,1.)); +#300=IFCAXIS2PLACEMENT3D(#297,#299,#298); +#301=IFCREPRESENTATIONMAP(#300,#283); +ENDSEC; +END-ISO-10303-21; From c131b815653ff746fa3b2c868c863c500d49fc25 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 28 Aug 2021 20:16:04 +1000 Subject: [PATCH 033/113] New bunny rabbit furniture asset :) It's the BlenderBIM Bunny! --- src/blenderbim/demo-library.blend | Bin 907208 -> 948556 bytes src/blenderbim/generate_demo_library.py | 1 + .../libraries/blenderbim-demo-library.ifc | 852 ++++++++++++++++-- 3 files changed, 788 insertions(+), 65 deletions(-) diff --git a/src/blenderbim/demo-library.blend b/src/blenderbim/demo-library.blend index 2bd0073cc2da5ac19f99ca279d2b29bc57517e57..58249f435e6c013e1d84e3cc03da760427a88cea 100644 GIT binary patch delta 157549 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 delta 122866 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 TPc7E;`{(&p8PBg)e)Hphs2L{6 diff --git a/src/blenderbim/generate_demo_library.py b/src/blenderbim/generate_demo_library.py index 24a1794683..29d7146a79 100644 --- a/src/blenderbim/generate_demo_library.py +++ b/src/blenderbim/generate_demo_library.py @@ -113,6 +113,7 @@ class LibraryGenerator: self.create_type("IfcWindowType", "DEMO1", {"body": "Window"}) self.create_type("IfcDoorType", "DEMO1", {"body": "Door"}) + self.create_type("IfcFurnitureType", "BUNNY", {"body": "Bunny"}) self.file.write("blenderbim-demo-library.ifc") diff --git a/src/blenderbim/libraries/blenderbim-demo-library.ifc b/src/blenderbim/libraries/blenderbim-demo-library.ifc index 85ab063e39..f78c7544f2 100644 --- a/src/blenderbim/libraries/blenderbim-demo-library.ifc +++ b/src/blenderbim/libraries/blenderbim-demo-library.ifc @@ -1,7 +1,7 @@ ISO-10303-21; HEADER; FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1'); -FILE_NAME('/dev/null','2021-08-28T18:41:40+10:00',(),(),'IfcOpenShell 0.6.0b0','IfcOpenShell 0.6.0b0','Nobody'); +FILE_NAME('/dev/null','2021-08-28T20:14:38+10:00',(),(),'IfcOpenShell 0.6.0b0','IfcOpenShell 0.6.0b0','Nobody'); FILE_SCHEMA(('IFC4')); ENDSEC; DATA; @@ -11,7 +11,7 @@ DATA; #4=IFCTELECOMADDRESS(.USERDEFINED.,'The source code repository of the software collection.','REPOSITORY',$,$,$,$,'https://github.com/IfcOpenShell/IfcOpenShell.git',$); #5=IFCORGANIZATION($,'IfcOpenShell','IfcOpenShell is an open source (LGPL) software library that helps users and software developers to work with the IFC file format.',(#1),(#2,#3,#4)); #6=IFCAPPLICATION(#5,'0.0.210605','BlenderBIM Add-on','BlenderBIM'); -#7=IFCPROJECTLIBRARY('0H_Sa4feT0KveBrtGY7p5V',$,'BlenderBIM Demo Library',$,$,$,$,(#16,#22),#11); +#7=IFCPROJECTLIBRARY('02JRAx4mH0fxBxsT$2v9UN',$,'BlenderBIM Demo Library',$,$,$,$,(#16,#22),#11); #8=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.); #9=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.); #10=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.); @@ -29,82 +29,82 @@ DATA; #22=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',2,1.E-05,#21,$); #23=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Model',*,*,*,*,#16,$,.PLAN_VIEW.,$); #24=IFCMATERIAL('Unknown',$,$); -#25=IFCWALLTYPE('2uk$uXokb3GBZVA$vNH_SU',$,'DEMO50',$,$,$,$,$,$,$); +#25=IFCWALLTYPE('0aRFf81nr9KgNzGnl1vnLe',$,'DEMO50',$,$,$,$,$,$,$); #26=IFCMATERIALLAYERSET((#28),$,$); -#27=IFCRELASSOCIATESMATERIAL('0jpC5URcX7OPayuV7$ohu_',$,$,$,(#25),#26); +#27=IFCRELASSOCIATESMATERIAL('2koq_$23rD88os1mhInHyb',$,$,$,(#25),#26); #28=IFCMATERIALLAYER(#24,0.05,$,$,$,$,$); -#29=IFCRELDECLARES('1ucSMOcOHFfeZjk8uPttNq',$,$,$,#7,(#52,#30,#58,#91,#46,#86,#63,#25,#42,#142,#100,#38,#71,#96,#34,#67,#81,#76)); -#30=IFCWALLTYPE('14PXQtM7T5J8vNA1dD1dhd',$,'DEMO100',$,$,$,$,$,$,$); +#29=IFCRELDECLARES('1lYav6m6zBKQoEMN6j73Lk',$,$,$,#7,(#86,#42,#46,#81,#30,#52,#96,#76,#63,#142,#34,#67,#100,#188,#58,#91,#25,#38,#71)); +#30=IFCWALLTYPE('3a3p4kAJL17wYFy5s_xKfU',$,'DEMO100',$,$,$,$,$,$,$); #31=IFCMATERIALLAYERSET((#33),$,$); -#32=IFCRELASSOCIATESMATERIAL('1i8cpFKY1Cz97HIemrGWnd',$,$,$,(#30),#31); +#32=IFCRELASSOCIATESMATERIAL('2F9F8a99n4eAhnCPWdQvMU',$,$,$,(#30),#31); #33=IFCMATERIALLAYER(#24,0.1,$,$,$,$,$); -#34=IFCWALLTYPE('1xhMMJsBf7Tez7_CYWD587',$,'DEMO200',$,$,$,$,$,$,$); +#34=IFCWALLTYPE('0BXBHqHQH0sPCQJlomIo$M',$,'DEMO200',$,$,$,$,$,$,$); #35=IFCMATERIALLAYERSET((#37),$,$); -#36=IFCRELASSOCIATESMATERIAL('2cwT38GFjB7v7Pqc4KZhZt',$,$,$,(#34),#35); +#36=IFCRELASSOCIATESMATERIAL('13EaTGBTz0yfHzRBkrfHLq',$,$,$,(#34),#35); #37=IFCMATERIALLAYER(#24,0.2,$,$,$,$,$); -#38=IFCWALLTYPE('2r37t_GPTBk9laaiX_nWH1',$,'DEMO300',$,$,$,$,$,$,$); +#38=IFCWALLTYPE('12L$xZFPHDOAe_voCWkP9m',$,'DEMO300',$,$,$,$,$,$,$); #39=IFCMATERIALLAYERSET((#41),$,$); -#40=IFCRELASSOCIATESMATERIAL('0qtVmmTMr3yAGWXRPSQqGd',$,$,$,(#38),#39); +#40=IFCRELASSOCIATESMATERIAL('2pPdYFmoX5$91hJWAUjjuq',$,$,$,(#38),#39); #41=IFCMATERIALLAYER(#24,0.3,$,$,$,$,$); -#42=IFCCOVERINGTYPE('3Uk6T5mFn0kg8XYa1At3cI',$,'DEMO10',$,$,$,$,$,$,$); +#42=IFCCOVERINGTYPE('1Ztgo28tn0kRfSfmNK9qkr',$,'DEMO10',$,$,$,$,$,$,$); #43=IFCMATERIALLAYERSET((#45),$,$); -#44=IFCRELASSOCIATESMATERIAL('3569eB$mr0CfLG8NM_SkmT',$,$,$,(#42),#43); +#44=IFCRELASSOCIATESMATERIAL('1iQozwRWTA0weLwbmnNB$w',$,$,$,(#42),#43); #45=IFCMATERIALLAYER(#24,0.01,$,$,$,$,$); -#46=IFCCOVERINGTYPE('0vHGihDTfAmxoNxodTI6YE',$,'DEMO20',$,$,(#50),$,$,$,$); +#46=IFCCOVERINGTYPE('2lM9kWziL5cQRN394$_Zyv',$,'DEMO20',$,$,(#50),$,$,$,$); #47=IFCMATERIALLAYERSET((#49),$,$); -#48=IFCRELASSOCIATESMATERIAL('1z6TWtxTbDNurEHgAbI44H',$,$,$,(#46),#47); +#48=IFCRELASSOCIATESMATERIAL('2By6p$FqL769hAQZ2V4eaV',$,$,$,(#46),#47); #49=IFCMATERIALLAYER(#24,0.02,$,$,$,$,$); -#50=IFCPROPERTYSET('07OrawIjLFcBuc8HuMhP34',$,'EPset_Parametric',$,(#51)); +#50=IFCPROPERTYSET('2RTXLcGejCB8gPPQMV_pBQ',$,'EPset_Parametric',$,(#51)); #51=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS2'),$); -#52=IFCCOVERINGTYPE('2S3nmtZdf0TeCEHilgLucE',$,'DEMO30',$,$,(#56),$,$,$,$); +#52=IFCCOVERINGTYPE('33NgKP0EX7Ge5vNOJ7RpZt',$,'DEMO30',$,$,(#56),$,$,$,$); #53=IFCMATERIALLAYERSET((#55),$,$); -#54=IFCRELASSOCIATESMATERIAL('1sBRKybo9BIPbWz0egldJq',$,$,$,(#52),#53); +#54=IFCRELASSOCIATESMATERIAL('1z1vowoR971QV7I0WJTlcx',$,$,$,(#52),#53); #55=IFCMATERIALLAYER(#24,0.03,$,$,$,$,$); -#56=IFCPROPERTYSET('3snYH$8sX4o8S5bvtPkDM1',$,'EPset_Parametric',$,(#57)); +#56=IFCPROPERTYSET('2LX9VQ2kT4G8DTq_2dbhVm',$,'EPset_Parametric',$,(#57)); #57=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS3'),$); -#58=IFCRAMPTYPE('0v_rACbQDASuK_tBy6uIph',$,'DEMO200',$,$,$,$,$,$,$); +#58=IFCRAMPTYPE('2yjsminuzBlQGRZCdQEkux',$,'DEMO200',$,$,$,$,$,$,$); #59=IFCMATERIALLAYERSET((#61),$,$); -#60=IFCRELASSOCIATESMATERIAL('3RVidt7sP7lABIbzFBstcC',$,$,$,(#58),#59); +#60=IFCRELASSOCIATESMATERIAL('0jjQ62VO13au$7us0dqoXe',$,$,$,(#58),#59); #61=IFCMATERIALLAYER(#24,0.2,$,$,$,$,$); #62=IFCCIRCLEPROFILEDEF(.AREA.,$,$,0.3); -#63=IFCPILETYPE('3GVp8yh5X8yeT0TyygKAZO',$,'DEMO1',$,$,$,$,$,$,$); +#63=IFCPILETYPE('0DMfr6TKnBQOX_SV9_XtuX',$,'DEMO1',$,$,$,$,$,$,$); #64=IFCMATERIALPROFILESET($,$,(#66),$); -#65=IFCRELASSOCIATESMATERIAL('1xWyh_Dfz5R9Kn_odeGRUs',$,$,$,(#63),#64); +#65=IFCRELASSOCIATESMATERIAL('2qs6_wQZ9DaBwahfxSH17L',$,$,$,(#63),#64); #66=IFCMATERIALPROFILE($,$,#24,#62,$,$); -#67=IFCSLABTYPE('0ax0t_7Z12XgneinEO826H',$,'DEMO150',$,$,$,$,$,$,$); +#67=IFCSLABTYPE('32buEX9un9ORdQqkh_Efau',$,'DEMO150',$,$,$,$,$,$,$); #68=IFCMATERIALLAYERSET((#70),$,$); -#69=IFCRELASSOCIATESMATERIAL('0AdABIDlT3pASYvQRkSeTv',$,$,$,(#67),#68); +#69=IFCRELASSOCIATESMATERIAL('3sYJsU9in5D9y$7B1ylu0x',$,$,$,(#67),#68); #70=IFCMATERIALLAYER(#24,0.2,$,$,$,$,$); -#71=IFCSLABTYPE('0anFF9jm56dv0b2EGcvUI4',$,'DEMO250',$,$,$,$,$,$,$); +#71=IFCSLABTYPE('12HGTxAH13EBpy8Nhv4ipU',$,'DEMO250',$,$,$,$,$,$,$); #72=IFCMATERIALLAYERSET((#74),$,$); -#73=IFCRELASSOCIATESMATERIAL('2gsu_uFxL8$gABCB9hvaZv',$,$,$,(#71),#72); +#73=IFCRELASSOCIATESMATERIAL('08BxXU$PX6Hgw4T1k$Hb2g',$,$,$,(#71),#72); #74=IFCMATERIALLAYER(#24,0.3,$,$,$,$,$); #75=IFCRECTANGLEPROFILEDEF(.AREA.,$,$,0.5,0.6); -#76=IFCCOLUMNTYPE('2teCQbo7591w3zzKEgTGw6',$,'DEMO1',$,$,$,$,$,$,$); +#76=IFCCOLUMNTYPE('35CgWk0qj8UxxUzuyHo9yD',$,'DEMO1',$,$,$,$,$,$,$); #77=IFCMATERIALPROFILESET($,$,(#79),$); -#78=IFCRELASSOCIATESMATERIAL('3gYaAwqj99fRfLHu900POL',$,$,$,(#76),#77); +#78=IFCRELASSOCIATESMATERIAL('2NUzzNq7H3$ROPkOWvlw7b',$,$,$,(#76),#77); #79=IFCMATERIALPROFILE($,$,#24,#75,$,$); #80=IFCCIRCLEHOLLOWPROFILEDEF(.AREA.,$,$,0.25,0.005); -#81=IFCCOLUMNTYPE('2C46Af2Rf5UR1u2jPH6Hrs',$,'DEMO2',$,$,$,$,$,$,$); +#81=IFCCOLUMNTYPE('07OrXqQdL9SBHPFgfackfV',$,'DEMO2',$,$,$,$,$,$,$); #82=IFCMATERIALPROFILESET($,$,(#84),$); -#83=IFCRELASSOCIATESMATERIAL('1GHUeK7ojBVAHw_J9tytsw',$,$,$,(#81),#82); +#83=IFCRELASSOCIATESMATERIAL('0SmKXJAEP02gO3H$H7rPWi',$,$,$,(#81),#82); #84=IFCMATERIALPROFILE($,$,#24,#80,$,$); #85=IFCRECTANGLEHOLLOWPROFILEDEF(.AREA.,$,$,0.075,0.15,0.005,0.005,0.005); -#86=IFCCOLUMNTYPE('2nd9Gq0rH5PvqcdIn0o5GE',$,'DEMO3',$,$,$,$,$,$,$); +#86=IFCCOLUMNTYPE('1B0dSsP3X2_gwf5hyPDcS_',$,'DEMO3',$,$,$,$,$,$,$); #87=IFCMATERIALPROFILESET($,$,(#89),$); -#88=IFCRELASSOCIATESMATERIAL('3O8tdK1q9Dc80NeoGA2mc2',$,$,$,(#86),#87); +#88=IFCRELASSOCIATESMATERIAL('2A_SAOv5DCl8RCJIYPg6x9',$,$,$,(#86),#87); #89=IFCMATERIALPROFILE($,$,#24,#85,$,$); #90=IFCISHAPEPROFILEDEF(.AREA.,'DEMO-I',$,0.1,0.2,0.005,0.01,0.005,$,$); -#91=IFCBEAMTYPE('1Qq2BHV_zDHOeQJzV2NFtQ',$,'DEMO1',$,$,$,$,$,$,$); +#91=IFCBEAMTYPE('1vI0ZJhIDCCuLzET9OiEF1',$,'DEMO1',$,$,$,$,$,$,$); #92=IFCMATERIALPROFILESET($,$,(#94),$); -#93=IFCRELASSOCIATESMATERIAL('0wKsOvs$jArRBcVacJEmdU',$,$,$,(#91),#92); +#93=IFCRELASSOCIATESMATERIAL('24xBfV4tr2UA1regqO0Zs8',$,$,$,(#91),#92); #94=IFCMATERIALPROFILE($,$,#24,#90,$,$); #95=IFCCSHAPEPROFILEDEF(.AREA.,'DEMO-C',$,0.2,0.1,0.0015,0.03,0.005); -#96=IFCBEAMTYPE('0FR2DbWM991Q0wEwhXUtao',$,'DEMO2',$,$,$,$,$,$,$); +#96=IFCBEAMTYPE('1T02r2eiX3VODL5pvqSVAq',$,'DEMO2',$,$,$,$,$,$,$); #97=IFCMATERIALPROFILESET($,$,(#99),$); -#98=IFCRELASSOCIATESMATERIAL('2P6qEKsav618SE2wpnLhpb',$,$,$,(#96),#97); +#98=IFCRELASSOCIATESMATERIAL('2tR1pAzU92kAT3y5i1T5mB',$,$,$,(#96),#97); #99=IFCMATERIALPROFILE($,$,#24,#95,$,$); -#100=IFCWINDOWTYPE('1lq$4g2j11RfmHPOxojjsF',$,'DEMO1',$,$,$,(#141),$,$,$,$,$,$); +#100=IFCWINDOWTYPE('000UdYzxXBhfUGvcBxrqpK',$,'DEMO1',$,$,$,(#141),$,$,$,$,$,$); #101=IFCINDEXEDPOLYGONALFACE((13,17,18,14)); #102=IFCINDEXEDPOLYGONALFACE((5,6,3,4)); #103=IFCINDEXEDPOLYGONALFACE((7,8,2,1)); @@ -146,33 +146,33 @@ DATA; #139=IFCDIRECTION((0.,0.,1.)); #140=IFCAXIS2PLACEMENT3D(#137,#139,#138); #141=IFCREPRESENTATIONMAP(#140,#126); -#142=IFCDOORTYPE('02Vmf$GHjA3xWMpsR7HwPP',$,'DEMO1',$,$,$,(#187),$,$,$,$,$,$); -#143=IFCINDEXEDPOLYGONALFACE((17,16,15,14)); -#144=IFCINDEXEDPOLYGONALFACE((2,3,29,28)); -#145=IFCINDEXEDPOLYGONALFACE((27,28,29,30,32,31)); -#146=IFCINDEXEDPOLYGONALFACE((7,6,5,3)); -#147=IFCINDEXEDPOLYGONALFACE((8,7,3,2)); -#148=IFCINDEXEDPOLYGONALFACE((23,24,6,7)); -#149=IFCINDEXEDPOLYGONALFACE((21,20,4,24,23,22)); -#150=IFCINDEXEDPOLYGONALFACE((12,13,14,15)); -#151=IFCINDEXEDPOLYGONALFACE((16,19,12,15)); -#152=IFCINDEXEDPOLYGONALFACE((11,10,25,26)); -#153=IFCINDEXEDPOLYGONALFACE((25,1,27,31)); -#154=IFCINDEXEDPOLYGONALFACE((24,4,11,6)); -#155=IFCINDEXEDPOLYGONALFACE((20,21,9,10)); -#156=IFCINDEXEDPOLYGONALFACE((9,8,2,1)); -#157=IFCINDEXEDPOLYGONALFACE((10,9,1,25)); -#158=IFCINDEXEDPOLYGONALFACE((22,23,7,8)); -#159=IFCINDEXEDPOLYGONALFACE((4,20,10,11)); -#160=IFCINDEXEDPOLYGONALFACE((19,16,17,18)); -#161=IFCINDEXEDPOLYGONALFACE((19,18,13,12)); -#162=IFCINDEXEDPOLYGONALFACE((21,22,8,9)); -#163=IFCINDEXEDPOLYGONALFACE((6,11,26,5)); -#164=IFCINDEXEDPOLYGONALFACE((18,17,14,13)); -#165=IFCINDEXEDPOLYGONALFACE((5,26,32,30)); -#166=IFCINDEXEDPOLYGONALFACE((1,2,28,27)); -#167=IFCINDEXEDPOLYGONALFACE((26,25,31,32)); -#168=IFCINDEXEDPOLYGONALFACE((3,5,30,29)); +#142=IFCDOORTYPE('17irmT$YzCZukJLwDmyQjR',$,'DEMO1',$,$,$,(#187),$,$,$,$,$,$); +#143=IFCINDEXEDPOLYGONALFACE((17,14,15,16)); +#144=IFCINDEXEDPOLYGONALFACE((2,28,29,3)); +#145=IFCINDEXEDPOLYGONALFACE((27,31,32,30,29,28)); +#146=IFCINDEXEDPOLYGONALFACE((7,3,5,6)); +#147=IFCINDEXEDPOLYGONALFACE((8,2,3,7)); +#148=IFCINDEXEDPOLYGONALFACE((23,7,6,24)); +#149=IFCINDEXEDPOLYGONALFACE((21,22,23,24,4,20)); +#150=IFCINDEXEDPOLYGONALFACE((12,15,14,13)); +#151=IFCINDEXEDPOLYGONALFACE((16,15,12,19)); +#152=IFCINDEXEDPOLYGONALFACE((11,26,25,10)); +#153=IFCINDEXEDPOLYGONALFACE((25,31,27,1)); +#154=IFCINDEXEDPOLYGONALFACE((24,6,11,4)); +#155=IFCINDEXEDPOLYGONALFACE((20,10,9,21)); +#156=IFCINDEXEDPOLYGONALFACE((9,1,2,8)); +#157=IFCINDEXEDPOLYGONALFACE((10,25,1,9)); +#158=IFCINDEXEDPOLYGONALFACE((22,8,7,23)); +#159=IFCINDEXEDPOLYGONALFACE((4,11,10,20)); +#160=IFCINDEXEDPOLYGONALFACE((19,18,17,16)); +#161=IFCINDEXEDPOLYGONALFACE((19,12,13,18)); +#162=IFCINDEXEDPOLYGONALFACE((21,9,8,22)); +#163=IFCINDEXEDPOLYGONALFACE((6,5,26,11)); +#164=IFCINDEXEDPOLYGONALFACE((18,13,14,17)); +#165=IFCINDEXEDPOLYGONALFACE((5,30,32,26)); +#166=IFCINDEXEDPOLYGONALFACE((1,27,28,2)); +#167=IFCINDEXEDPOLYGONALFACE((26,32,31,25)); +#168=IFCINDEXEDPOLYGONALFACE((3,29,30,5)); #169=IFCCARTESIANPOINTLIST3D(((0.955000162124634,0.0999999940395355,2.09000015258789),(0.955000162124634,0.0450000055134296,2.09000015258789),(0.970000028610229,0.0450000017881393,2.10500001907349),(0.,0.,0.),(0.970000028610229,0.,2.10500001907349),(0.0399999991059303,0.,2.10500001907349),(0.0399999991059303,0.0450000017881393,2.10500001907349),(0.0549999997019768,0.0450000055134296,2.09000015258789),(0.0549999997019768,0.0999999940395355,2.09000015258789),(0.,0.0999999940395355,2.14500021934509),(0.,-3.72529029846191E-09,2.14500021934509),(0.044999998062849,0.,2.09999990463257),(0.044999998062849,0.0399999991059303,2.09999990463257),(0.965000033378601,0.0399999991059303,2.09999990463257),(0.965000033378601,0.,2.09999990463257),(0.965000033378601,0.,0.),(0.965000033378601,0.0399999991059303,0.),(0.044999998062849,0.0399999991059303,0.),(0.044999998062849,0.,0.),(0.,0.0999999940395355,0.),(0.0549999997019768,0.0999999940395355,0.),(0.0549999997019768,0.0450000017881393,0.),(0.0399999991059303,0.0450000017881393,0.),(0.0399999991059303,0.,0.),(1.01000034809113,0.0999999940395355,2.14500021934509),(1.01000034809113,-3.72529029846191E-09,2.14500021934509),(0.955000162124634,0.0999999940395355,0.),(0.955000162124634,0.0450000055134296,0.),(0.970000028610229,0.0450000017881393,0.),(0.970000028610229,0.,0.),(1.01000034809113,0.0999999940395355,0.),(1.01000034809113,-3.72529029846191E-09,0.))); #170=IFCPOLYGONALFACESET(#169,$,(#144,#145,#146,#147,#148,#149,#152,#153,#154,#155,#156,#157,#158,#159,#162,#163,#165,#166,#167,#168),$); #171=IFCPOLYGONALFACESET(#169,$,(#143,#150,#151,#160,#161,#164),$); @@ -192,5 +192,727 @@ DATA; #185=IFCDIRECTION((0.,0.,1.)); #186=IFCAXIS2PLACEMENT3D(#183,#185,#184); #187=IFCREPRESENTATIONMAP(#186,#172); +#188=IFCFURNITURETYPE('1YMC_IQEX76wEG0ScT5$Er',$,'BUNNY',$,$,$,(#909),$,$,$,$); +#189=IFCINDEXEDPOLYGONALFACE((187,278,44)); +#190=IFCINDEXEDPOLYGONALFACE((21,52,60)); +#191=IFCINDEXEDPOLYGONALFACE((91,100,31)); +#192=IFCINDEXEDPOLYGONALFACE((162,19,191)); +#193=IFCINDEXEDPOLYGONALFACE((288,180,159)); +#194=IFCINDEXEDPOLYGONALFACE((241,219,307)); +#195=IFCINDEXEDPOLYGONALFACE((54,93,173)); +#196=IFCINDEXEDPOLYGONALFACE((60,45,21)); +#197=IFCINDEXEDPOLYGONALFACE((58,110,55)); +#198=IFCINDEXEDPOLYGONALFACE((64,18,70)); +#199=IFCINDEXEDPOLYGONALFACE((2,207,162)); +#200=IFCINDEXEDPOLYGONALFACE((10,176,188)); +#201=IFCINDEXEDPOLYGONALFACE((105,114,113)); +#202=IFCINDEXEDPOLYGONALFACE((220,106,249)); +#203=IFCINDEXEDPOLYGONALFACE((252,321,244)); +#204=IFCINDEXEDPOLYGONALFACE((162,57,19)); +#205=IFCINDEXEDPOLYGONALFACE((224,147,220)); +#206=IFCINDEXEDPOLYGONALFACE((90,373,124)); +#207=IFCINDEXEDPOLYGONALFACE((70,199,64)); +#208=IFCINDEXEDPOLYGONALFACE((256,248,258)); +#209=IFCINDEXEDPOLYGONALFACE((115,212,207)); +#210=IFCINDEXEDPOLYGONALFACE((103,36,7)); +#211=IFCINDEXEDPOLYGONALFACE((71,306,320)); +#212=IFCINDEXEDPOLYGONALFACE((297,267,294)); +#213=IFCINDEXEDPOLYGONALFACE((57,50,19)); +#214=IFCINDEXEDPOLYGONALFACE((117,44,188)); +#215=IFCINDEXEDPOLYGONALFACE((62,56,153)); +#216=IFCINDEXEDPOLYGONALFACE((106,147,120)); +#217=IFCINDEXEDPOLYGONALFACE((254,244,245)); +#218=IFCINDEXEDPOLYGONALFACE((208,207,2)); +#219=IFCINDEXEDPOLYGONALFACE((256,257,250)); +#220=IFCINDEXEDPOLYGONALFACE((203,205,211)); +#221=IFCINDEXEDPOLYGONALFACE((56,278,157)); +#222=IFCINDEXEDPOLYGONALFACE((103,7,9)); +#223=IFCINDEXEDPOLYGONALFACE((63,140,176)); +#224=IFCINDEXEDPOLYGONALFACE((15,109,118)); +#225=IFCINDEXEDPOLYGONALFACE((59,159,180)); +#226=IFCINDEXEDPOLYGONALFACE((158,154,208)); +#227=IFCINDEXEDPOLYGONALFACE((300,241,308)); +#228=IFCINDEXEDPOLYGONALFACE((23,32,42)); +#229=IFCINDEXEDPOLYGONALFACE((44,278,56)); +#230=IFCINDEXEDPOLYGONALFACE((189,259,67)); +#231=IFCINDEXEDPOLYGONALFACE((309,304,333)); +#232=IFCINDEXEDPOLYGONALFACE((136,89,259)); +#233=IFCINDEXEDPOLYGONALFACE((31,191,19)); +#234=IFCINDEXEDPOLYGONALFACE((295,304,294)); +#235=IFCINDEXEDPOLYGONALFACE((50,38,19)); +#236=IFCINDEXEDPOLYGONALFACE((44,62,10)); +#237=IFCINDEXEDPOLYGONALFACE((369,25,227)); +#238=IFCINDEXEDPOLYGONALFACE((136,47,233)); +#239=IFCINDEXEDPOLYGONALFACE((33,54,201)); +#240=IFCINDEXEDPOLYGONALFACE((333,304,329)); +#241=IFCINDEXEDPOLYGONALFACE((281,110,285)); +#242=IFCINDEXEDPOLYGONALFACE((275,80,276)); +#243=IFCINDEXEDPOLYGONALFACE((119,106,120)); +#244=IFCINDEXEDPOLYGONALFACE((276,80,233)); +#245=IFCINDEXEDPOLYGONALFACE((232,318,312)); +#246=IFCINDEXEDPOLYGONALFACE((208,63,115)); +#247=IFCINDEXEDPOLYGONALFACE((150,288,159)); +#248=IFCINDEXEDPOLYGONALFACE((286,287,284)); +#249=IFCINDEXEDPOLYGONALFACE((286,285,287)); +#250=IFCINDEXEDPOLYGONALFACE((285,286,279)); +#251=IFCINDEXEDPOLYGONALFACE((239,171,240)); +#252=IFCINDEXEDPOLYGONALFACE((233,47,276)); +#253=IFCINDEXEDPOLYGONALFACE((124,213,90)); +#254=IFCINDEXEDPOLYGONALFACE((157,278,47)); +#255=IFCINDEXEDPOLYGONALFACE((187,47,157)); +#256=IFCINDEXEDPOLYGONALFACE((268,75,222)); +#257=IFCINDEXEDPOLYGONALFACE((101,269,232)); +#258=IFCINDEXEDPOLYGONALFACE((277,7,13)); +#259=IFCINDEXEDPOLYGONALFACE((140,63,74)); +#260=IFCINDEXEDPOLYGONALFACE((140,74,56)); +#261=IFCINDEXEDPOLYGONALFACE((74,153,56)); +#262=IFCINDEXEDPOLYGONALFACE((57,201,50)); +#263=IFCINDEXEDPOLYGONALFACE((320,236,193)); +#264=IFCINDEXEDPOLYGONALFACE((222,236,268)); +#265=IFCINDEXEDPOLYGONALFACE((173,50,201)); +#266=IFCINDEXEDPOLYGONALFACE((299,267,297)); +#267=IFCINDEXEDPOLYGONALFACE((162,212,57)); +#268=IFCINDEXEDPOLYGONALFACE((208,115,207)); +#269=IFCINDEXEDPOLYGONALFACE((267,292,274)); +#270=IFCINDEXEDPOLYGONALFACE((98,197,277)); +#271=IFCINDEXEDPOLYGONALFACE((295,328,329)); +#272=IFCINDEXEDPOLYGONALFACE((158,208,2)); +#273=IFCINDEXEDPOLYGONALFACE((201,57,33)); +#274=IFCINDEXEDPOLYGONALFACE((187,47,278)); +#275=IFCINDEXEDPOLYGONALFACE((241,307,308)); +#276=IFCINDEXEDPOLYGONALFACE((335,317,245)); +#277=IFCINDEXEDPOLYGONALFACE((328,330,329)); +#278=IFCINDEXEDPOLYGONALFACE((84,128,121)); +#279=IFCINDEXEDPOLYGONALFACE((331,330,328)); +#280=IFCINDEXEDPOLYGONALFACE((300,331,328)); +#281=IFCINDEXEDPOLYGONALFACE((129,19,38)); +#282=IFCINDEXEDPOLYGONALFACE((154,298,66)); +#283=IFCINDEXEDPOLYGONALFACE((317,322,323)); +#284=IFCINDEXEDPOLYGONALFACE((302,297,303)); +#285=IFCINDEXEDPOLYGONALFACE((212,93,167)); +#286=IFCINDEXEDPOLYGONALFACE((94,185,184)); +#287=IFCINDEXEDPOLYGONALFACE((211,121,100)); +#288=IFCINDEXEDPOLYGONALFACE((212,173,93)); +#289=IFCINDEXEDPOLYGONALFACE((317,254,245)); +#290=IFCINDEXEDPOLYGONALFACE((51,15,41)); +#291=IFCINDEXEDPOLYGONALFACE((321,339,244)); +#292=IFCINDEXEDPOLYGONALFACE((244,335,245)); +#293=IFCINDEXEDPOLYGONALFACE((211,204,121)); +#294=IFCINDEXEDPOLYGONALFACE((246,72,358)); +#295=IFCINDEXEDPOLYGONALFACE((300,360,301)); +#296=IFCINDEXEDPOLYGONALFACE((234,177,39)); +#297=IFCINDEXEDPOLYGONALFACE((125,152,177)); +#298=IFCINDEXEDPOLYGONALFACE((338,314,311)); +#299=IFCINDEXEDPOLYGONALFACE((149,94,152)); +#300=IFCINDEXEDPOLYGONALFACE((39,175,137)); +#301=IFCINDEXEDPOLYGONALFACE((334,292,267)); +#302=IFCINDEXEDPOLYGONALFACE((343,338,340,346)); +#303=IFCINDEXEDPOLYGONALFACE((283,286,284)); +#304=IFCINDEXEDPOLYGONALFACE((129,16,53)); +#305=IFCINDEXEDPOLYGONALFACE((102,249,106)); +#306=IFCINDEXEDPOLYGONALFACE((197,12,23)); +#307=IFCINDEXEDPOLYGONALFACE((330,310,178)); +#308=IFCINDEXEDPOLYGONALFACE((307,61,22,308)); +#309=IFCINDEXEDPOLYGONALFACE((300,310,331)); +#310=IFCINDEXEDPOLYGONALFACE((205,190,194)); +#311=IFCINDEXEDPOLYGONALFACE((133,2,31)); +#312=IFCINDEXEDPOLYGONALFACE((85,92,20)); +#313=IFCINDEXEDPOLYGONALFACE((360,39,301)); +#314=IFCINDEXEDPOLYGONALFACE((122,47,136)); +#315=IFCINDEXEDPOLYGONALFACE((281,282,186)); +#316=IFCINDEXEDPOLYGONALFACE((2,191,31)); +#317=IFCINDEXEDPOLYGONALFACE((250,249,247)); +#318=IFCINDEXEDPOLYGONALFACE((58,214,216)); +#319=IFCINDEXEDPOLYGONALFACE((234,138,143)); +#320=IFCINDEXEDPOLYGONALFACE((141,298,154)); +#321=IFCINDEXEDPOLYGONALFACE((27,45,76)); +#322=IFCINDEXEDPOLYGONALFACE((146,181,145)); +#323=IFCINDEXEDPOLYGONALFACE((144,181,180)); +#324=IFCINDEXEDPOLYGONALFACE((195,185,179)); +#325=IFCINDEXEDPOLYGONALFACE((228,223,229)); +#326=IFCINDEXEDPOLYGONALFACE((49,358,72)); +#327=IFCINDEXEDPOLYGONALFACE((74,34,183)); +#328=IFCINDEXEDPOLYGONALFACE((221,218,223)); +#329=IFCINDEXEDPOLYGONALFACE((146,107,108)); +#330=IFCINDEXEDPOLYGONALFACE((194,204,205)); +#331=IFCINDEXEDPOLYGONALFACE((352,359,280,279)); +#332=IFCINDEXEDPOLYGONALFACE((46,64,199)); +#333=IFCINDEXEDPOLYGONALFACE((366,86,251)); +#334=IFCINDEXEDPOLYGONALFACE((48,114,105)); +#335=IFCINDEXEDPOLYGONALFACE((198,95,164)); +#336=IFCINDEXEDPOLYGONALFACE((372,65,167)); +#337=IFCINDEXEDPOLYGONALFACE((74,132,153)); +#338=IFCINDEXEDPOLYGONALFACE((21,12,4)); +#339=IFCINDEXEDPOLYGONALFACE((288,111,113)); +#340=IFCINDEXEDPOLYGONALFACE((75,225,174)); +#341=IFCINDEXEDPOLYGONALFACE((166,262,200)); +#342=IFCINDEXEDPOLYGONALFACE((223,230,229)); +#343=IFCINDEXEDPOLYGONALFACE((26,92,3)); +#344=IFCINDEXEDPOLYGONALFACE((219,88,82)); +#345=IFCINDEXEDPOLYGONALFACE((355,357,365,364)); +#346=IFCINDEXEDPOLYGONALFACE((322,325,324)); +#347=IFCINDEXEDPOLYGONALFACE((257,220,250)); +#348=IFCINDEXEDPOLYGONALFACE((289,104,253)); +#349=IFCINDEXEDPOLYGONALFACE((228,108,221)); +#350=IFCINDEXEDPOLYGONALFACE((119,218,102)); +#351=IFCINDEXEDPOLYGONALFACE((367,124,25)); +#352=IFCINDEXEDPOLYGONALFACE((327,325,326)); +#353=IFCINDEXEDPOLYGONALFACE((40,115,63)); +#354=IFCINDEXEDPOLYGONALFACE((321,248,247)); +#355=IFCINDEXEDPOLYGONALFACE((158,83,141)); +#356=IFCINDEXEDPOLYGONALFACE((13,98,277)); +#357=IFCINDEXEDPOLYGONALFACE((352,345,343,365)); +#358=IFCINDEXEDPOLYGONALFACE((5,374,1)); +#359=IFCINDEXEDPOLYGONALFACE((339,347,348,341)); +#360=IFCINDEXEDPOLYGONALFACE((135,87,22)); +#361=IFCINDEXEDPOLYGONALFACE((156,224,231)); +#362=IFCINDEXEDPOLYGONALFACE((163,63,170)); +#363=IFCINDEXEDPOLYGONALFACE((56,142,140)); +#364=IFCINDEXEDPOLYGONALFACE((362,355,356,363)); +#365=IFCINDEXEDPOLYGONALFACE((88,203,15)); +#366=IFCINDEXEDPOLYGONALFACE((24,163,73)); +#367=IFCINDEXEDPOLYGONALFACE((14,78,68)); +#368=IFCINDEXEDPOLYGONALFACE((248,260,258)); +#369=IFCINDEXEDPOLYGONALFACE((78,26,17)); +#370=IFCINDEXEDPOLYGONALFACE((16,17,53)); +#371=IFCINDEXEDPOLYGONALFACE((161,164,95)); +#372=IFCINDEXEDPOLYGONALFACE((291,287,293)); +#373=IFCINDEXEDPOLYGONALFACE((127,18,32)); +#374=IFCINDEXEDPOLYGONALFACE((182,199,148)); +#375=IFCINDEXEDPOLYGONALFACE((319,71,320)); +#376=IFCINDEXEDPOLYGONALFACE((225,232,312)); +#377=IFCINDEXEDPOLYGONALFACE((302,309,289)); +#378=IFCINDEXEDPOLYGONALFACE((13,36,11)); +#379=IFCINDEXEDPOLYGONALFACE((308,87,310)); +#380=IFCINDEXEDPOLYGONALFACE((353,348,347,246)); +#381=IFCINDEXEDPOLYGONALFACE((262,79,200)); +#382=IFCINDEXEDPOLYGONALFACE((131,73,135)); +#383=IFCINDEXEDPOLYGONALFACE((370,213,243)); +#384=IFCINDEXEDPOLYGONALFACE((92,100,91)); +#385=IFCINDEXEDPOLYGONALFACE((89,233,80)); +#386=IFCINDEXEDPOLYGONALFACE((332,165,174)); +#387=IFCINDEXEDPOLYGONALFACE((1,374,2)); +#388=IFCINDEXEDPOLYGONALFACE((28,368,209)); +#389=IFCINDEXEDPOLYGONALFACE((189,136,259)); +#390=IFCINDEXEDPOLYGONALFACE((326,332,327)); +#391=IFCINDEXEDPOLYGONALFACE((117,122,189)); +#392=IFCINDEXEDPOLYGONALFACE((132,16,40)); +#393=IFCINDEXEDPOLYGONALFACE((263,334,311)); +#394=IFCINDEXEDPOLYGONALFACE((134,183,68)); +#395=IFCINDEXEDPOLYGONALFACE((157,122,142)); +#396=IFCINDEXEDPOLYGONALFACE((239,230,97)); +#397=IFCINDEXEDPOLYGONALFACE((180,96,59)); +#398=IFCINDEXEDPOLYGONALFACE((99,113,111)); +#399=IFCINDEXEDPOLYGONALFACE((22,131,135)); +#400=IFCINDEXEDPOLYGONALFACE((321,249,349)); +#401=IFCINDEXEDPOLYGONALFACE((156,120,147)); +#402=IFCINDEXEDPOLYGONALFACE((148,181,182)); +#403=IFCINDEXEDPOLYGONALFACE((152,126,149)); +#404=IFCINDEXEDPOLYGONALFACE((346,340,337,344)); +#405=IFCINDEXEDPOLYGONALFACE((358,215,353,246)); +#406=IFCINDEXEDPOLYGONALFACE((275,89,80)); +#407=IFCINDEXEDPOLYGONALFACE((240,37,239)); +#408=IFCINDEXEDPOLYGONALFACE((14,183,34)); +#409=IFCINDEXEDPOLYGONALFACE((293,295,274)); +#410=IFCINDEXEDPOLYGONALFACE((350,351,344,342)); +#411=IFCINDEXEDPOLYGONALFACE((148,112,96)); +#412=IFCINDEXEDPOLYGONALFACE((313,325,264)); +#413=IFCINDEXEDPOLYGONALFACE((154,170,208)); +#414=IFCINDEXEDPOLYGONALFACE((226,123,46)); +#415=IFCINDEXEDPOLYGONALFACE((351,364,346,344)); +#416=IFCINDEXEDPOLYGONALFACE((355,362,216,357)); +#417=IFCINDEXEDPOLYGONALFACE((349,339,321)); +#418=IFCINDEXEDPOLYGONALFACE((318,324,327)); +#419=IFCINDEXEDPOLYGONALFACE((338,311,334,340)); +#420=IFCINDEXEDPOLYGONALFACE((326,299,302)); +#421=IFCINDEXEDPOLYGONALFACE((112,59,96)); +#422=IFCINDEXEDPOLYGONALFACE((262,198,242)); +#423=IFCINDEXEDPOLYGONALFACE((272,51,41)); +#424=IFCINDEXEDPOLYGONALFACE((318,261,315)); +#425=IFCINDEXEDPOLYGONALFACE((167,57,212)); +#426=IFCINDEXEDPOLYGONALFACE((271,266,255)); +#427=IFCINDEXEDPOLYGONALFACE((218,246,102)); +#428=IFCINDEXEDPOLYGONALFACE((94,179,185)); +#429=IFCINDEXEDPOLYGONALFACE((343,346,364,365)); +#430=IFCINDEXEDPOLYGONALFACE((40,153,132)); +#431=IFCINDEXEDPOLYGONALFACE((345,314,338,343)); +#432=IFCINDEXEDPOLYGONALFACE((8,121,204)); +#433=IFCINDEXEDPOLYGONALFACE((32,64,123)); +#434=IFCINDEXEDPOLYGONALFACE((88,109,82)); +#435=IFCINDEXEDPOLYGONALFACE((133,128,81)); +#436=IFCINDEXEDPOLYGONALFACE((193,319,320)); +#437=IFCINDEXEDPOLYGONALFACE((370,367,369)); +#438=IFCINDEXEDPOLYGONALFACE((6,9,42)); +#439=IFCINDEXEDPOLYGONALFACE((214,186,282)); +#440=IFCINDEXEDPOLYGONALFACE((200,75,166)); +#441=IFCINDEXEDPOLYGONALFACE((375,79,139)); +#442=IFCINDEXEDPOLYGONALFACE((95,309,333)); +#443=IFCINDEXEDPOLYGONALFACE((221,49,72)); +#444=IFCINDEXEDPOLYGONALFACE((36,273,11)); +#445=IFCINDEXEDPOLYGONALFACE((69,155,251)); +#446=IFCINDEXEDPOLYGONALFACE((316,302,289)); +#447=IFCINDEXEDPOLYGONALFACE((297,304,303)); +#448=IFCINDEXEDPOLYGONALFACE((195,159,196)); +#449=IFCINDEXEDPOLYGONALFACE((110,186,55)); +#450=IFCINDEXEDPOLYGONALFACE((323,324,315)); +#451=IFCINDEXEDPOLYGONALFACE((172,83,242)); +#452=IFCINDEXEDPOLYGONALFACE((61,219,82)); +#453=IFCINDEXEDPOLYGONALFACE((283,291,265)); +#454=IFCINDEXEDPOLYGONALFACE((184,175,177)); +#455=IFCINDEXEDPOLYGONALFACE((349,246,347)); +#456=IFCINDEXEDPOLYGONALFACE((174,166,75)); +#457=IFCINDEXEDPOLYGONALFACE((48,363,361)); +#458=IFCINDEXEDPOLYGONALFACE((199,237,46)); +#459=IFCINDEXEDPOLYGONALFACE((164,242,198)); +#460=IFCINDEXEDPOLYGONALFACE((290,317,335,336)); +#461=IFCINDEXEDPOLYGONALFACE((217,298,160)); +#462=IFCINDEXEDPOLYGONALFACE((193,200,79)); +#463=IFCINDEXEDPOLYGONALFACE((253,166,165)); +#464=IFCINDEXEDPOLYGONALFACE((202,116,51)); +#465=IFCINDEXEDPOLYGONALFACE((236,366,268)); +#466=IFCINDEXEDPOLYGONALFACE((170,73,163)); +#467=IFCINDEXEDPOLYGONALFACE((360,328,296)); +#468=IFCINDEXEDPOLYGONALFACE((354,350,348,353)); +#469=IFCINDEXEDPOLYGONALFACE((359,357,216,214)); +#470=IFCINDEXEDPOLYGONALFACE((143,110,125)); +#471=IFCINDEXEDPOLYGONALFACE((265,314,345,283)); +#472=IFCINDEXEDPOLYGONALFACE((252,261,260)); +#473=IFCINDEXEDPOLYGONALFACE((305,337,340,334)); +#474=IFCINDEXEDPOLYGONALFACE((131,116,24)); +#475=IFCINDEXEDPOLYGONALFACE((104,168,253)); +#476=IFCINDEXEDPOLYGONALFACE((126,99,111)); +#477=IFCINDEXEDPOLYGONALFACE((47,275,276)); +#478=IFCINDEXEDPOLYGONALFACE((230,120,97)); +#479=IFCINDEXEDPOLYGONALFACE((279,283,345,352)); +#480=IFCINDEXEDPOLYGONALFACE((67,89,275)); +#481=IFCINDEXEDPOLYGONALFACE((257,271,255)); +#482=IFCINDEXEDPOLYGONALFACE((257,231,224)); +#483=IFCINDEXEDPOLYGONALFACE((316,253,165)); +#484=IFCINDEXEDPOLYGONALFACE((17,3,53)); +#485=IFCINDEXEDPOLYGONALFACE((273,171,266)); +#486=IFCINDEXEDPOLYGONALFACE((260,270,258)); +#487=IFCINDEXEDPOLYGONALFACE((362,58,216)); +#488=IFCINDEXEDPOLYGONALFACE((48,108,107)); +#489=IFCINDEXEDPOLYGONALFACE((57,65,33)); +#490=IFCINDEXEDPOLYGONALFACE((160,172,164)); +#491=IFCINDEXEDPOLYGONALFACE((190,235,184)); +#492=IFCINDEXEDPOLYGONALFACE((354,353,215,361)); +#493=IFCINDEXEDPOLYGONALFACE((258,271,256)); +#494=IFCINDEXEDPOLYGONALFACE((155,366,251)); +#495=IFCINDEXEDPOLYGONALFACE((365,357,359,352)); +#496=IFCINDEXEDPOLYGONALFACE((169,20,26)); +#497=IFCINDEXEDPOLYGONALFACE((312,174,225)); +#498=IFCINDEXEDPOLYGONALFACE((273,43,11)); +#499=IFCINDEXEDPOLYGONALFACE((264,317,290)); +#500=IFCINDEXEDPOLYGONALFACE((287,296,293)); +#501=IFCINDEXEDPOLYGONALFACE((159,149,150)); +#502=IFCINDEXEDPOLYGONALFACE((267,305,334)); +#503=IFCINDEXEDPOLYGONALFACE((206,211,100)); +#504=IFCINDEXEDPOLYGONALFACE((126,150,149)); +#505=IFCINDEXEDPOLYGONALFACE((288,114,144)); +#506=IFCINDEXEDPOLYGONALFACE((266,101,273)); +#507=IFCINDEXEDPOLYGONALFACE((123,42,32)); +#508=IFCINDEXEDPOLYGONALFACE((255,171,231)); +#509=IFCINDEXEDPOLYGONALFACE((34,116,14)); +#510=IFCINDEXEDPOLYGONALFACE((91,3,92)); +#511=IFCINDEXEDPOLYGONALFACE((287,143,138)); +#512=IFCINDEXEDPOLYGONALFACE((77,12,71)); +#513=IFCINDEXEDPOLYGONALFACE((95,178,161)); +#514=IFCINDEXEDPOLYGONALFACE((285,280,281)); +#515=IFCINDEXEDPOLYGONALFACE((242,139,262)); +#516=IFCINDEXEDPOLYGONALFACE((332,318,327)); +#517=IFCINDEXEDPOLYGONALFACE((226,239,37)); +#518=IFCINDEXEDPOLYGONALFACE((175,219,137)); +#519=IFCINDEXEDPOLYGONALFACE((177,94,184)); +#520=IFCINDEXEDPOLYGONALFACE((103,226,37)); +#521=IFCINDEXEDPOLYGONALFACE((372,371,65)); +#522=IFCINDEXEDPOLYGONALFACE((341,335,244,339)); +#523=IFCINDEXEDPOLYGONALFACE((101,69,43)); +#524=IFCINDEXEDPOLYGONALFACE((146,192,182)); +#525=IFCINDEXEDPOLYGONALFACE((52,77,5)); +#526=IFCINDEXEDPOLYGONALFACE((133,60,52)); +#527=IFCINDEXEDPOLYGONALFACE((28,243,213)); +#528=IFCINDEXEDPOLYGONALFACE((110,126,125)); +#529=IFCINDEXEDPOLYGONALFACE((140,188,176)); +#530=IFCINDEXEDPOLYGONALFACE((341,342,336,335)); +#531=IFCINDEXEDPOLYGONALFACE((82,131,61)); +#532=IFCINDEXEDPOLYGONALFACE((290,336,337,305)); +#533=IFCINDEXEDPOLYGONALFACE((109,51,116)); +#534=IFCINDEXEDPOLYGONALFACE((210,29,90)); +#535=IFCINDEXEDPOLYGONALFACE((45,30,21)); +#536=IFCINDEXEDPOLYGONALFACE((204,196,8)); +#537=IFCINDEXEDPOLYGONALFACE((229,238,237)); +#538=IFCINDEXEDPOLYGONALFACE((161,217,160)); +#539=IFCINDEXEDPOLYGONALFACE((305,264,290)); +#540=IFCINDEXEDPOLYGONALFACE((84,60,81)); +#541=IFCINDEXEDPOLYGONALFACE((185,190,184)); +#542=IFCINDEXEDPOLYGONALFACE((5,133,52)); +#543=IFCINDEXEDPOLYGONALFACE((189,187,117)); +#544=IFCINDEXEDPOLYGONALFACE((226,237,238)); +#545=IFCINDEXEDPOLYGONALFACE((23,277,197)); +#546=IFCINDEXEDPOLYGONALFACE((76,8,27)); +#547=IFCINDEXEDPOLYGONALFACE((294,274,295)); +#548=IFCINDEXEDPOLYGONALFACE((145,114,107)); +#549=IFCINDEXEDPOLYGONALFACE((188,44,10)); +#550=IFCINDEXEDPOLYGONALFACE((41,203,85)); +#551=IFCINDEXEDPOLYGONALFACE((13,43,86)); +#552=IFCINDEXEDPOLYGONALFACE((355,364,351,356)); +#553=IFCINDEXEDPOLYGONALFACE((234,125,177)); +#554=IFCINDEXEDPOLYGONALFACE((40,38,50)); +#555=IFCINDEXEDPOLYGONALFACE((272,85,20)); +#556=IFCINDEXEDPOLYGONALFACE((215,48,361)); +#557=IFCINDEXEDPOLYGONALFACE((39,241,301)); +#558=IFCINDEXEDPOLYGONALFACE((311,292,263)); +#559=IFCINDEXEDPOLYGONALFACE((69,86,43)); +#560=IFCINDEXEDPOLYGONALFACE((310,161,178)); +#561=IFCINDEXEDPOLYGONALFACE((202,169,78)); +#562=IFCINDEXEDPOLYGONALFACE((248,250,247)); +#563=IFCINDEXEDPOLYGONALFACE((296,138,360)); +#564=IFCINDEXEDPOLYGONALFACE((42,9,23)); +#565=IFCINDEXEDPOLYGONALFACE((203,206,85)); +#566=IFCINDEXEDPOLYGONALFACE((202,272,169)); +#567=IFCINDEXEDPOLYGONALFACE((342,344,337,336)); +#568=IFCINDEXEDPOLYGONALFACE((129,35,19)); +#569=IFCINDEXEDPOLYGONALFACE((2,162,191)); +#570=IFCINDEXEDPOLYGONALFACE((366,306,98)); +#571=IFCINDEXEDPOLYGONALFACE((361,363,356,354)); +#572=IFCINDEXEDPOLYGONALFACE((68,17,134)); +#573=IFCINDEXEDPOLYGONALFACE((54,173,201)); +#574=IFCINDEXEDPOLYGONALFACE((210,167,151)); +#575=IFCINDEXEDPOLYGONALFACE((156,171,97)); +#576=IFCINDEXEDPOLYGONALFACE((54,151,93)); +#577=IFCINDEXEDPOLYGONALFACE((59,8,196)); +#578=IFCINDEXEDPOLYGONALFACE((213,210,90)); +#579=IFCINDEXEDPOLYGONALFACE((54,371,373)); +#580=IFCINDEXEDPOLYGONALFACE((130,243,209)); +#581=IFCINDEXEDPOLYGONALFACE((359,214,282,280)); +#582=IFCINDEXEDPOLYGONALFACE((142,117,188)); +#583=IFCINDEXEDPOLYGONALFACE((28,367,368)); +#584=IFCINDEXEDPOLYGONALFACE((237,228,229)); +#585=IFCINDEXEDPOLYGONALFACE((362,105,99)); +#586=IFCINDEXEDPOLYGONALFACE((291,314,265)); +#587=IFCINDEXEDPOLYGONALFACE((45,70,18)); +#588=IFCINDEXEDPOLYGONALFACE((210,372,167)); +#589=IFCINDEXEDPOLYGONALFACE((62,63,176)); +#590=IFCINDEXEDPOLYGONALFACE((91,19,35)); +#591=IFCINDEXEDPOLYGONALFACE((206,203,211)); +#592=IFCINDEXEDPOLYGONALFACE((269,260,261)); +#593=IFCINDEXEDPOLYGONALFACE((53,35,129)); +#594=IFCINDEXEDPOLYGONALFACE((54,29,151)); +#595=IFCINDEXEDPOLYGONALFACE((130,368,370)); +#596=IFCINDEXEDPOLYGONALFACE((67,187,189)); +#597=IFCINDEXEDPOLYGONALFACE((371,25,124)); +#598=IFCINDEXEDPOLYGONALFACE((130,209,368)); +#599=IFCINDEXEDPOLYGONALFACE((243,130,370)); +#600=IFCINDEXEDPOLYGONALFACE((213,227,210)); +#601=IFCINDEXEDPOLYGONALFACE((227,372,210)); +#602=IFCINDEXEDPOLYGONALFACE((167,93,151)); +#603=IFCINDEXEDPOLYGONALFACE((372,227,25)); +#604=IFCINDEXEDPOLYGONALFACE((373,29,54)); +#605=IFCINDEXEDPOLYGONALFACE((213,369,227)); +#606=IFCINDEXEDPOLYGONALFACE((371,124,373)); +#607=IFCINDEXEDPOLYGONALFACE((341,348,350,342)); +#608=IFCINDEXEDPOLYGONALFACE((135,66,217)); +#609=IFCINDEXEDPOLYGONALFACE((65,371,33)); +#610=IFCINDEXEDPOLYGONALFACE((350,354,356,351)); +#611=IFCINDEXEDPOLYGONALFACE((333,330,178)); +#612=IFCINDEXEDPOLYGONALFACE((315,254,323)); +#613=IFCINDEXEDPOLYGONALFACE((127,12,30)); +#614=IFCINDEXEDPOLYGONALFACE((100,128,31)); +#615=IFCINDEXEDPOLYGONALFACE((319,5,77)); +#616=IFCINDEXEDPOLYGONALFACE((374,158,2)); +#617=IFCINDEXEDPOLYGONALFACE((375,83,374)); +#618=IFCINDEXEDPOLYGONALFACE((314,274,311)); +#619=IFCINDEXEDPOLYGONALFACE((21,4,52)); +#620=IFCINDEXEDPOLYGONALFACE((288,144,180)); +#621=IFCINDEXEDPOLYGONALFACE((241,137,219)); +#622=IFCINDEXEDPOLYGONALFACE((60,76,45)); +#623=IFCINDEXEDPOLYGONALFACE((10,62,176)); +#624=IFCINDEXEDPOLYGONALFACE((220,147,106)); +#625=IFCINDEXEDPOLYGONALFACE((90,29,373)); +#626=IFCINDEXEDPOLYGONALFACE((70,148,199)); +#627=IFCINDEXEDPOLYGONALFACE((103,37,36)); +#628=IFCINDEXEDPOLYGONALFACE((71,197,306)); +#629=IFCINDEXEDPOLYGONALFACE((117,187,44)); +#630=IFCINDEXEDPOLYGONALFACE((62,44,56)); +#631=IFCINDEXEDPOLYGONALFACE((254,252,244)); +#632=IFCINDEXEDPOLYGONALFACE((59,196,159)); +#633=IFCINDEXEDPOLYGONALFACE((158,141,154)); +#634=IFCINDEXEDPOLYGONALFACE((300,301,241)); +#635=IFCINDEXEDPOLYGONALFACE((23,127,32)); +#636=IFCINDEXEDPOLYGONALFACE((309,303,304)); +#637=IFCINDEXEDPOLYGONALFACE((295,329,304)); +#638=IFCINDEXEDPOLYGONALFACE((369,367,25)); +#639=IFCINDEXEDPOLYGONALFACE((119,102,106)); +#640=IFCINDEXEDPOLYGONALFACE((232,269,318)); +#641=IFCINDEXEDPOLYGONALFACE((208,170,63)); +#642=IFCINDEXEDPOLYGONALFACE((239,97,171)); +#643=IFCINDEXEDPOLYGONALFACE((124,28,213)); +#644=IFCINDEXEDPOLYGONALFACE((268,155,75)); +#645=IFCINDEXEDPOLYGONALFACE((101,270,269)); +#646=IFCINDEXEDPOLYGONALFACE((277,9,7)); +#647=IFCINDEXEDPOLYGONALFACE((320,306,236)); +#648=IFCINDEXEDPOLYGONALFACE((222,193,236)); +#649=IFCINDEXEDPOLYGONALFACE((173,115,50)); +#650=IFCINDEXEDPOLYGONALFACE((299,313,267)); +#651=IFCINDEXEDPOLYGONALFACE((162,207,212)); +#652=IFCINDEXEDPOLYGONALFACE((98,306,197)); +#653=IFCINDEXEDPOLYGONALFACE((295,296,328)); +#654=IFCINDEXEDPOLYGONALFACE((84,81,128)); +#655=IFCINDEXEDPOLYGONALFACE((302,299,297)); +#656=IFCINDEXEDPOLYGONALFACE((212,115,173)); +#657=IFCINDEXEDPOLYGONALFACE((317,323,254)); +#658=IFCINDEXEDPOLYGONALFACE((211,205,204)); +#659=IFCINDEXEDPOLYGONALFACE((39,177,175)); +#660=IFCINDEXEDPOLYGONALFACE((334,263,292)); +#661=IFCINDEXEDPOLYGONALFACE((283,279,286)); +#662=IFCINDEXEDPOLYGONALFACE((129,38,16)); +#663=IFCINDEXEDPOLYGONALFACE((102,349,249)); +#664=IFCINDEXEDPOLYGONALFACE((197,71,12)); +#665=IFCINDEXEDPOLYGONALFACE((330,331,310)); +#666=IFCINDEXEDPOLYGONALFACE((300,308,310)); +#667=IFCINDEXEDPOLYGONALFACE((205,203,190)); +#668=IFCINDEXEDPOLYGONALFACE((133,1,2)); +#669=IFCINDEXEDPOLYGONALFACE((85,206,92)); +#670=IFCINDEXEDPOLYGONALFACE((360,234,39)); +#671=IFCINDEXEDPOLYGONALFACE((122,157,47)); +#672=IFCINDEXEDPOLYGONALFACE((281,280,282)); +#673=IFCINDEXEDPOLYGONALFACE((250,220,249)); +#674=IFCINDEXEDPOLYGONALFACE((58,55,214)); +#675=IFCINDEXEDPOLYGONALFACE((234,360,138)); +#676=IFCINDEXEDPOLYGONALFACE((141,172,298)); +#677=IFCINDEXEDPOLYGONALFACE((27,112,45)); +#678=IFCINDEXEDPOLYGONALFACE((146,182,181)); +#679=IFCINDEXEDPOLYGONALFACE((144,145,181)); +#680=IFCINDEXEDPOLYGONALFACE((195,194,185)); +#681=IFCINDEXEDPOLYGONALFACE((228,221,223)); +#682=IFCINDEXEDPOLYGONALFACE((49,215,358)); +#683=IFCINDEXEDPOLYGONALFACE((74,163,34)); +#684=IFCINDEXEDPOLYGONALFACE((221,72,218)); +#685=IFCINDEXEDPOLYGONALFACE((146,145,107)); +#686=IFCINDEXEDPOLYGONALFACE((194,195,204)); +#687=IFCINDEXEDPOLYGONALFACE((46,123,64)); +#688=IFCINDEXEDPOLYGONALFACE((366,98,86)); +#689=IFCINDEXEDPOLYGONALFACE((48,107,114)); +#690=IFCINDEXEDPOLYGONALFACE((198,104,95)); +#691=IFCINDEXEDPOLYGONALFACE((74,183,132)); +#692=IFCINDEXEDPOLYGONALFACE((21,30,12)); +#693=IFCINDEXEDPOLYGONALFACE((288,150,111)); +#694=IFCINDEXEDPOLYGONALFACE((75,155,225)); +#695=IFCINDEXEDPOLYGONALFACE((166,168,262)); +#696=IFCINDEXEDPOLYGONALFACE((223,119,230)); +#697=IFCINDEXEDPOLYGONALFACE((26,20,92)); +#698=IFCINDEXEDPOLYGONALFACE((219,235,88)); +#699=IFCINDEXEDPOLYGONALFACE((322,264,325)); +#700=IFCINDEXEDPOLYGONALFACE((257,224,220)); +#701=IFCINDEXEDPOLYGONALFACE((289,309,104)); +#702=IFCINDEXEDPOLYGONALFACE((228,146,108)); +#703=IFCINDEXEDPOLYGONALFACE((119,223,218)); +#704=IFCINDEXEDPOLYGONALFACE((367,28,124)); +#705=IFCINDEXEDPOLYGONALFACE((327,324,325)); +#706=IFCINDEXEDPOLYGONALFACE((40,50,115)); +#707=IFCINDEXEDPOLYGONALFACE((321,252,248)); +#708=IFCINDEXEDPOLYGONALFACE((13,86,98)); +#709=IFCINDEXEDPOLYGONALFACE((5,375,374)); +#710=IFCINDEXEDPOLYGONALFACE((135,217,87)); +#711=IFCINDEXEDPOLYGONALFACE((156,147,224)); +#712=IFCINDEXEDPOLYGONALFACE((163,74,63)); +#713=IFCINDEXEDPOLYGONALFACE((56,157,142)); +#714=IFCINDEXEDPOLYGONALFACE((88,190,203)); +#715=IFCINDEXEDPOLYGONALFACE((24,34,163)); +#716=IFCINDEXEDPOLYGONALFACE((14,202,78)); +#717=IFCINDEXEDPOLYGONALFACE((248,252,260)); +#718=IFCINDEXEDPOLYGONALFACE((78,169,26)); +#719=IFCINDEXEDPOLYGONALFACE((16,134,17)); +#720=IFCINDEXEDPOLYGONALFACE((161,160,164)); +#721=IFCINDEXEDPOLYGONALFACE((291,284,287)); +#722=IFCINDEXEDPOLYGONALFACE((127,30,18)); +#723=IFCINDEXEDPOLYGONALFACE((182,192,199)); +#724=IFCINDEXEDPOLYGONALFACE((319,77,71)); +#725=IFCINDEXEDPOLYGONALFACE((225,69,232)); +#726=IFCINDEXEDPOLYGONALFACE((302,303,309)); +#727=IFCINDEXEDPOLYGONALFACE((13,7,36)); +#728=IFCINDEXEDPOLYGONALFACE((308,22,87)); +#729=IFCINDEXEDPOLYGONALFACE((262,139,79)); +#730=IFCINDEXEDPOLYGONALFACE((131,24,73)); +#731=IFCINDEXEDPOLYGONALFACE((370,369,213)); +#732=IFCINDEXEDPOLYGONALFACE((92,206,100)); +#733=IFCINDEXEDPOLYGONALFACE((89,136,233)); +#734=IFCINDEXEDPOLYGONALFACE((332,316,165)); +#735=IFCINDEXEDPOLYGONALFACE((189,122,136)); +#736=IFCINDEXEDPOLYGONALFACE((326,316,332)); +#737=IFCINDEXEDPOLYGONALFACE((117,142,122)); +#738=IFCINDEXEDPOLYGONALFACE((132,134,16)); +#739=IFCINDEXEDPOLYGONALFACE((134,132,183)); +#740=IFCINDEXEDPOLYGONALFACE((239,238,230)); +#741=IFCINDEXEDPOLYGONALFACE((180,181,96)); +#742=IFCINDEXEDPOLYGONALFACE((99,105,113)); +#743=IFCINDEXEDPOLYGONALFACE((22,61,131)); +#744=IFCINDEXEDPOLYGONALFACE((321,247,249)); +#745=IFCINDEXEDPOLYGONALFACE((156,97,120)); +#746=IFCINDEXEDPOLYGONALFACE((148,96,181)); +#747=IFCINDEXEDPOLYGONALFACE((152,125,126)); +#748=IFCINDEXEDPOLYGONALFACE((240,36,37)); +#749=IFCINDEXEDPOLYGONALFACE((14,68,183)); +#750=IFCINDEXEDPOLYGONALFACE((293,296,295)); +#751=IFCINDEXEDPOLYGONALFACE((148,70,112)); +#752=IFCINDEXEDPOLYGONALFACE((313,299,325)); +#753=IFCINDEXEDPOLYGONALFACE((154,66,170)); +#754=IFCINDEXEDPOLYGONALFACE((226,6,123)); +#755=IFCINDEXEDPOLYGONALFACE((349,347,339)); +#756=IFCINDEXEDPOLYGONALFACE((318,315,324)); +#757=IFCINDEXEDPOLYGONALFACE((326,325,299)); +#758=IFCINDEXEDPOLYGONALFACE((112,27,59)); +#759=IFCINDEXEDPOLYGONALFACE((262,168,198)); +#760=IFCINDEXEDPOLYGONALFACE((272,202,51)); +#761=IFCINDEXEDPOLYGONALFACE((318,269,261)); +#762=IFCINDEXEDPOLYGONALFACE((167,65,57)); +#763=IFCINDEXEDPOLYGONALFACE((271,270,266)); +#764=IFCINDEXEDPOLYGONALFACE((218,72,246)); +#765=IFCINDEXEDPOLYGONALFACE((94,149,179)); +#766=IFCINDEXEDPOLYGONALFACE((40,62,153)); +#767=IFCINDEXEDPOLYGONALFACE((8,84,121)); +#768=IFCINDEXEDPOLYGONALFACE((32,18,64)); +#769=IFCINDEXEDPOLYGONALFACE((88,15,109)); +#770=IFCINDEXEDPOLYGONALFACE((133,31,128)); +#771=IFCINDEXEDPOLYGONALFACE((193,79,319)); +#772=IFCINDEXEDPOLYGONALFACE((370,368,367)); +#773=IFCINDEXEDPOLYGONALFACE((6,103,9)); +#774=IFCINDEXEDPOLYGONALFACE((214,55,186)); +#775=IFCINDEXEDPOLYGONALFACE((200,222,75)); +#776=IFCINDEXEDPOLYGONALFACE((375,319,79)); +#777=IFCINDEXEDPOLYGONALFACE((95,104,309)); +#778=IFCINDEXEDPOLYGONALFACE((221,108,49)); +#779=IFCINDEXEDPOLYGONALFACE((36,240,273)); +#780=IFCINDEXEDPOLYGONALFACE((69,225,155)); +#781=IFCINDEXEDPOLYGONALFACE((316,326,302)); +#782=IFCINDEXEDPOLYGONALFACE((297,294,304)); +#783=IFCINDEXEDPOLYGONALFACE((195,179,159)); +#784=IFCINDEXEDPOLYGONALFACE((110,281,186)); +#785=IFCINDEXEDPOLYGONALFACE((323,322,324)); +#786=IFCINDEXEDPOLYGONALFACE((172,141,83)); +#787=IFCINDEXEDPOLYGONALFACE((61,307,219)); +#788=IFCINDEXEDPOLYGONALFACE((283,284,291)); +#789=IFCINDEXEDPOLYGONALFACE((184,235,175)); +#790=IFCINDEXEDPOLYGONALFACE((349,102,246)); +#791=IFCINDEXEDPOLYGONALFACE((174,165,166)); +#792=IFCINDEXEDPOLYGONALFACE((48,105,363)); +#793=IFCINDEXEDPOLYGONALFACE((199,192,237)); +#794=IFCINDEXEDPOLYGONALFACE((164,172,242)); +#795=IFCINDEXEDPOLYGONALFACE((217,66,298)); +#796=IFCINDEXEDPOLYGONALFACE((193,222,200)); +#797=IFCINDEXEDPOLYGONALFACE((253,168,166)); +#798=IFCINDEXEDPOLYGONALFACE((202,14,116)); +#799=IFCINDEXEDPOLYGONALFACE((236,306,366)); +#800=IFCINDEXEDPOLYGONALFACE((170,66,73)); +#801=IFCINDEXEDPOLYGONALFACE((360,300,328)); +#802=IFCINDEXEDPOLYGONALFACE((143,285,110)); +#803=IFCINDEXEDPOLYGONALFACE((252,254,261)); +#804=IFCINDEXEDPOLYGONALFACE((131,109,116)); +#805=IFCINDEXEDPOLYGONALFACE((104,198,168)); +#806=IFCINDEXEDPOLYGONALFACE((126,58,99)); +#807=IFCINDEXEDPOLYGONALFACE((47,67,275)); +#808=IFCINDEXEDPOLYGONALFACE((230,119,120)); +#809=IFCINDEXEDPOLYGONALFACE((67,259,89)); +#810=IFCINDEXEDPOLYGONALFACE((257,256,271)); +#811=IFCINDEXEDPOLYGONALFACE((257,255,231)); +#812=IFCINDEXEDPOLYGONALFACE((316,289,253)); +#813=IFCINDEXEDPOLYGONALFACE((17,26,3)); +#814=IFCINDEXEDPOLYGONALFACE((273,240,171)); +#815=IFCINDEXEDPOLYGONALFACE((362,99,58)); +#816=IFCINDEXEDPOLYGONALFACE((48,49,108)); +#817=IFCINDEXEDPOLYGONALFACE((160,298,172)); +#818=IFCINDEXEDPOLYGONALFACE((190,88,235)); +#819=IFCINDEXEDPOLYGONALFACE((258,270,271)); +#820=IFCINDEXEDPOLYGONALFACE((155,268,366)); +#821=IFCINDEXEDPOLYGONALFACE((169,272,20)); +#822=IFCINDEXEDPOLYGONALFACE((312,332,174)); +#823=IFCINDEXEDPOLYGONALFACE((273,101,43)); +#824=IFCINDEXEDPOLYGONALFACE((264,322,317)); +#825=IFCINDEXEDPOLYGONALFACE((287,138,296)); +#826=IFCINDEXEDPOLYGONALFACE((159,179,149)); +#827=IFCINDEXEDPOLYGONALFACE((267,313,305)); +#828=IFCINDEXEDPOLYGONALFACE((126,111,150)); +#829=IFCINDEXEDPOLYGONALFACE((288,113,114)); +#830=IFCINDEXEDPOLYGONALFACE((266,270,101)); +#831=IFCINDEXEDPOLYGONALFACE((123,6,42)); +#832=IFCINDEXEDPOLYGONALFACE((255,266,171)); +#833=IFCINDEXEDPOLYGONALFACE((34,24,116)); +#834=IFCINDEXEDPOLYGONALFACE((91,35,3)); +#835=IFCINDEXEDPOLYGONALFACE((287,285,143)); +#836=IFCINDEXEDPOLYGONALFACE((77,4,12)); +#837=IFCINDEXEDPOLYGONALFACE((95,333,178)); +#838=IFCINDEXEDPOLYGONALFACE((285,279,280)); +#839=IFCINDEXEDPOLYGONALFACE((242,83,139)); +#840=IFCINDEXEDPOLYGONALFACE((332,312,318)); +#841=IFCINDEXEDPOLYGONALFACE((226,238,239)); +#842=IFCINDEXEDPOLYGONALFACE((175,235,219)); +#843=IFCINDEXEDPOLYGONALFACE((177,152,94)); +#844=IFCINDEXEDPOLYGONALFACE((103,6,226)); +#845=IFCINDEXEDPOLYGONALFACE((372,25,371)); +#846=IFCINDEXEDPOLYGONALFACE((101,232,69)); +#847=IFCINDEXEDPOLYGONALFACE((146,228,192)); +#848=IFCINDEXEDPOLYGONALFACE((52,4,77)); +#849=IFCINDEXEDPOLYGONALFACE((133,81,60)); +#850=IFCINDEXEDPOLYGONALFACE((28,209,243)); +#851=IFCINDEXEDPOLYGONALFACE((110,58,126)); +#852=IFCINDEXEDPOLYGONALFACE((140,142,188)); +#853=IFCINDEXEDPOLYGONALFACE((82,109,131)); +#854=IFCINDEXEDPOLYGONALFACE((109,15,51)); +#855=IFCINDEXEDPOLYGONALFACE((210,151,29)); +#856=IFCINDEXEDPOLYGONALFACE((45,18,30)); +#857=IFCINDEXEDPOLYGONALFACE((204,195,196)); +#858=IFCINDEXEDPOLYGONALFACE((229,230,238)); +#859=IFCINDEXEDPOLYGONALFACE((161,87,217)); +#860=IFCINDEXEDPOLYGONALFACE((305,313,264)); +#861=IFCINDEXEDPOLYGONALFACE((84,76,60)); +#862=IFCINDEXEDPOLYGONALFACE((185,194,190)); +#863=IFCINDEXEDPOLYGONALFACE((5,1,133)); +#864=IFCINDEXEDPOLYGONALFACE((226,46,237)); +#865=IFCINDEXEDPOLYGONALFACE((23,9,277)); +#866=IFCINDEXEDPOLYGONALFACE((76,84,8)); +#867=IFCINDEXEDPOLYGONALFACE((294,267,274)); +#868=IFCINDEXEDPOLYGONALFACE((145,144,114)); +#869=IFCINDEXEDPOLYGONALFACE((41,15,203)); +#870=IFCINDEXEDPOLYGONALFACE((13,11,43)); +#871=IFCINDEXEDPOLYGONALFACE((234,143,125)); +#872=IFCINDEXEDPOLYGONALFACE((40,16,38)); +#873=IFCINDEXEDPOLYGONALFACE((272,41,85)); +#874=IFCINDEXEDPOLYGONALFACE((215,49,48)); +#875=IFCINDEXEDPOLYGONALFACE((39,137,241)); +#876=IFCINDEXEDPOLYGONALFACE((311,274,292)); +#877=IFCINDEXEDPOLYGONALFACE((69,251,86)); +#878=IFCINDEXEDPOLYGONALFACE((310,87,161)); +#879=IFCINDEXEDPOLYGONALFACE((248,256,250)); +#880=IFCINDEXEDPOLYGONALFACE((68,78,17)); +#881=IFCINDEXEDPOLYGONALFACE((156,231,171)); +#882=IFCINDEXEDPOLYGONALFACE((59,27,8)); +#883=IFCINDEXEDPOLYGONALFACE((54,33,371)); +#884=IFCINDEXEDPOLYGONALFACE((237,192,228)); +#885=IFCINDEXEDPOLYGONALFACE((362,363,105)); +#886=IFCINDEXEDPOLYGONALFACE((291,293,314)); +#887=IFCINDEXEDPOLYGONALFACE((45,112,70)); +#888=IFCINDEXEDPOLYGONALFACE((62,40,63)); +#889=IFCINDEXEDPOLYGONALFACE((91,31,19)); +#890=IFCINDEXEDPOLYGONALFACE((269,270,260)); +#891=IFCINDEXEDPOLYGONALFACE((53,3,35)); +#892=IFCINDEXEDPOLYGONALFACE((67,47,187)); +#893=IFCINDEXEDPOLYGONALFACE((135,73,66)); +#894=IFCINDEXEDPOLYGONALFACE((333,329,330)); +#895=IFCINDEXEDPOLYGONALFACE((315,261,254)); +#896=IFCINDEXEDPOLYGONALFACE((127,23,12)); +#897=IFCINDEXEDPOLYGONALFACE((100,121,128)); +#898=IFCINDEXEDPOLYGONALFACE((319,375,5)); +#899=IFCINDEXEDPOLYGONALFACE((374,83,158)); +#900=IFCINDEXEDPOLYGONALFACE((375,139,83)); +#901=IFCINDEXEDPOLYGONALFACE((314,293,274)); +#902=IFCCARTESIANPOINTLIST3D(((-0.0757642686367035,-0.0121694896370173,0.220662087202072),(-0.105255022644997,-0.0141069469973445,0.230906546115875),(-0.164038479328156,-0.0962571799755096,0.263201057910919),(-0.0149683114141226,-0.0434482358396053,0.228664547204971),(-0.0426693223416805,-0.0120228659361601,0.222334340214729),(0.0788992568850517,-0.0767349451780319,0.173714026808739),(0.0953715369105339,-0.0409212671220303,0.16986283659935),(-0.0719772353768349,-0.0949608311057091,0.171763256192207),(0.0735535696148872,-0.0462111458182335,0.199328601360321),(-0.160245850682259,0.0397466160356998,0.298533588647842),(0.106730677187443,-0.0124975387006998,0.138676866889),(0.0139651391655207,-0.0423045344650745,0.229461222887039),(0.0967235639691353,-0.0144418459385633,0.168111309409142),(-0.219927728176117,-0.0414205342531204,0.239053592085838),(-0.198184996843338,-0.0742136090993881,0.172668352723122),(-0.162167191505432,-0.0434498824179173,0.289568781852722),(-0.189809292554855,-0.0716947764158249,0.281713783740997),(0.0152298724278808,-0.0849794447422028,0.205268412828445),(-0.123513199388981,-0.0452961064875126,0.264716774225235),(-0.188629180192947,-0.119135543704033,0.233101561665535),(-0.0130218090489507,-0.0651145428419113,0.222954735159874),(-0.196876853704453,0.0119782146066427,0.138698890805244),(0.0431601963937283,-0.0451620146632195,0.22145189344883),(-0.216075524687767,-0.016599427908659,0.204968154430389),(-0.0582821778953075,0.0224160328507423,0.331800371408463),(-0.190823614597321,-0.102445237338543,0.260164886713028),(-0.0431380830705166,-0.0991964489221573,0.176975786685944),(-0.0522686094045639,0.0494366958737373,0.351232975721359),(-0.0895938724279404,0.0322130136191845,0.318689584732056),(0.013082567602396,-0.0668555349111557,0.223062723875046),(-0.106145963072777,-0.0415130592882633,0.22882467508316),(0.0448657646775246,-0.0776780471205711,0.203667193651199),(-0.10371295362711,-0.00366749544627964,0.314385384321213),(-0.21360756456852,-0.0169711355119944,0.233581200242043),(-0.138989388942719,-0.0749303176999092,0.265050023794174),(0.105769321322441,-0.0415658876299858,0.138697892427444),(0.0992072820663452,-0.0677607133984566,0.138679757714272),(-0.135680645704269,-0.0402409471571445,0.287896603345871),(-0.17496183514595,-0.0425181090831757,0.0743281096220016),(-0.161954745650291,-0.0129314502701163,0.289540559053421),(-0.208628505468369,-0.103418782353401,0.201527774333954),(0.0640031322836876,-0.0677034556865692,0.197900995612144),(0.100172616541386,0.0126537960022688,0.138708665966988),(-0.168615952134132,0.0482185557484627,0.30722576379776),(-0.0140691194683313,-0.0847146064043045,0.205532997846603),(0.0702492073178291,-0.1020467877388,0.138582319021225),(-0.181213811039925,0.0992056727409363,0.328065633773804),(-0.0152021609246731,-0.112156376242638,0.0183885656297207),(0.0162124074995518,-0.111216500401497,0.021827794611454),(-0.133747041225433,-0.0159911345690489,0.290624916553497),(-0.216561943292618,-0.0709330290555954,0.202728658914566),(-0.0427242144942284,-0.0426300838589668,0.222017183899879),(-0.159124106168747,-0.0738818794488907,0.283847242593765),(-0.103956542909145,0.0154779236763716,0.320181280374527),(-0.136982098221779,-0.102321907877922,0.0194435473531485),(-0.183684900403023,0.0396271869540215,0.295159220695496),(-0.107928916811943,-0.010153891518712,0.291135489940643),(-0.103886745870113,-0.101836994290352,0.0180104468017817),(-0.0461161360144615,-0.119219377636909,0.138967230916023),(-0.0461340732872486,-0.061420276761055,0.21500451862812),(-0.211329713463783,-0.0169732719659805,0.138692498207092),(-0.165825873613358,0.0170033983886242,0.294365167617798),(-0.162926822900772,0.0167535953223705,0.259086668491364),(0.044605728238821,-0.0985531806945801,0.171382486820221),(-0.0834082290530205,0.00335463741794229,0.315553486347198),(-0.15971240401268,0.0247225016355515,0.197611734271049),(-0.16489240527153,0.105032727122307,0.322820842266083),(-0.215148985385895,-0.0462404675781727,0.266269713640213),(0.074162483215332,0.0414574705064297,0.138786911964417),(0.0142031144350767,-0.105447888374329,0.170478105545044),(0.0141690038144588,-0.0131895141676068,0.229208543896675),(0.0433205515146255,-0.101634204387665,0.0178499221801758),(-0.194831639528275,0.00855887122452259,0.19867131114006),(-0.190071240067482,0.00837886054068804,0.263859361410141),(0.0146396514028311,0.0503562577068806,0.171330958604813),(-0.0466328002512455,-0.0789417400956154,0.203323245048523),(-0.0142267476767302,-0.0157651714980602,0.22864143550396),(-0.214272990822792,-0.0700500085949898,0.258544147014618),(-0.0187377445399761,0.0234869290143251,0.211539566516876),(-0.169090524315834,0.130419373512268,0.343455374240875),(-0.0730840340256691,-0.0585213899612427,0.211252138018608),(-0.211533859372139,-0.0429056100547314,0.138715773820877),(-0.0739177912473679,0.0154376216232777,0.210008263587952),(-0.07377789914608,-0.0735882744193077,0.200627535581589),(-0.186267927289009,-0.121167339384556,0.205986142158508),(0.0892870724201202,0.0163372419774532,0.167569145560265),(-0.163796290755272,0.0387952998280525,0.138641089200974),(-0.197594255208969,-0.07469642162323,0.138668864965439),(-0.157580107450485,0.132616892457008,0.328512966632843),(-0.0735077708959579,0.0393004417419434,0.326341509819031),(-0.133432641625404,-0.0800390690565109,0.240147277712822),(-0.161642774939537,-0.107512913644314,0.235317841172218),(-0.103187024593353,0.0151489116251469,0.293316811323166),(-0.131257891654968,-0.0962524563074112,0.0883080363273621),(-0.0977480411529541,0.0540151223540306,0.138882651925087),(-0.015323237515986,-0.12871652841568,0.138334348797798),(0.102820813655853,-0.0720862969756126,0.0782168358564377),(0.0691742300987244,0.00961552746593952,0.196848139166832),(-0.0784864947199821,-0.104707300662994,0.0244421008974314),(-0.129387423396111,-0.0837726294994354,0.201711267232895),(0.10028512775898,0.0147631969302893,0.106750056147575),(0.0725274235010147,-0.0733503252267838,0.0162904672324657),(0.0907945036888123,-0.0631996393203735,0.166820541024208),(-0.0685850381851196,0.0688069462776184,0.138255223631859),(-0.0430277064442635,-0.107757613062859,0.022122398018837),(0.102449595928192,-0.0650743395090103,0.0278087817132473),(-0.0123228346928954,-0.128916323184967,0.0516869872808456),(0.0133168455213308,-0.126367673277855,0.0497013293206692),(-0.211436733603477,-0.0425778105854988,0.171008050441742),(-0.135128378868103,-0.0737440511584282,0.028781833127141),(-0.0713493376970291,-0.0974928066134453,0.048680767416954),(-0.0144545361399651,-0.10740352421999,0.169533520936966),(-0.0520200654864311,-0.106458351016045,0.0468626022338867),(-0.0383422300219536,-0.121899470686913,0.0537898242473602),(-0.135303497314453,0.00472360569983721,0.269406676292419),(-0.222012773156166,-0.0435851588845253,0.201951056718826),(-0.150152832269669,0.0706916153430939,0.296226799488068),(-0.205232128500938,-0.0530128739774227,0.172492980957031),(0.0815067514777184,-0.0842671692371368,0.0463023483753204),(0.101917430758476,-0.0744422674179077,0.0511590167880058),(-0.104162633419037,-0.0769466981291771,0.197300210595131),(-0.165175527334213,0.100392691791058,0.295828104019165),(0.0624474883079529,-0.0914158597588539,0.172223627567291),(-0.0696270391345024,0.0371879562735558,0.345104366540909),(-0.129096910357475,-0.0715842396020889,0.0532362163066864),(-0.102229714393616,-0.0918472409248352,0.0500270053744316),(0.0328243598341942,-0.0628630220890045,0.219847500324249),(-0.0929397568106651,-0.0598123446106911,0.212814390659332),(-0.140351414680481,-0.0651696026325226,0.281688511371613),(-0.0299176927655935,0.0646412074565887,0.345614969730377),(-0.210334226489067,-0.019161444157362,0.170468419790268),(-0.189835593104362,-0.0147899463772774,0.284663945436478),(-0.0706062465906143,-0.0353134833276272,0.219783633947372),(-0.196250692009926,-0.0419037826359272,0.286000579595566),(-0.189289301633835,0.015417193993926,0.167268991470337),(-0.165491297841072,0.119253136217594,0.309156060218811),(-0.188711583614349,-0.0422543436288834,0.0857931450009346),(-0.137549817562103,-0.0175594426691532,0.048555850982666),(-0.0439321398735046,0.0188035927712917,0.209587976336479),(-0.166142821311951,0.0438390895724297,0.269286632537842),(-0.100659042596817,0.0212050415575504,0.210695147514343),(-0.165524810552597,0.0681574642658234,0.275103896856308),(-0.131917878985405,-0.0432314537465572,0.0469778589904308),(-0.0393056124448776,-0.127956256270409,0.0805243328213692),(-0.0148295955732465,-0.134464859962463,0.078124076128006),(0.0156515818089247,-0.132012516260147,0.0775675550103188),(0.128680378198624,-0.0638554841279984,0.0486980155110359),(0.011726126074791,-0.12689021229744,0.138521879911423),(-0.104669205844402,-0.0973712056875229,0.0786209478974342),(-0.0722803771495819,-0.0994613841176033,0.0782437026500702),(-0.0900976955890656,0.0289249792695045,0.304527103900909),(-0.131665915250778,-0.0805337652564049,0.0729337483644485),(-0.17888680100441,0.0127522293478251,0.288278430700302),(-0.131906762719154,0.0216084867715836,0.211986422538757),(0.0438910871744156,0.0446652211248875,0.170035198330879),(0.126842275261879,-0.0620891898870468,0.0720244571566582),(-0.181458547711372,0.0720020085573196,0.30515855550766),(-0.105359517037868,0.0106867477297783,0.222205132246017),(-0.0755681917071342,-0.105624243617058,0.10775239020586),(-0.130771055817604,0.0436740666627884,0.171749204397202),(-0.133024662733078,0.049973726272583,0.138679206371307),(-0.11655567586422,-0.016352504491806,0.262825727462769),(-0.192813113331795,0.00962049700319767,0.228011801838875),(-0.0995994955301285,0.0463632792234421,0.169919461011887),(-0.0153328543528914,0.07756557315588,0.138280719518661),(-0.0149811441078782,0.0544508099555969,0.170514196157455),(-0.0777326822280884,0.0189591310918331,0.297642737627029),(-0.0429378487169743,0.0526389256119728,0.171193689107895),(-0.210668057203293,-0.0934961810708046,0.245899826288223),(-0.162400558590889,0.0198477655649185,0.223333954811096),(0.112556174397469,-0.0415905937552452,0.087884321808815),(-0.0984991043806076,0.0341813936829567,0.19681504368782),(-0.125417664647102,0.00907643139362335,0.292186677455902),(0.0127286352217197,0.0715995132923126,0.138794869184494),(-0.184464573860168,-0.063567191362381,0.0917578190565109),(-0.159845903515816,0.0349735803902149,0.277037382125854),(-0.163954228162766,-0.073273241519928,0.079649306833744),(-0.130220845341682,0.0479081235826015,0.111017473042011),(-0.105627626180649,-0.103251308202744,0.104907594621181),(-0.0447412990033627,-0.130966305732727,0.105820834636688),(-0.0145897325128317,-0.137667417526245,0.107010833919048),(0.0177259147167206,-0.133680522441864,0.11051332205534),(-0.20410780608654,-0.015498636290431,0.265768945217133),(-0.163662612438202,-0.0963144749403,0.108248025178909),(-0.133774682879448,-0.102946348488331,0.108776144683361),(-0.152653515338898,-0.093793697655201,0.0112244309857488),(-0.169374197721481,0.0769077241420746,0.31595915555954),(-0.15337011218071,0.0495448186993599,0.289855599403381),(-0.14865180850029,0.0935175195336342,0.306516766548157),(-0.163774311542511,-0.100279614329338,0.138708546757698),(-0.114786863327026,-0.0349755696952343,0.251059830188751),(0.0435214228928089,-0.123003117740154,0.107089169323444),(0.0122568001970649,0.0234032459557056,0.212896287441254),(-0.132915586233139,-0.105148307979107,0.138666361570358),(-0.103796437382698,-0.10418801009655,0.13867013156414),(-0.0721595510840416,-0.1059859842062,0.138681977987289),(0.0412953048944473,-0.0123581402003765,0.221496060490608),(-0.0697300583124161,0.0507166534662247,0.170578330755234),(0.0441036224365234,-0.114852353930473,0.138935402035713),(-0.0128488391637802,0.0388977639377117,0.196252673864365),(-0.124916173517704,-0.00659546442329884,0.306106418371201),(-0.218161851167679,-0.071009561419487,0.230814844369888),(-0.163197606801987,-0.0973011329770088,0.173606932163239),(-0.106259688735008,-0.0960564464330673,0.167294099926949),(-0.134439319372177,-0.0996981337666512,0.164969086647034),(-0.160570159554482,-0.110724151134491,0.202919006347656),(-0.120365753769875,-0.00549432123079896,0.253050655126572),(-0.133883744478226,0.0106024611741304,0.23326064646244),(-0.0365464128553867,0.062771737575531,0.351498425006866),(-0.0698662772774696,0.0357129909098148,0.305281817913055),(-0.135447904467583,-0.0874549821019173,0.184239640831947),(-0.112891294062138,0.00657996907830238,0.271908432245255),(-0.0499069318175316,0.0498133301734924,0.325594484806061),(-0.135738432407379,-0.100006818771362,-7.45058059692383E-09),(0.0123523958027363,-0.101531967520714,-7.45058059692383E-09),(-0.102930329740047,-0.0987276136875153,-7.45058059692383E-09),(-0.158383101224899,0.0353976972401142,0.167762398719788),(0.0585155189037323,-0.0887269079685211,0.0169257298111916),(-0.202236160635948,-0.0440891794860363,0.107780121266842),(0.12652799487114,-0.0424845181405544,0.0317913927137852),(0.0445115864276886,-0.111490845680237,0.0452388003468513),(0.0178857706487179,0.0359265469014645,0.199328750371933),(0.0685334727168083,-0.0978689268231392,0.0533365905284882),(0.138488471508026,-0.0432419404387474,0.0493728704750538),(0.0406565591692924,0.062880277633667,0.138536900281906),(0.0871811881661415,-0.0870387107133865,0.138694822788239),(-0.0505233928561211,0.0300182458013296,0.313426643610001),(0.0435324311256409,-0.119963906705379,0.0792121887207031),(0.0723142325878143,-0.100660108029842,0.0801471099257469),(0.0880676060914993,-0.086207315325737,0.078484445810318),(0.136276960372925,-0.0405644066631794,0.078633114695549),(0.0735301449894905,0.0462804175913334,0.10518267005682),(-0.180783584713936,0.120272636413574,0.33598318696022),(-0.155802026391029,-0.042164009064436,0.0622472763061523),(-0.192451253533363,-0.0732510983943939,0.112686090171337),(0.0313579067587852,0.0240139346569777,0.208784699440002),(0.0728883668780327,-0.103513494133949,0.107350297272205),(0.0885002017021179,-0.0885679498314857,0.105739302933216),(0.100790202617645,-0.0713259652256966,0.10683286935091),(0.109439946711063,-0.0426978133618832,0.107300646603107),(-0.18864569067955,-0.0167884975671768,0.0867345333099365),(-0.0709428116679192,0.0352016389369965,0.19365206360817),(-0.0357190407812595,0.0615072995424271,0.335724234580994),(0.0447911284863949,0.0144118629395962,-7.45058059692383E-09),(0.0369860865175724,0.0369828194379807,-7.45058059692383E-09),(0.0461129434406757,-0.0748821049928665,-7.45058059692383E-09),(0.104031659662724,-0.0135611081495881,0.0148804550990462),(0.0986066535115242,0.0066530667245388,0.0271508432924747),(0.103960558772087,-0.0420542061328888,0.0150693515315652),(0.121874935925007,-0.0147962821647525,0.0282622296363115),(0.0696230307221413,0.0340555869042873,0.168976783752441),(0.0729203075170517,0.015480482019484,0.0226278305053711),(-0.0445376336574554,0.0741409137845039,0.139188349246979),(0.046685803681612,0.0460076108574867,0.0192816369235516),(0.132462680339813,-0.0147683853283525,0.079218864440918),(0.123972199857235,0.00519884005188942,0.0471794344484806),(0.13483801484108,-0.0135693158954382,0.0477543026208878),(0.101557418704033,0.0150842368602753,0.0500984787940979),(-0.151446789503098,0.125798091292381,0.318272113800049),(0.0826703608036041,0.023927254602313,0.0464257299900055),(0.0693408101797104,0.0435765013098717,0.0500893704593182),(-0.0420871675014496,0.0380131863057613,0.193471923470497),(-0.0971032008528709,0.0616641864180565,-7.45058059692383E-09),(-0.0130963791161776,0.064698226749897,0.0197515171021223),(-0.157119512557983,0.00803167372941971,-7.45058059692383E-09),(0.113602519035339,-0.0132037419825792,0.0879008769989014),(-0.069912314414978,0.066078893840313,0.0191369466483593),(0.0389328189194202,0.0351467467844486,0.194373697042465),(0.0768988505005836,0.0420413166284561,0.0788332372903824),(0.10157422721386,0.0134498169645667,0.0773250162601471),(0.123080961406231,0.00395354814827442,0.0694246292114258),(-0.211960434913635,-0.102200835943222,0.224356546998024),(0.110181555151939,-0.0136255938559771,0.109196342527866),(-0.102282598614693,0.0414383597671986,0.019612405449152),(-0.172445297241211,0.11539913713932,0.340771019458771),(-0.181048646569252,0.112369157373905,0.34296378493309),(0.0725264996290207,-0.0152853392064571,0.200319215655327),(-0.183978870511055,0.0709394812583923,0.317676812410355),(-0.153028383851051,-0.0384657420217991,-7.45058059692383E-09),(-0.154637187719345,-0.0691222250461578,-7.45058059692383E-09),(-0.152765303850174,-0.0738510563969612,0.015262059867382),(-0.153248697519302,-0.0919284746050835,-7.45058059692383E-09),(-0.16192090511322,-0.0145302480086684,-7.45058059692383E-09),(-0.161076262593269,-0.0149271814152598,0.0173035766929388),(-0.139386385679245,-0.0480194091796875,0.0201432537287474),(-0.15407682955265,-0.0336258858442307,0.0154564278200269),(-0.141747921705246,-0.0158547051250935,0.0288874395191669),(-0.0563743449747562,-0.108996540307999,0.0737379342317581),(-0.0461691729724407,0.0890766233205795,0.110146202147007),(-0.0146415047347546,0.0512426868081093,-7.45058059692383E-09),(-0.156508177518845,0.00872325897216797,0.0127747664228082),(-0.0932494476437569,0.0622886717319489,0.0157215017825365),(-0.134241998195648,0.0180515833199024,0.0220324043184519),(-0.0754619538784027,0.0455531552433968,0.0509162880480289),(-0.103701874613762,0.0273517612367868,0.0517874732613564),(-0.131066977977753,0.0115249017253518,0.0524038933217525),(-0.062931016087532,0.0692232176661491,0.0536416172981262),(-0.132335588335991,0.0322872921824455,0.19751612842083),(-0.0452888980507851,0.0760203972458839,0.0472172982990742),(-0.163926124572754,0.0142420912161469,0.0823174566030502),(-0.174691706895828,-0.0135900285094976,0.0736509189009666),(-0.0486402213573456,0.0849898308515549,0.0788175389170647),(-0.0689510703086853,0.0702485665678978,0.0784279331564903),(-0.0810153111815453,0.0491584502160549,0.0748984813690186),(-0.0429749675095081,0.0617619827389717,-7.45058059692383E-09),(0.0349937379360199,0.00642204098403454,0.219141826033592),(-0.202323064208031,-0.0122631303966045,0.109208643436432),(-0.188646167516708,0.0148954978212714,0.108683586120605),(-0.0744052901864052,0.0735662579536438,0.106222227215767),(-0.161729156970978,0.0381991006433964,0.108166508376598),(-0.104008600115776,0.0453929454088211,-7.45058059692383E-09),(0.0388389863073826,0.0702219158411026,0.10972835123539),(-0.0412575826048851,0.0688836574554443,0.0204634200781584),(-0.132600158452988,0.0162683837115765,-7.45058059692383E-09),(0.0419384241104126,0.0643723532557487,0.0487342029809952),(-0.0230755694210529,0.0902970731258392,0.106796741485596),(0.0122685618698597,0.0503091886639595,-7.45058059692383E-09),(0.0420029424130917,0.0680971890687943,0.0789963230490685),(-0.0132175851613283,0.00625489093363285,0.222308561205864),(0.0146723045036197,0.00723757036030293,0.223271667957306),(0.0720149055123329,-0.0120490025728941,0.00231547281146049),(0.013688700273633,0.0642379224300385,0.0262222941964865),(0.033619936555624,0.0599825419485569,0.0301631242036819),(0.0156846102327108,0.0726122707128525,0.0497567467391491),(-0.0139973452314734,0.0766579210758209,0.0474896989762783),(-0.0166601836681366,0.0857705846428871,0.0790435597300529),(0.0127416122704744,0.0787845030426979,0.0776184424757957),(-0.137325063347816,0.0222998633980751,0.0709470063447952),(-0.103061355650425,0.0392319709062576,0.082869827747345),(-0.133015736937523,0.0387391112744808,0.0904415026307106),(-0.151931047439575,0.0321191623806953,0.0878717452287674),(0.0125869233161211,0.0806632563471794,0.105742789804935),(-0.0995742082595825,0.049370177090168,0.106232292950153),(-0.0746603757143021,0.0656085163354874,-7.45058059692383E-09),(0.0122953318059444,0.017735980451107,-7.45058059692383E-09),(-0.0146934473887086,0.0262711010873318,-7.45058059692383E-09),(-0.0429374538362026,0.0298651698976755,-7.45058059692383E-09),(-0.103215932846069,0.0137835666537285,-7.45058059692383E-09),(0.0444422401487827,-0.0129836350679398,-7.45058059692383E-09),(-0.0746518895030022,0.0316607765853405,-7.45058059692383E-09),(0.0123018361628056,-0.0127876792103052,-7.45058059692383E-09),(-0.0147215090692043,-0.0133242877200246,-7.45058059692383E-09),(-0.101430043578148,-0.0147481001913548,-7.45058059692383E-09),(-0.0429213680326939,-0.0151002155616879,-7.45058059692383E-09),(-0.132630944252014,-0.0134387537837029,-7.45058059692383E-09),(-0.0746475011110306,-0.0111579261720181,-7.45058059692383E-09),(0.0461949594318867,-0.0483818538486958,-7.45058059692383E-09),(0.0123028568923473,-0.0431565642356873,-7.45058059692383E-09),(0.0676943361759186,-0.0439984127879143,0.00213921279646456),(-0.0147214606404305,-0.0419304519891739,-7.45058059692383E-09),(-0.0429213680326939,-0.0426230616867542,-7.45058059692383E-09),(-0.134391859173775,-0.0420995727181435,-7.45058059692383E-09),(0.0123003236949444,-0.0714240521192551,-7.45058059692383E-09),(-0.0147217661142349,-0.0719940662384033,-7.45058059692383E-09),(-0.0746477097272873,-0.0698381289839745,-7.45058059692383E-09),(-0.0429213680326939,-0.0721928924322128,-7.45058059692383E-09),(-0.101144231855869,-0.0718697011470795,-7.45058059692383E-09),(0.0347950644791126,-0.096686989068985,-7.45058059692383E-09),(-0.132067084312439,-0.0720017328858376,-7.45058059692383E-09),(-0.159548789262772,-0.0125050684437156,0.0618688985705376),(-0.0169071108102798,-0.107485927641392,-7.45058059692383E-09),(-0.0746394321322441,-0.103576719760895,-7.45058059692383E-09),(-0.0428757518529892,-0.105996340513229,-7.45058059692383E-09),(-0.0746474862098694,-0.0418127365410328,-7.45058059692383E-09),(-0.101288944482803,-0.0456511229276657,-7.45058059692383E-09),(0.061871238052845,0.0245271548628807,0.191577181220055),(-0.0470216795802116,0.0414715930819511,0.344332307577133),(-0.0351001992821693,0.0582603961229324,0.352131396532059),(-0.043320570141077,0.0422725304961205,0.325726985931396),(-0.0332878455519676,0.056865319609642,0.334871053695679),(-0.0782285928726196,0.010980136692524,0.334277510643005),(-0.0612197890877724,0.0185103937983513,0.30783212184906),(-0.0876919776201248,0.0268637835979462,0.333815038204193),(-0.0750949084758759,-0.00158989988267422,0.21651217341423),(-0.0432584583759308,0.000724630663171411,0.217384174466133))); +#903=IFCPOLYGONALFACESET(#902,$,(#189,#190,#191,#192,#193,#194,#195,#196,#197,#198,#199,#200,#201,#202,#203,#204,#205,#206,#207,#208,#209,#210,#211,#212,#213,#214,#215,#216,#217,#218,#219,#220,#221,#222,#223,#224,#225,#226,#227,#228,#229,#230,#231,#232,#233,#234,#235,#236,#237,#238,#239,#240,#241,#242,#243,#244,#245,#246,#247,#248,#249,#250,#251,#252,#253,#254,#255,#256,#257,#258,#259,#260,#261,#262,#263,#264,#265,#266,#267,#268,#269,#270,#271,#272,#273,#274,#275,#276,#277,#278,#279,#280,#281,#282,#283,#284,#285,#286,#287,#288,#289,#290,#291,#292,#293,#294,#295,#296,#297,#298,#299,#300,#301,#302,#303,#304,#305,#306,#307,#308,#309,#310,#311,#312,#313,#314,#315,#316,#317,#318,#319,#320,#321,#322,#323,#324,#325,#326,#327,#328,#329,#330,#331,#332,#333,#334,#335,#336,#337,#338,#339,#340,#341,#342,#343,#344,#345,#346,#347,#348,#349,#350,#351,#352,#353,#354,#355,#356,#357,#358,#359,#360,#361,#362,#363,#364,#365,#366,#367,#368,#369,#370,#371,#372,#373,#374,#375,#376,#377,#378,#379,#380,#381,#382,#383,#384,#385,#386,#387,#388,#389,#390,#391,#392,#393,#394,#395,#396,#397,#398,#399,#400,#401,#402,#403,#404,#405,#406,#407,#408,#409,#410,#411,#412,#413,#414,#415,#416,#417,#418,#419,#420,#421,#422,#423,#424,#425,#426,#427,#428,#429,#430,#431,#432,#433,#434,#435,#436,#437,#438,#439,#440,#441,#442,#443,#444,#445,#446,#447,#448,#449,#450,#451,#452,#453,#454,#455,#456,#457,#458,#459,#460,#461,#462,#463,#464,#465,#466,#467,#468,#469,#470,#471,#472,#473,#474,#475,#476,#477,#478,#479,#480,#481,#482,#483,#484,#485,#486,#487,#488,#489,#490,#491,#492,#493,#494,#495,#496,#497,#498,#499,#500,#501,#502,#503,#504,#505,#506,#507,#508,#509,#510,#511,#512,#513,#514,#515,#516,#517,#518,#519,#520,#521,#522,#523,#524,#525,#526,#527,#528,#529,#530,#531,#532,#533,#534,#535,#536,#537,#538,#539,#540,#541,#542,#543,#544,#545,#546,#547,#548,#549,#550,#551,#552,#553,#554,#555,#556,#557,#558,#559,#560,#561,#562,#563,#564,#565,#566,#567,#568,#569,#570,#571,#572,#573,#574,#575,#576,#577,#578,#579,#580,#581,#582,#583,#584,#585,#586,#587,#588,#589,#590,#591,#592,#593,#594,#595,#596,#597,#598,#599,#600,#601,#602,#603,#604,#605,#606,#607,#608,#609,#610,#611,#612,#613,#614,#615,#616,#617,#618,#619,#620,#621,#622,#623,#624,#625,#626,#627,#628,#629,#630,#631,#632,#633,#634,#635,#636,#637,#638,#639,#640,#641,#642,#643,#644,#645,#646,#647,#648,#649,#650,#651,#652,#653,#654,#655,#656,#657,#658,#659,#660,#661,#662,#663,#664,#665,#666,#667,#668,#669,#670,#671,#672,#673,#674,#675,#676,#677,#678,#679,#680,#681,#682,#683,#684,#685,#686,#687,#688,#689,#690,#691,#692,#693,#694,#695,#696,#697,#698,#699,#700,#701,#702,#703,#704,#705,#706,#707,#708,#709,#710,#711,#712,#713,#714,#715,#716,#717,#718,#719,#720,#721,#722,#723,#724,#725,#726,#727,#728,#729,#730,#731,#732,#733,#734,#735,#736,#737,#738,#739,#740,#741,#742,#743,#744,#745,#746,#747,#748,#749,#750,#751,#752,#753,#754,#755,#756,#757,#758,#759,#760,#761,#762,#763,#764,#765,#766,#767,#768,#769,#770,#771,#772,#773,#774,#775,#776,#777,#778,#779,#780,#781,#782,#783,#784,#785,#786,#787,#788,#789,#790,#791,#792,#793,#794,#795,#796,#797,#798,#799,#800,#801,#802,#803,#804,#805,#806,#807,#808,#809,#810,#811,#812,#813,#814,#815,#816,#817,#818,#819,#820,#821,#822,#823,#824,#825,#826,#827,#828,#829,#830,#831,#832,#833,#834,#835,#836,#837,#838,#839,#840,#841,#842,#843,#844,#845,#846,#847,#848,#849,#850,#851,#852,#853,#854,#855,#856,#857,#858,#859,#860,#861,#862,#863,#864,#865,#866,#867,#868,#869,#870,#871,#872,#873,#874,#875,#876,#877,#878,#879,#880,#881,#882,#883,#884,#885,#886,#887,#888,#889,#890,#891,#892,#893,#894,#895,#896,#897,#898,#899,#900,#901),$); +#904=IFCSHAPEREPRESENTATION(#17,'Body','Tessellation',(#903)); +#905=IFCCARTESIANPOINT((0.,0.,0.)); +#906=IFCDIRECTION((1.,0.,0.)); +#907=IFCDIRECTION((0.,0.,1.)); +#908=IFCAXIS2PLACEMENT3D(#905,#907,#906); +#909=IFCREPRESENTATIONMAP(#908,#904); ENDSEC; END-ISO-10303-21; From bcda3d8e93b1486ed8e4a844574bc53a07e4ec23 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sun, 29 Aug 2021 15:26:45 +1000 Subject: [PATCH 034/113] Minor fix --- src/blenderbim/blenderbim/bim/module/cost/ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index c6512fdbbc..5eecac9d49 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -211,10 +211,10 @@ class BIM_PT_cost_schedules(Panel): def draw_cost_value_operator_ui(self, layout, cost_value_id, parent_id): if self.props.active_cost_value_id and self.props.active_cost_value_id == cost_value_id: if self.props.cost_value_editing_type == "ATTRIBUTES": - op = layout.operator("bim.edit_cost_item_value", text="", icon="CHECKMARK") + op = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK") op.cost_value = cost_value_id elif self.props.cost_value_editing_type == "FORMULA": - op = layout.operator("bim.edit_cost_item_value_formula", text="", icon="CHECKMARK") + op = layout.operator("bim.edit_cost_value_formula", text="", icon="CHECKMARK") op.cost_value = cost_value_id layout.operator("bim.disable_editing_cost_item_value", text="", icon="CANCEL") elif self.props.active_cost_value_id: From 5782691cdb61ebe662fff1b31573413b2477e94b Mon Sep 17 00:00:00 2001 From: Prabhat Singh <59395410+TestPrab@users.noreply.github.com> Date: Sun, 29 Aug 2021 11:18:37 +0530 Subject: [PATCH 035/113] minor fix (#1704) --- src/bcf/src/bcf/v3/bcfapi.py | 10 +++++----- src/{foundationserver => opencdeserver}/README.md | 0 .../opencdeserver}/bcf/project.json | 0 .../opencdeserver}/bcf/routes.py | 0 .../schemas/Collaboration/Action/comment_actions.json | 0 .../schemas/Collaboration/Action/project_actions.json | 0 .../schemas/Collaboration/Action/topic_actions.json | 0 .../Collaboration/Action/viewpoint_actions.json | 0 .../bcf/schemas/Collaboration/Comment/comment_GET.json | 0 .../schemas/Collaboration/Comment/comment_POST.json | 0 .../bcf/schemas/Collaboration/Comment/comment_PUT.json | 0 .../schemas/Collaboration/Document/document_GET.json | 0 .../DocumentReference/document_reference_GET.json | 0 .../DocumentReference/document_reference_POST.json | 0 .../DocumentReference/document_reference_PUT.json | 0 .../Collaboration/Events/comment_event_GET.json | 0 .../bcf/schemas/Collaboration/Events/event_action.json | 0 .../schemas/Collaboration/Events/topic_event_GET.json | 0 .../bcf/schemas/Collaboration/File/file_GET.json | 0 .../bcf/schemas/Collaboration/File/file_PUT.json | 0 .../File/project_file_display_information.json | 0 .../Collaboration/File/project_file_information.json | 0 .../File/project_files_information_GET.json | 0 .../Collaboration/RelatedTopic/related_topic_GET.json | 0 .../Collaboration/RelatedTopic/related_topic_PUT.json | 0 .../bcf/schemas/Collaboration/Topic/bim_snippet.json | 0 .../bcf/schemas/Collaboration/Topic/topic_GET.json | 0 .../bcf/schemas/Collaboration/Topic/topic_POST.json | 0 .../bcf/schemas/Collaboration/Topic/topic_PUT.json | 0 .../schemas/Collaboration/Viewpoint/bitmap_GET.json | 0 .../schemas/Collaboration/Viewpoint/bitmap_POST.json | 0 .../Collaboration/Viewpoint/clipping_plane.json | 0 .../bcf/schemas/Collaboration/Viewpoint/coloring.json | 0 .../schemas/Collaboration/Viewpoint/coloring_GET.json | 0 .../bcf/schemas/Collaboration/Viewpoint/component.json | 0 .../Collaboration/Viewpoint/component_list.json | 0 .../schemas/Collaboration/Viewpoint/components.json | 0 .../bcf/schemas/Collaboration/Viewpoint/direction.json | 0 .../bcf/schemas/Collaboration/Viewpoint/line.json | 0 .../bcf/schemas/Collaboration/Viewpoint/location.json | 0 .../Collaboration/Viewpoint/orthogonal_camera.json | 0 .../Collaboration/Viewpoint/perspective_camera.json | 0 .../bcf/schemas/Collaboration/Viewpoint/point.json | 0 .../schemas/Collaboration/Viewpoint/selection_GET.json | 0 .../schemas/Collaboration/Viewpoint/snapshot_GET.json | 0 .../schemas/Collaboration/Viewpoint/snapshot_POST.json | 0 .../Collaboration/Viewpoint/view_setup_hints.json | 0 .../schemas/Collaboration/Viewpoint/viewpoint_GET.json | 0 .../Collaboration/Viewpoint/viewpoint_POST.json | 0 .../schemas/Collaboration/Viewpoint/visibility.json | 0 .../Collaboration/Viewpoint/visibility_GET.json | 0 .../bcf/schemas/Project/extensions_GET.json | 0 .../bcf/schemas/Project/project_GET.json | 0 .../bcf/schemas/Project/project_PUT.json | 0 .../opencdeserver}/bcf/schemas/User/user_GET.json | 0 .../opencdeserver}/bcf/schemas/error.json | 0 .../opencdeserver}/bcf/success.txt | 0 .../opencdeserver}/bcf/templates/base.html | 0 .../opencdeserver}/foundation/forms.py | 0 .../opencdeserver}/foundation/models.py | 0 .../opencdeserver}/foundation/oauth2.py | 0 .../opencdeserver}/foundation/routes.py | 0 .../opencdeserver}/foundation/templates/base.html | 0 .../foundation/templates/clientdata.html | 0 .../foundation/templates/createclient.html | 0 .../opencdeserver}/foundation/templates/index.html | 0 .../opencdeserver}/foundation/templates/login.html | 0 .../opencdeserver}/foundation/templates/oauth.html | 0 .../opencdeserver}/foundation/templates/ologin.html | 0 .../opencdeserver}/foundation/templates/register.html | 0 .../bcfserver => opencdeserver/opencdeserver}/run.py | 0 .../requirements.txt | 0 72 files changed, 5 insertions(+), 5 deletions(-) rename src/{foundationserver => opencdeserver}/README.md (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/project.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/routes.py (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Action/comment_actions.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Action/project_actions.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Action/topic_actions.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Action/viewpoint_actions.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Comment/comment_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Comment/comment_POST.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Comment/comment_PUT.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Document/document_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/DocumentReference/document_reference_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/DocumentReference/document_reference_POST.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/DocumentReference/document_reference_PUT.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Events/comment_event_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Events/event_action.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Events/topic_event_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/File/file_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/File/file_PUT.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/File/project_file_display_information.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/File/project_file_information.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/File/project_files_information_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/RelatedTopic/related_topic_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/RelatedTopic/related_topic_PUT.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Topic/bim_snippet.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Topic/topic_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Topic/topic_POST.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Topic/topic_PUT.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/bitmap_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/bitmap_POST.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/clipping_plane.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/coloring.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/coloring_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/component.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/component_list.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/components.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/direction.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/line.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/location.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/orthogonal_camera.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/perspective_camera.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/point.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/selection_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/snapshot_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/snapshot_POST.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/view_setup_hints.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/viewpoint_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/viewpoint_POST.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/visibility.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Collaboration/Viewpoint/visibility_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Project/extensions_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Project/project_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/Project/project_PUT.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/User/user_GET.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/schemas/error.json (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/success.txt (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/bcf/templates/base.html (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/forms.py (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/models.py (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/oauth2.py (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/routes.py (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/templates/base.html (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/templates/clientdata.html (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/templates/createclient.html (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/templates/index.html (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/templates/login.html (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/templates/oauth.html (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/templates/ologin.html (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/foundation/templates/register.html (100%) rename src/{foundationserver/bcfserver => opencdeserver/opencdeserver}/run.py (100%) rename src/{foundationserver => opencdeserver}/requirements.txt (100%) diff --git a/src/bcf/src/bcf/v3/bcfapi.py b/src/bcf/src/bcf/v3/bcfapi.py index 0ec2cf262f..a6fa9907e9 100644 --- a/src/bcf/src/bcf/v3/bcfapi.py +++ b/src/bcf/src/bcf/v3/bcfapi.py @@ -6,9 +6,8 @@ import requests import webbrowser import http.server import base64 - -from werkzeug.datastructures import HeaderSet - +import tempfile +import os client_id, client_secret = "", "" @@ -135,6 +134,7 @@ class BcfClient: self.foundation_client = foundation_client self.version_id = None self.baseurl = None + self.filepath = tempfile.mkdtemp() def set_version(self, version): self.version_id = version["version_id"] @@ -282,7 +282,7 @@ class BcfClient: headers=headers, ) # TODO: write to tmpdir - with open(f"{project_id}_{topic_id}_snippet.txt", "w") as f: + with open(os.path.join(self.filepath, f"{project_id}_{topic_id}_snippet.txt"), "wb") as f: f.write(response.content.decode("utf-8")) return response.status_code, response.content @@ -544,7 +544,7 @@ class BcfClient: f"{self.baseurl}/projects/{project_id}/topics/documents/{document_id}", headers=headers, ) - with open(f"{project_id}_{topic_id}_{document_id}_document.txt", "w") as f: + with open(os.path.join(self.filepath, f"{project_id}_{topic_id}_{document_id}_document.txt"), "wb") as f: f.write(response.content.decode("utf-8")) return response.status_code, response.content diff --git a/src/foundationserver/README.md b/src/opencdeserver/README.md similarity index 100% rename from src/foundationserver/README.md rename to src/opencdeserver/README.md diff --git a/src/foundationserver/bcfserver/bcf/project.json b/src/opencdeserver/opencdeserver/bcf/project.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/project.json rename to src/opencdeserver/opencdeserver/bcf/project.json diff --git a/src/foundationserver/bcfserver/bcf/routes.py b/src/opencdeserver/opencdeserver/bcf/routes.py similarity index 100% rename from src/foundationserver/bcfserver/bcf/routes.py rename to src/opencdeserver/opencdeserver/bcf/routes.py diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Action/comment_actions.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Action/comment_actions.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Action/comment_actions.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Action/comment_actions.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Action/project_actions.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Action/project_actions.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Action/project_actions.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Action/project_actions.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Action/topic_actions.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Action/topic_actions.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Action/topic_actions.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Action/topic_actions.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Action/viewpoint_actions.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Action/viewpoint_actions.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Action/viewpoint_actions.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Action/viewpoint_actions.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Comment/comment_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Comment/comment_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Comment/comment_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Comment/comment_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Comment/comment_POST.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Comment/comment_POST.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Comment/comment_POST.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Comment/comment_POST.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Comment/comment_PUT.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Comment/comment_PUT.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Comment/comment_PUT.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Comment/comment_PUT.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Document/document_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Document/document_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Document/document_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Document/document_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/DocumentReference/document_reference_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/DocumentReference/document_reference_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/DocumentReference/document_reference_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/DocumentReference/document_reference_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/DocumentReference/document_reference_POST.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/DocumentReference/document_reference_POST.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/DocumentReference/document_reference_POST.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/DocumentReference/document_reference_POST.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/DocumentReference/document_reference_PUT.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/DocumentReference/document_reference_PUT.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/DocumentReference/document_reference_PUT.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/DocumentReference/document_reference_PUT.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Events/comment_event_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Events/comment_event_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Events/comment_event_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Events/comment_event_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Events/event_action.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Events/event_action.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Events/event_action.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Events/event_action.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Events/topic_event_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Events/topic_event_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Events/topic_event_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Events/topic_event_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/File/file_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/File/file_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/File/file_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/File/file_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/File/file_PUT.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/File/file_PUT.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/File/file_PUT.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/File/file_PUT.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/File/project_file_display_information.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/File/project_file_display_information.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/File/project_file_display_information.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/File/project_file_display_information.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/File/project_file_information.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/File/project_file_information.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/File/project_file_information.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/File/project_file_information.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/File/project_files_information_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/File/project_files_information_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/File/project_files_information_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/File/project_files_information_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/RelatedTopic/related_topic_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/RelatedTopic/related_topic_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/RelatedTopic/related_topic_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/RelatedTopic/related_topic_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/RelatedTopic/related_topic_PUT.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/RelatedTopic/related_topic_PUT.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/RelatedTopic/related_topic_PUT.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/RelatedTopic/related_topic_PUT.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Topic/bim_snippet.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Topic/bim_snippet.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Topic/bim_snippet.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Topic/bim_snippet.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Topic/topic_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Topic/topic_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Topic/topic_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Topic/topic_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Topic/topic_POST.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Topic/topic_POST.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Topic/topic_POST.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Topic/topic_POST.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Topic/topic_PUT.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Topic/topic_PUT.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Topic/topic_PUT.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Topic/topic_PUT.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/bitmap_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/bitmap_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/bitmap_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/bitmap_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/bitmap_POST.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/bitmap_POST.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/bitmap_POST.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/bitmap_POST.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/clipping_plane.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/clipping_plane.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/clipping_plane.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/clipping_plane.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/coloring.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/coloring.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/coloring.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/coloring.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/coloring_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/coloring_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/coloring_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/coloring_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/component.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/component.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/component.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/component.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/component_list.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/component_list.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/component_list.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/component_list.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/components.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/components.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/components.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/components.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/direction.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/direction.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/direction.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/direction.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/line.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/line.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/line.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/line.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/location.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/location.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/location.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/location.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/orthogonal_camera.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/orthogonal_camera.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/orthogonal_camera.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/orthogonal_camera.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/perspective_camera.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/perspective_camera.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/perspective_camera.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/perspective_camera.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/point.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/point.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/point.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/point.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/selection_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/selection_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/selection_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/selection_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/snapshot_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/snapshot_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/snapshot_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/snapshot_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/snapshot_POST.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/snapshot_POST.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/snapshot_POST.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/snapshot_POST.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/view_setup_hints.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/view_setup_hints.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/view_setup_hints.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/view_setup_hints.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/viewpoint_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/viewpoint_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/viewpoint_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/viewpoint_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/viewpoint_POST.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/viewpoint_POST.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/viewpoint_POST.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/viewpoint_POST.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/visibility.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/visibility.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/visibility.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/visibility.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/visibility_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/visibility_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Collaboration/Viewpoint/visibility_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Collaboration/Viewpoint/visibility_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Project/extensions_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Project/extensions_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Project/extensions_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Project/extensions_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Project/project_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/Project/project_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Project/project_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Project/project_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/Project/project_PUT.json b/src/opencdeserver/opencdeserver/bcf/schemas/Project/project_PUT.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/Project/project_PUT.json rename to src/opencdeserver/opencdeserver/bcf/schemas/Project/project_PUT.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/User/user_GET.json b/src/opencdeserver/opencdeserver/bcf/schemas/User/user_GET.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/User/user_GET.json rename to src/opencdeserver/opencdeserver/bcf/schemas/User/user_GET.json diff --git a/src/foundationserver/bcfserver/bcf/schemas/error.json b/src/opencdeserver/opencdeserver/bcf/schemas/error.json similarity index 100% rename from src/foundationserver/bcfserver/bcf/schemas/error.json rename to src/opencdeserver/opencdeserver/bcf/schemas/error.json diff --git a/src/foundationserver/bcfserver/bcf/success.txt b/src/opencdeserver/opencdeserver/bcf/success.txt similarity index 100% rename from src/foundationserver/bcfserver/bcf/success.txt rename to src/opencdeserver/opencdeserver/bcf/success.txt diff --git a/src/foundationserver/bcfserver/bcf/templates/base.html b/src/opencdeserver/opencdeserver/bcf/templates/base.html similarity index 100% rename from src/foundationserver/bcfserver/bcf/templates/base.html rename to src/opencdeserver/opencdeserver/bcf/templates/base.html diff --git a/src/foundationserver/bcfserver/foundation/forms.py b/src/opencdeserver/opencdeserver/foundation/forms.py similarity index 100% rename from src/foundationserver/bcfserver/foundation/forms.py rename to src/opencdeserver/opencdeserver/foundation/forms.py diff --git a/src/foundationserver/bcfserver/foundation/models.py b/src/opencdeserver/opencdeserver/foundation/models.py similarity index 100% rename from src/foundationserver/bcfserver/foundation/models.py rename to src/opencdeserver/opencdeserver/foundation/models.py diff --git a/src/foundationserver/bcfserver/foundation/oauth2.py b/src/opencdeserver/opencdeserver/foundation/oauth2.py similarity index 100% rename from src/foundationserver/bcfserver/foundation/oauth2.py rename to src/opencdeserver/opencdeserver/foundation/oauth2.py diff --git a/src/foundationserver/bcfserver/foundation/routes.py b/src/opencdeserver/opencdeserver/foundation/routes.py similarity index 100% rename from src/foundationserver/bcfserver/foundation/routes.py rename to src/opencdeserver/opencdeserver/foundation/routes.py diff --git a/src/foundationserver/bcfserver/foundation/templates/base.html b/src/opencdeserver/opencdeserver/foundation/templates/base.html similarity index 100% rename from src/foundationserver/bcfserver/foundation/templates/base.html rename to src/opencdeserver/opencdeserver/foundation/templates/base.html diff --git a/src/foundationserver/bcfserver/foundation/templates/clientdata.html b/src/opencdeserver/opencdeserver/foundation/templates/clientdata.html similarity index 100% rename from src/foundationserver/bcfserver/foundation/templates/clientdata.html rename to src/opencdeserver/opencdeserver/foundation/templates/clientdata.html diff --git a/src/foundationserver/bcfserver/foundation/templates/createclient.html b/src/opencdeserver/opencdeserver/foundation/templates/createclient.html similarity index 100% rename from src/foundationserver/bcfserver/foundation/templates/createclient.html rename to src/opencdeserver/opencdeserver/foundation/templates/createclient.html diff --git a/src/foundationserver/bcfserver/foundation/templates/index.html b/src/opencdeserver/opencdeserver/foundation/templates/index.html similarity index 100% rename from src/foundationserver/bcfserver/foundation/templates/index.html rename to src/opencdeserver/opencdeserver/foundation/templates/index.html diff --git a/src/foundationserver/bcfserver/foundation/templates/login.html b/src/opencdeserver/opencdeserver/foundation/templates/login.html similarity index 100% rename from src/foundationserver/bcfserver/foundation/templates/login.html rename to src/opencdeserver/opencdeserver/foundation/templates/login.html diff --git a/src/foundationserver/bcfserver/foundation/templates/oauth.html b/src/opencdeserver/opencdeserver/foundation/templates/oauth.html similarity index 100% rename from src/foundationserver/bcfserver/foundation/templates/oauth.html rename to src/opencdeserver/opencdeserver/foundation/templates/oauth.html diff --git a/src/foundationserver/bcfserver/foundation/templates/ologin.html b/src/opencdeserver/opencdeserver/foundation/templates/ologin.html similarity index 100% rename from src/foundationserver/bcfserver/foundation/templates/ologin.html rename to src/opencdeserver/opencdeserver/foundation/templates/ologin.html diff --git a/src/foundationserver/bcfserver/foundation/templates/register.html b/src/opencdeserver/opencdeserver/foundation/templates/register.html similarity index 100% rename from src/foundationserver/bcfserver/foundation/templates/register.html rename to src/opencdeserver/opencdeserver/foundation/templates/register.html diff --git a/src/foundationserver/bcfserver/run.py b/src/opencdeserver/opencdeserver/run.py similarity index 100% rename from src/foundationserver/bcfserver/run.py rename to src/opencdeserver/opencdeserver/run.py diff --git a/src/foundationserver/requirements.txt b/src/opencdeserver/requirements.txt similarity index 100% rename from src/foundationserver/requirements.txt rename to src/opencdeserver/requirements.txt From 6addbd935f0e28b2540ef245aea503bcb218b631 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sun, 29 Aug 2021 16:51:56 +1000 Subject: [PATCH 036/113] Add copyright boilerplate to BCF and OpenCDE code --- src/bcf/src/bcf/__init__.py | 17 +++++++++++++++++ src/bcf/src/bcf/bcfxml.py | 19 +++++++++++++++++++ src/bcf/src/bcf/v2/__init__.py | 19 +++++++++++++++++++ src/bcf/src/bcf/v2/bcfxml.py | 19 +++++++++++++++++++ src/bcf/src/bcf/v2/data.py | 19 +++++++++++++++++++ src/bcf/src/bcf/v3/__init__.py | 19 +++++++++++++++++++ src/bcf/src/bcf/v3/bcfapi.py | 19 +++++++++++++++++++ src/bcf/src/bcf/v3/bcfxml.py | 19 +++++++++++++++++++ src/bcf/src/bcf/v3/data.py | 19 +++++++++++++++++++ src/opencdeserver/opencdeserver/bcf/routes.py | 19 +++++++++++++++++++ .../opencdeserver/bcf/templates/base.html | 19 +++++++++++++++++++ .../opencdeserver/foundation/forms.py | 19 +++++++++++++++++++ .../opencdeserver/foundation/models.py | 19 +++++++++++++++++++ .../opencdeserver/foundation/oauth2.py | 19 +++++++++++++++++++ .../opencdeserver/foundation/routes.py | 19 +++++++++++++++++++ .../foundation/templates/base.html | 19 +++++++++++++++++++ .../foundation/templates/clientdata.html | 19 +++++++++++++++++++ .../foundation/templates/createclient.html | 19 +++++++++++++++++++ .../foundation/templates/index.html | 19 +++++++++++++++++++ .../foundation/templates/login.html | 19 +++++++++++++++++++ .../foundation/templates/oauth.html | 19 +++++++++++++++++++ .../foundation/templates/ologin.html | 19 +++++++++++++++++++ .../foundation/templates/register.html | 19 +++++++++++++++++++ src/opencdeserver/opencdeserver/run.py | 19 +++++++++++++++++++ 24 files changed, 454 insertions(+) diff --git a/src/bcf/src/bcf/__init__.py b/src/bcf/src/bcf/__init__.py index e69de29bb2..a91684cdf7 100644 --- a/src/bcf/src/bcf/__init__.py +++ b/src/bcf/src/bcf/__init__.py @@ -0,0 +1,17 @@ +# BCF - BCF Python library +# Copyright (C) 2021 Prabhat Singh +# +# This file is part of BCF. +# +# BCF is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BCF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with BCF. If not, see . diff --git a/src/bcf/src/bcf/bcfxml.py b/src/bcf/src/bcf/bcfxml.py index 513c64cdf2..e9bcde349f 100644 --- a/src/bcf/src/bcf/bcfxml.py +++ b/src/bcf/src/bcf/bcfxml.py @@ -1,3 +1,22 @@ +# BCF - BCF Python library +# Copyright (C) 2021 Prabhat Singh +# +# This file is part of BCF. +# +# BCF is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BCF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with BCF. If not, see . + + import os.path import zipfile import tempfile diff --git a/src/bcf/src/bcf/v2/__init__.py b/src/bcf/src/bcf/v2/__init__.py index e69de29bb2..adb79b4034 100644 --- a/src/bcf/src/bcf/v2/__init__.py +++ b/src/bcf/src/bcf/v2/__init__.py @@ -0,0 +1,19 @@ + +# BCF - BCF Python library +# Copyright (C) 2020, 2021 Dion Moult +# +# This file is part of BCF. +# +# BCF is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BCF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with BCF. If not, see . + diff --git a/src/bcf/src/bcf/v2/bcfxml.py b/src/bcf/src/bcf/v2/bcfxml.py index 3b4dd722b0..76c094d69e 100644 --- a/src/bcf/src/bcf/v2/bcfxml.py +++ b/src/bcf/src/bcf/v2/bcfxml.py @@ -1,3 +1,22 @@ + +# BCF - BCF Python library +# Copyright (C) 2020, 2021 Dion Moult +# +# This file is part of BCF. +# +# BCF is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BCF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with BCF. If not, see . + import os import uuid import shutil diff --git a/src/bcf/src/bcf/v2/data.py b/src/bcf/src/bcf/v2/data.py index 534aacaffc..e79352dbd0 100644 --- a/src/bcf/src/bcf/v2/data.py +++ b/src/bcf/src/bcf/v2/data.py @@ -1,3 +1,22 @@ + +# BCF - BCF Python library +# Copyright (C) 2020, 2021 Dion Moult +# +# This file is part of BCF. +# +# BCF is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BCF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with BCF. If not, see . + class Project: def __init__(self): self.project_id = "" diff --git a/src/bcf/src/bcf/v3/__init__.py b/src/bcf/src/bcf/v3/__init__.py index e69de29bb2..5828265521 100644 --- a/src/bcf/src/bcf/v3/__init__.py +++ b/src/bcf/src/bcf/v3/__init__.py @@ -0,0 +1,19 @@ + +# BCF - BCF Python library +# Copyright (C) 2021 Prabhat Singh +# +# This file is part of BCF. +# +# BCF is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BCF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with BCF. If not, see . + diff --git a/src/bcf/src/bcf/v3/bcfapi.py b/src/bcf/src/bcf/v3/bcfapi.py index a6fa9907e9..308f323eb8 100644 --- a/src/bcf/src/bcf/v3/bcfapi.py +++ b/src/bcf/src/bcf/v3/bcfapi.py @@ -1,3 +1,22 @@ + +# BCF - BCF Python library +# Copyright (C) 2021 Prabhat Singh +# +# This file is part of BCF. +# +# BCF is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BCF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with BCF. If not, see . + import uuid import time import json diff --git a/src/bcf/src/bcf/v3/bcfxml.py b/src/bcf/src/bcf/v3/bcfxml.py index 6298260d3d..9d9578e576 100644 --- a/src/bcf/src/bcf/v3/bcfxml.py +++ b/src/bcf/src/bcf/v3/bcfxml.py @@ -1,3 +1,22 @@ + +# BCF - BCF Python library +# Copyright (C) 2021 Prabhat Singh +# +# This file is part of BCF. +# +# BCF is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BCF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with BCF. If not, see . + import os import uuid import shutil diff --git a/src/bcf/src/bcf/v3/data.py b/src/bcf/src/bcf/v3/data.py index b6f241e148..a166a3b1b2 100644 --- a/src/bcf/src/bcf/v3/data.py +++ b/src/bcf/src/bcf/v3/data.py @@ -1,3 +1,22 @@ + +# BCF - BCF Python library +# Copyright (C) 2021 Prabhat Singh +# +# This file is part of BCF. +# +# BCF is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BCF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with BCF. If not, see . + class Project: def __init__(self): self.project_id = "" diff --git a/src/opencdeserver/opencdeserver/bcf/routes.py b/src/opencdeserver/opencdeserver/bcf/routes.py index d9a1450bc7..fb0779dbc5 100644 --- a/src/opencdeserver/opencdeserver/bcf/routes.py +++ b/src/opencdeserver/opencdeserver/bcf/routes.py @@ -1,3 +1,22 @@ + +# OpenCDE - OpenCDE Python implementation +# Copyright (C) 2021 Prabhat Singh +# +# This file is part of OpenCDE. +# +# OpenCDE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenCDE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with OpenCDE. If not, see . + from flask import jsonify, url_for, redirect, render_template, request, session, send_file from flask_login import login_user, logout_user, login_required, current_user from flask.blueprints import Blueprint diff --git a/src/opencdeserver/opencdeserver/bcf/templates/base.html b/src/opencdeserver/opencdeserver/bcf/templates/base.html index e69de29bb2..59ae9b37da 100644 --- a/src/opencdeserver/opencdeserver/bcf/templates/base.html +++ b/src/opencdeserver/opencdeserver/bcf/templates/base.html @@ -0,0 +1,19 @@ + diff --git a/src/opencdeserver/opencdeserver/foundation/forms.py b/src/opencdeserver/opencdeserver/foundation/forms.py index d326108a6e..2f86536b8d 100644 --- a/src/opencdeserver/opencdeserver/foundation/forms.py +++ b/src/opencdeserver/opencdeserver/foundation/forms.py @@ -1,3 +1,22 @@ + +# OpenCDE - OpenCDE Python implementation +# Copyright (C) 2021 Prabhat Singh +# +# This file is part of OpenCDE. +# +# OpenCDE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenCDE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with OpenCDE. If not, see . + from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms.validators import DataRequired, Length, Email, Regexp, EqualTo diff --git a/src/opencdeserver/opencdeserver/foundation/models.py b/src/opencdeserver/opencdeserver/foundation/models.py index 4eef828f05..db451e4022 100644 --- a/src/opencdeserver/opencdeserver/foundation/models.py +++ b/src/opencdeserver/opencdeserver/foundation/models.py @@ -1,3 +1,22 @@ + +# OpenCDE - OpenCDE Python implementation +# Copyright (C) 2021 Prabhat Singh +# +# This file is part of OpenCDE. +# +# OpenCDE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenCDE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with OpenCDE. If not, see . + from run import db, bcrypt, login_manager import time from authlib.integrations.sqla_oauth2 import ( diff --git a/src/opencdeserver/opencdeserver/foundation/oauth2.py b/src/opencdeserver/opencdeserver/foundation/oauth2.py index b0d9e24b9f..bc69b28c0e 100644 --- a/src/opencdeserver/opencdeserver/foundation/oauth2.py +++ b/src/opencdeserver/opencdeserver/foundation/oauth2.py @@ -1,3 +1,22 @@ + +# OpenCDE - OpenCDE Python implementation +# Copyright (C) 2021 Prabhat Singh +# +# This file is part of OpenCDE. +# +# OpenCDE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenCDE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with OpenCDE. If not, see . + from authlib.integrations.flask_oauth2 import ( AuthorizationServer, ResourceProtector, diff --git a/src/opencdeserver/opencdeserver/foundation/routes.py b/src/opencdeserver/opencdeserver/foundation/routes.py index 9fdb7957bb..4f817b3d70 100644 --- a/src/opencdeserver/opencdeserver/foundation/routes.py +++ b/src/opencdeserver/opencdeserver/foundation/routes.py @@ -1,3 +1,22 @@ + +# OpenCDE - OpenCDE Python implementation +# Copyright (C) 2021 Prabhat Singh +# +# This file is part of OpenCDE. +# +# OpenCDE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenCDE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with OpenCDE. If not, see . + from flask.blueprints import Blueprint from flask import render_template, redirect, url_for, flash, request from werkzeug import datastructures diff --git a/src/opencdeserver/opencdeserver/foundation/templates/base.html b/src/opencdeserver/opencdeserver/foundation/templates/base.html index 534b7f5c43..70cb6009c5 100644 --- a/src/opencdeserver/opencdeserver/foundation/templates/base.html +++ b/src/opencdeserver/opencdeserver/foundation/templates/base.html @@ -1,3 +1,22 @@ + diff --git a/src/opencdeserver/opencdeserver/foundation/templates/clientdata.html b/src/opencdeserver/opencdeserver/foundation/templates/clientdata.html index ee434e5861..c78d37c989 100644 --- a/src/opencdeserver/opencdeserver/foundation/templates/clientdata.html +++ b/src/opencdeserver/opencdeserver/foundation/templates/clientdata.html @@ -1,3 +1,22 @@ + {%extends 'base.html'%} {%block content%} {% if user %}