mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 02:47:48 +00:00
merge update
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,632 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the Lesser GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3.0 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 *
|
||||
* Lesser GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/xml_parser.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include "XmlSerializer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "../../ifcparse/IfcSIPrefix.h"
|
||||
#include "../../ifcgeom/IfcGeom.h"
|
||||
#include "../../ifcparse/utils.h"
|
||||
|
||||
using boost::property_tree::ptree;
|
||||
|
||||
#include "XmlSerializer.h"
|
||||
|
||||
namespace {
|
||||
struct MAKE_TYPE_NAME(factory_t) {
|
||||
XmlSerializer* operator()(IfcParse::IfcFile* file, const std::string& xml_filename) const {
|
||||
MAKE_TYPE_NAME(XmlSerializer)* s = new MAKE_TYPE_NAME(XmlSerializer)(file, xml_filename);
|
||||
s->setFile(file);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void MAKE_INIT_FN(XmlSerializer)(XmlSerializerFactory::Factory* mapping) {
|
||||
static const std::string schema_name = STRINGIFY(IfcSchema);
|
||||
MAKE_TYPE_NAME(factory_t) factory;
|
||||
mapping->bind(schema_name, factory);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO: Make this a member of XmlSerializer?
|
||||
std::map<std::string, std::string> MAKE_TYPE_NAME(argument_name_map);
|
||||
|
||||
// Format an IFC attribute and maybe returns as string. Only literal scalar
|
||||
// values are converted. Things like entity instances and lists are omitted.
|
||||
boost::optional<std::string> format_attribute(const Argument* argument, IfcUtil::ArgumentType argument_type, const std::string& argument_name) {
|
||||
boost::optional<std::string> value;
|
||||
|
||||
// Hard-code lat-lon as it represents an array
|
||||
// of integers best emitted as a single decimal
|
||||
if (argument_name == "IfcSite.RefLatitude" ||
|
||||
argument_name == "IfcSite.RefLongitude")
|
||||
{
|
||||
std::vector<int> angle = *argument;
|
||||
double deg;
|
||||
if (angle.size() >= 3) {
|
||||
deg = angle[0] + angle[1] / 60. + angle[2] / 3600.;
|
||||
int prec = 8;
|
||||
if (angle.size() == 4) {
|
||||
deg += angle[3] / (1000000. * 3600.);
|
||||
prec = 14;
|
||||
}
|
||||
std::stringstream stream;
|
||||
stream << std::setprecision(prec) << deg;
|
||||
value = stream.str();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
switch (argument_type) {
|
||||
case IfcUtil::Argument_BOOL: {
|
||||
const bool b = *argument;
|
||||
value = b ? "true" : "false";
|
||||
break; }
|
||||
case IfcUtil::Argument_DOUBLE: {
|
||||
const double d = *argument;
|
||||
std::stringstream stream;
|
||||
stream << d;
|
||||
value = stream.str();
|
||||
break; }
|
||||
case IfcUtil::Argument_STRING:
|
||||
case IfcUtil::Argument_ENUMERATION: {
|
||||
value = static_cast<std::string>(*argument);
|
||||
break; }
|
||||
case IfcUtil::Argument_INT: {
|
||||
const int v = *argument;
|
||||
std::stringstream stream;
|
||||
stream << v;
|
||||
value = stream.str();
|
||||
break; }
|
||||
case IfcUtil::Argument_ENTITY_INSTANCE: {
|
||||
IfcUtil::IfcBaseClass* e = *argument;
|
||||
if (!e->declaration().as_entity()) {
|
||||
IfcUtil::IfcBaseType* f = (IfcUtil::IfcBaseType*) e;
|
||||
value = format_attribute(f->data().getArgument(0), f->data().getArgument(0)->type(), argument_name);
|
||||
}
|
||||
else if (e->declaration().is(IfcSchema::IfcSIUnit::Class()) || e->declaration().is(IfcSchema::IfcConversionBasedUnit::Class())) {
|
||||
// Some string concatenation to have a unit name as a XML attribute.
|
||||
|
||||
std::string unit_name;
|
||||
|
||||
if (e->declaration().is(IfcSchema::IfcSIUnit::Class())) {
|
||||
IfcSchema::IfcSIUnit* unit = (IfcSchema::IfcSIUnit*) e;
|
||||
unit_name = IfcSchema::IfcSIUnitName::ToString(unit->Name());
|
||||
if (unit->hasPrefix()) {
|
||||
unit_name = IfcSchema::IfcSIPrefix::ToString(unit->Prefix()) + unit_name;
|
||||
}
|
||||
}
|
||||
else {
|
||||
IfcSchema::IfcConversionBasedUnit* unit = (IfcSchema::IfcConversionBasedUnit*) e;
|
||||
unit_name = unit->Name();
|
||||
}
|
||||
|
||||
value = unit_name;
|
||||
}
|
||||
else if (e->declaration().is(IfcSchema::IfcLocalPlacement::Class())) {
|
||||
IfcSchema::IfcLocalPlacement* placement = e->as<IfcSchema::IfcLocalPlacement>();
|
||||
gp_Trsf trsf;
|
||||
IfcGeom::MAKE_TYPE_NAME(Kernel) kernel;
|
||||
|
||||
if (kernel.convert(placement, trsf)) {
|
||||
std::stringstream stream;
|
||||
for (int i = 1; i < 5; ++i) {
|
||||
for (int j = 1; j < 4; ++j) {
|
||||
const double trsf_value = trsf.Value(j, i);
|
||||
stream << trsf_value << " ";
|
||||
}
|
||||
stream << ((i == 4) ? "1" : "0 ");
|
||||
}
|
||||
value = stream.str();
|
||||
}
|
||||
}
|
||||
break; }
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Appends to a node with possibly existing attributes
|
||||
ptree* format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& child, ptree& tree, bool as_link = false) {
|
||||
const unsigned n = instance->declaration().attribute_count();
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
try {
|
||||
instance->data().getArgument(i);
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
Logger::Error("Expected " + boost::lexical_cast<std::string>(n) + " attributes for:", instance);
|
||||
break;
|
||||
}
|
||||
const Argument* argument = instance->data().getArgument(i);
|
||||
if (argument->isNull()) continue;
|
||||
|
||||
std::string argument_name = instance->declaration().attribute_by_index(i)->name();
|
||||
std::map<std::string, std::string>::const_iterator argument_name_it;
|
||||
argument_name_it = MAKE_TYPE_NAME(argument_name_map).find(argument_name);
|
||||
if (argument_name_it != MAKE_TYPE_NAME(argument_name_map).end()) {
|
||||
argument_name = argument_name_it->second;
|
||||
}
|
||||
const IfcUtil::ArgumentType argument_type = instance->data().getArgument(i)->type();
|
||||
|
||||
const std::string qualified_name = instance->declaration().name() + "." + argument_name;
|
||||
boost::optional<std::string> value;
|
||||
try {
|
||||
value = format_attribute(argument, argument_type, qualified_name);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
}
|
||||
catch (const Standard_ConstructionError& e) {
|
||||
Logger::Error(e.GetMessageString(), instance);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (as_link) {
|
||||
if (argument_name == "id") {
|
||||
child.put("<xmlattr>.xlink:href", std::string("#") + *value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::stringstream stream;
|
||||
stream << "<xmlattr>." << argument_name;
|
||||
child.put(stream.str(), *value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return &tree.add_child(instance->declaration().name(), child);
|
||||
}
|
||||
|
||||
// Formats an entity instances as a ptree node, and insert into the DOM. Recurses
|
||||
// over the entity attributes and writes them as xml attributes of the node.
|
||||
ptree* format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& tree, bool as_link = false) {
|
||||
ptree child;
|
||||
return format_entity_instance(instance, child, tree, as_link);
|
||||
}
|
||||
|
||||
std::string qualify_unrooted_instance(IfcUtil::IfcBaseClass* inst) {
|
||||
return inst->declaration().name() + "_" + boost::lexical_cast<std::string>(inst->data().id());
|
||||
}
|
||||
|
||||
// A function to be called recursively. Template specialization is used
|
||||
// to descend into decomposition, containment and property relationships.
|
||||
template <typename A>
|
||||
ptree* descend(A* instance, ptree& tree, IfcUtil::IfcBaseClass* parent = nullptr) {
|
||||
if (instance->declaration().is(IfcSchema::IfcObjectDefinition::Class())) {
|
||||
return descend(instance->template as<IfcSchema::IfcObjectDefinition>(), tree, parent);
|
||||
}
|
||||
else {
|
||||
return format_entity_instance(instance, tree);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns related entity instances using IFC's objectified relationship
|
||||
// model. The second and third argument require a member function pointer.
|
||||
template <typename T, typename U, typename V, typename F, typename G>
|
||||
typename V::list::ptr get_related(T* t, F f, G g) {
|
||||
typename U::list::ptr li = (*t.*f)()->template as<U>();
|
||||
typename V::list::ptr acc(new typename V::list);
|
||||
for (typename U::list::it it = li->begin(); it != li->end(); ++it) {
|
||||
U* u = *it;
|
||||
acc->push((*u.*g)()->template as<V>());
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
// Descends into the tree by recursing into IfcRelContainedInSpatialStructure,
|
||||
// IfcRelDecomposes, IfcRelDefinesByType, IfcRelDefinesByProperties relations.
|
||||
template <>
|
||||
ptree* descend(IfcSchema::IfcObjectDefinition* product, ptree& tree, IfcUtil::IfcBaseClass* parent) {
|
||||
if (product->declaration().is(IfcSchema::IfcElement::Class())) {
|
||||
auto voids = product->as<IfcSchema::IfcElement>()->FillsVoids();
|
||||
if (voids && voids->size() == 1 && (*voids->begin())->RelatingOpeningElement() != parent) {
|
||||
// Fills are placed under their corresponding opening, return early to avoid duplication.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ptree& child = *format_entity_instance(product, tree);
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcOpeningElement::Class())) {
|
||||
IfcSchema::IfcOpeningElement* opening = static_cast<IfcSchema::IfcOpeningElement*>(product);
|
||||
IfcSchema::IfcElement::list::ptr fills = get_related<IfcSchema::IfcOpeningElement, IfcSchema::IfcRelFillsElement, IfcSchema::IfcElement>(
|
||||
opening, &IfcSchema::IfcOpeningElement::HasFillings, &IfcSchema::IfcRelFillsElement::RelatedBuildingElement);
|
||||
|
||||
for (IfcSchema::IfcElement::list::it it = fills->begin(); it != fills->end(); ++it) {
|
||||
descend(*it, child, product);
|
||||
}
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcSpatialStructureElement::Class())) {
|
||||
IfcSchema::IfcSpatialStructureElement* structure = (IfcSchema::IfcSpatialStructureElement*) product;
|
||||
|
||||
IfcSchema::IfcObjectDefinition::list::ptr elements = get_related
|
||||
<IfcSchema::IfcSpatialStructureElement, IfcSchema::IfcRelContainedInSpatialStructure, IfcSchema::IfcObjectDefinition>
|
||||
(structure, &IfcSchema::IfcSpatialStructureElement::ContainsElements, &IfcSchema::IfcRelContainedInSpatialStructure::RelatedElements);
|
||||
|
||||
for (IfcSchema::IfcObjectDefinition::list::it it = elements->begin(); it != elements->end(); ++it) {
|
||||
descend(*it, child, product);
|
||||
}
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcElement::Class())) {
|
||||
IfcSchema::IfcElement* element = static_cast<IfcSchema::IfcElement*>(product);
|
||||
IfcSchema::IfcOpeningElement::list::ptr openings = get_related<IfcSchema::IfcElement, IfcSchema::IfcRelVoidsElement, IfcSchema::IfcOpeningElement>(
|
||||
element, &IfcSchema::IfcElement::HasOpenings, &IfcSchema::IfcRelVoidsElement::RelatedOpeningElement);
|
||||
|
||||
for (IfcSchema::IfcOpeningElement::list::it it = openings->begin(); it != openings->end(); ++it) {
|
||||
descend(*it, child, product);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SCHEMA_IfcRelDecomposes_HAS_RelatedObjects
|
||||
IfcSchema::IfcObjectDefinition::list::ptr structures = get_related
|
||||
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelDecomposes, IfcSchema::IfcObjectDefinition>
|
||||
(product, &IfcSchema::IfcObjectDefinition::IsDecomposedBy, &IfcSchema::IfcRelDecomposes::RelatedObjects);
|
||||
#else
|
||||
IfcSchema::IfcObjectDefinition::list::ptr structures = get_related
|
||||
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelAggregates, IfcSchema::IfcObjectDefinition>
|
||||
(product, &IfcSchema::IfcObjectDefinition::IsDecomposedBy, &IfcSchema::IfcRelAggregates::RelatedObjects);
|
||||
|
||||
structures->push(get_related
|
||||
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelNests, IfcSchema::IfcObjectDefinition>
|
||||
(product, &IfcSchema::IfcObjectDefinition::IsNestedBy, &IfcSchema::IfcRelNests::RelatedObjects));
|
||||
#endif
|
||||
|
||||
for (IfcSchema::IfcObjectDefinition::list::it it = structures->begin(); it != structures->end(); ++it) {
|
||||
IfcSchema::IfcObjectDefinition* ob = *it;
|
||||
descend(ob, child, product);
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcObject::Class())) {
|
||||
IfcSchema::IfcObject* object = product->as<IfcSchema::IfcObject>();
|
||||
|
||||
IfcSchema::IfcPropertySetDefinition::list::ptr property_sets = get_related
|
||||
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByProperties, IfcSchema::IfcPropertySetDefinition>
|
||||
(object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByProperties::RelatingPropertyDefinition);
|
||||
|
||||
for (IfcSchema::IfcPropertySetDefinition::list::it it = property_sets->begin(); it != property_sets->end(); ++it) {
|
||||
IfcSchema::IfcPropertySetDefinition* pset = *it;
|
||||
if (pset->declaration().is(IfcSchema::IfcPropertySet::Class())) {
|
||||
format_entity_instance(pset, child, true);
|
||||
}
|
||||
else if (pset->declaration().is(IfcSchema::IfcElementQuantity::Class())) {
|
||||
format_entity_instance(pset, child, true);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SCHEMA_IfcObject_HAS_IsTypedBy
|
||||
IfcSchema::IfcTypeObject::list::ptr types = get_related
|
||||
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByType, IfcSchema::IfcTypeObject>
|
||||
(object, &IfcSchema::IfcObject::IsTypedBy, &IfcSchema::IfcRelDefinesByType::RelatingType);
|
||||
#else
|
||||
IfcSchema::IfcTypeObject::list::ptr types = get_related
|
||||
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByType, IfcSchema::IfcTypeObject>
|
||||
(object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByType::RelatingType);
|
||||
#endif
|
||||
|
||||
for (IfcSchema::IfcTypeObject::list::it it = types->begin(); it != types->end(); ++it) {
|
||||
IfcSchema::IfcTypeObject* type = *it;
|
||||
format_entity_instance(type, child, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcProduct::Class())) {
|
||||
std::map<std::string, IfcUtil::IfcBaseEntity*> layers = IfcGeom::Kernel::get_layers(product);
|
||||
for (std::map<std::string, IfcUtil::IfcBaseEntity*>::const_iterator it = layers.begin(); it != layers.end(); ++it) {
|
||||
// IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier) so use name as the ID.
|
||||
// Note that the IfcPresentationLayerAssignment passed here doesn't really matter as as_link is true
|
||||
// for the format_entity_instance() call.
|
||||
ptree node;
|
||||
node.put("<xmlattr>.xlink:href", "#" + it->first);
|
||||
format_entity_instance(it->second, node, child, true);
|
||||
}
|
||||
|
||||
IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations();
|
||||
for (IfcSchema::IfcRelAssociates::list::it it = associations->begin(); it != associations->end(); ++it) {
|
||||
if ((*it)->as<IfcSchema::IfcRelAssociatesMaterial>()) {
|
||||
IfcSchema::IfcMaterialSelect* mat = (*it)->as<IfcSchema::IfcRelAssociatesMaterial>()->RelatingMaterial();
|
||||
ptree node;
|
||||
node.put("<xmlattr>.xlink:href", "#" + qualify_unrooted_instance(mat));
|
||||
format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, child, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &child;
|
||||
}
|
||||
|
||||
void writeGroupToNode(IfcSchema::IfcGroup* group, ptree& node, std::set<std::string>notRootGroups)
|
||||
{
|
||||
if (notRootGroups.find(group->Name()) != notRootGroups.end()) {
|
||||
return;
|
||||
}
|
||||
ptree* node2 = descend(group, node);//write one group to root
|
||||
auto father = group->IsGroupedBy();
|
||||
for (auto iter = father->begin(); iter != father->end(); iter++)
|
||||
{
|
||||
IfcSchema::IfcRelAssigns* ii = *iter;
|
||||
auto objs = ii->RelatedObjects();
|
||||
for (auto objit = objs->begin(); objit != objs->end(); objit++) {
|
||||
auto entity = *objit;
|
||||
if (entity->declaration().is(IfcSchema::IfcGroup::Class())) {
|
||||
writeGroupToNode(entity->as<IfcSchema::IfcGroup>(), *node2, notRootGroups);
|
||||
notRootGroups.emplace(entity->Name());
|
||||
}
|
||||
else {
|
||||
descend(entity, *node2);//write son to father group
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Format IfcProperty instances and insert into the DOM. IfcComplexProperties are flattened out.
|
||||
void format_properties(IfcSchema::IfcProperty::list::ptr properties, ptree& node) {
|
||||
for (IfcSchema::IfcProperty::list::it it = properties->begin(); it != properties->end(); ++it) {
|
||||
IfcSchema::IfcProperty* p = *it;
|
||||
if (p->declaration().is(IfcSchema::IfcComplexProperty::Class())) {
|
||||
IfcSchema::IfcComplexProperty* complex = (IfcSchema::IfcComplexProperty*) p;
|
||||
format_properties(complex->HasProperties(), node);
|
||||
}
|
||||
else {
|
||||
format_entity_instance(p, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format IfcElementQuantity instances and insert into the DOM.
|
||||
void format_quantities(IfcSchema::IfcPhysicalQuantity::list::ptr quantities, ptree& node) {
|
||||
for (IfcSchema::IfcPhysicalQuantity::list::it it = quantities->begin(); it != quantities->end(); ++it) {
|
||||
IfcSchema::IfcPhysicalQuantity* p = *it;
|
||||
ptree* node2 = format_entity_instance(p, node);
|
||||
if (node2 && p->declaration().is(IfcSchema::IfcPhysicalComplexQuantity::Class())) {
|
||||
IfcSchema::IfcPhysicalComplexQuantity* complex = (IfcSchema::IfcPhysicalComplexQuantity*)p;
|
||||
format_quantities(complex->HasQuantities(), *node2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // ~unnamed namespace
|
||||
|
||||
void MAKE_TYPE_NAME(XmlSerializer)::finalize() {
|
||||
MAKE_TYPE_NAME(argument_name_map).insert(std::make_pair("GlobalId", "id"));
|
||||
|
||||
IfcSchema::IfcProject::list::ptr projects = file->instances_by_type<IfcSchema::IfcProject>();
|
||||
if (projects->size() != 1) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Expected a single IfcProject");
|
||||
return;
|
||||
}
|
||||
IfcSchema::IfcProject* project = *projects->begin();
|
||||
|
||||
ptree root, header, units, decomposition, properties, quantities, types, layers, materials, groups;
|
||||
|
||||
// Write the SPF header as XML nodes.
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_description().description()) {
|
||||
header.add_child("file_description.description", ptree(s));
|
||||
}
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_name().author()) {
|
||||
header.add_child("file_name.author", ptree(s));
|
||||
}
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_name().organization()) {
|
||||
header.add_child("file_name.organization", ptree(s));
|
||||
}
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_schema().schema_identifiers()) {
|
||||
header.add_child("file_schema.schema_identifiers", ptree(s));
|
||||
}
|
||||
try {
|
||||
header.put("file_description.implementation_level", file->header().file_description().implementation_level());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_description implementation_level, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.name", file->header().file_name().name());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name name, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.time_stamp", file->header().file_name().time_stamp());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name time_stamp, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.preprocessor_version", file->header().file_name().preprocessor_version());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name preprocessor_version, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.originating_system", file->header().file_name().originating_system());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name originating_system, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.authorization", file->header().file_name().authorization());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name authorization, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
|
||||
// Descend into the decomposition structure of the IFC file.
|
||||
descend(project, decomposition);
|
||||
|
||||
// Write all property sets and values as XML nodes.
|
||||
IfcSchema::IfcPropertySet::list::ptr psets = file->instances_by_type<IfcSchema::IfcPropertySet>();
|
||||
for (IfcSchema::IfcPropertySet::list::it it = psets->begin(); it != psets->end(); ++it) {
|
||||
IfcSchema::IfcPropertySet* pset = *it;
|
||||
ptree* node = format_entity_instance(pset, properties);
|
||||
if (node) {
|
||||
format_properties(pset->HasProperties(), *node);
|
||||
}
|
||||
}
|
||||
|
||||
// Write all group sets and values as XML nodes.
|
||||
IfcSchema::IfcGroup::list::ptr gsets = file->instances_by_type<IfcSchema::IfcGroup>();
|
||||
std::set<std::string> notRootGroups;//selfname, fathername
|
||||
for (IfcSchema::IfcGroup::list::it it = gsets->begin(); it != gsets->end(); ++it) {
|
||||
writeGroupToNode(*it, groups, notRootGroups);
|
||||
}
|
||||
for (auto it = groups.begin(); it != groups.end();)//root
|
||||
{
|
||||
if (notRootGroups.find(it->second.get<std::string>("<xmlattr>.Name")) != notRootGroups.end()) {
|
||||
it = groups.erase(it);
|
||||
}
|
||||
else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Write all quantities and values as XML nodes.
|
||||
IfcSchema::IfcElementQuantity::list::ptr qtosets = file->instances_by_type<IfcSchema::IfcElementQuantity>();
|
||||
for (IfcSchema::IfcElementQuantity::list::it it = qtosets->begin(); it != qtosets->end(); ++it) {
|
||||
IfcSchema::IfcElementQuantity* qto = *it;
|
||||
ptree* node = format_entity_instance(qto, quantities);
|
||||
if (node) {
|
||||
format_quantities(qto->Quantities(), *node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Write all type objects as XML nodes.
|
||||
IfcSchema::IfcTypeObject::list::ptr type_objects = file->instances_by_type<IfcSchema::IfcTypeObject>();
|
||||
for (IfcSchema::IfcTypeObject::list::it it = type_objects->begin(); it != type_objects->end(); ++it) {
|
||||
IfcSchema::IfcTypeObject* type_object = *it;
|
||||
ptree* node = descend(type_object, types);
|
||||
|
||||
if (node && type_object->hasHasPropertySets()) {
|
||||
IfcSchema::IfcPropertySetDefinition::list::ptr property_sets = type_object->HasPropertySets();
|
||||
for (IfcSchema::IfcPropertySetDefinition::list::it jt = property_sets->begin(); jt != property_sets->end(); ++jt) {
|
||||
IfcSchema::IfcPropertySetDefinition* pset = *jt;
|
||||
if (pset->declaration().is(IfcSchema::IfcPropertySet::Class())) {
|
||||
format_entity_instance(pset, *node, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write all assigned units as XML nodes.
|
||||
IfcEntityList::ptr unit_assignments = project->UnitsInContext()->Units();
|
||||
for (IfcEntityList::it it = unit_assignments->begin(); it != unit_assignments->end(); ++it) {
|
||||
if ((*it)->declaration().is(IfcSchema::IfcNamedUnit::Class())) {
|
||||
IfcSchema::IfcNamedUnit* named_unit = (*it)->as<IfcSchema::IfcNamedUnit>();
|
||||
ptree* node = format_entity_instance(named_unit, units);
|
||||
if (node) {
|
||||
node->put("<xmlattr>.SI_equivalent", IfcParse::get_SI_equivalent<IfcSchema>(named_unit));
|
||||
}
|
||||
}
|
||||
else if ((*it)->declaration().is(IfcSchema::IfcMonetaryUnit::Class())) {
|
||||
format_entity_instance((*it)->as<IfcSchema::IfcMonetaryUnit>(), units);
|
||||
}
|
||||
}
|
||||
|
||||
// Layer assignments. IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier)
|
||||
// so use names as the IDs and only insert those with unique names. In case of possible duplicate names/IDs
|
||||
// the first IfcPresentationLayerAssignment occurrence takes precedence.
|
||||
std::set<std::string> layer_names;
|
||||
IfcSchema::IfcPresentationLayerAssignment::list::ptr layer_assignments = file->instances_by_type<IfcSchema::IfcPresentationLayerAssignment>();
|
||||
for (IfcSchema::IfcPresentationLayerAssignment::list::it it = layer_assignments->begin(); it != layer_assignments->end(); ++it) {
|
||||
const std::string& name = (*it)->Name();
|
||||
if (layer_names.find(name) == layer_names.end()) {
|
||||
layer_names.insert(name);
|
||||
ptree node;
|
||||
node.put("<xmlattr>.id", name);
|
||||
format_entity_instance(*it, node, layers);
|
||||
}
|
||||
}
|
||||
|
||||
IfcSchema::IfcRelAssociatesMaterial::list::ptr materal_associations = file->instances_by_type<IfcSchema::IfcRelAssociatesMaterial>();
|
||||
std::set<IfcSchema::IfcMaterialSelect*> emitted_materials;
|
||||
for (IfcSchema::IfcRelAssociatesMaterial::list::it it = materal_associations->begin(); it != materal_associations->end(); ++it) {
|
||||
IfcSchema::IfcMaterialSelect* mat = (**it).RelatingMaterial();
|
||||
if (emitted_materials.find(mat) == emitted_materials.end()) {
|
||||
emitted_materials.insert(mat);
|
||||
ptree node;
|
||||
node.put("<xmlattr>.id", qualify_unrooted_instance(mat));
|
||||
if (mat->as<IfcSchema::IfcMaterialLayerSetUsage>() || mat->as<IfcSchema::IfcMaterialLayerSet>()) {
|
||||
IfcSchema::IfcMaterialLayerSet* layerset = mat->as<IfcSchema::IfcMaterialLayerSet>();
|
||||
if (!layerset) {
|
||||
layerset = mat->as<IfcSchema::IfcMaterialLayerSetUsage>()->ForLayerSet();
|
||||
}
|
||||
if (layerset->hasLayerSetName()) {
|
||||
node.put("<xmlattr>.LayerSetName", layerset->LayerSetName());
|
||||
}
|
||||
IfcSchema::IfcMaterialLayer::list::ptr ls = layerset->MaterialLayers();
|
||||
for (IfcSchema::IfcMaterialLayer::list::it jt = ls->begin(); jt != ls->end(); ++jt) {
|
||||
ptree subnode;
|
||||
if ((*jt)->hasMaterial()) {
|
||||
subnode.put("<xmlattr>.Name", (*jt)->Material()->Name());
|
||||
}
|
||||
format_entity_instance(*jt, subnode, node);
|
||||
}
|
||||
}
|
||||
else if (mat->as<IfcSchema::IfcMaterialList>()) {
|
||||
IfcSchema::IfcMaterial::list::ptr mats = mat->as<IfcSchema::IfcMaterialList>()->Materials();
|
||||
for (IfcSchema::IfcMaterial::list::it jt = mats->begin(); jt != mats->end(); ++jt) {
|
||||
ptree subnode;
|
||||
format_entity_instance(*jt, subnode, node);
|
||||
}
|
||||
}
|
||||
format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, materials);
|
||||
}
|
||||
}
|
||||
|
||||
root.add_child("ifc.header", header);
|
||||
root.add_child("ifc.units", units);
|
||||
root.add_child("ifc.properties", properties);
|
||||
root.add_child("ifc.quantities", quantities);
|
||||
root.add_child("ifc.types", types);
|
||||
root.add_child("ifc.layers", layers);
|
||||
root.add_child("ifc.groups", groups);
|
||||
root.add_child("ifc.materials", materials);
|
||||
root.add_child("ifc.decomposition", decomposition);
|
||||
|
||||
root.put("ifc.<xmlattr>.xmlns:xlink", "http://www.w3.org/1999/xlink");
|
||||
|
||||
#if BOOST_VERSION >= 105600
|
||||
boost::property_tree::xml_writer_settings<ptree::key_type> settings = boost::property_tree::xml_writer_make_settings<ptree::key_type>('\t', 1);
|
||||
#else
|
||||
boost::property_tree::xml_writer_settings<char> settings('\t', 1);
|
||||
#endif
|
||||
|
||||
std::ofstream f(IfcUtil::path::from_utf8(xml_filename).c_str());
|
||||
boost::property_tree::write_xml(f, root, settings);
|
||||
}
|
||||
@@ -0,0 +1,635 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the Lesser GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3.0 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 *
|
||||
* Lesser GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/xml_parser.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include "XmlSerializer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "../../ifcparse/IfcSIPrefix.h"
|
||||
#include "../../ifcgeom/IfcGeom.h"
|
||||
#include "../../ifcparse/utils.h"
|
||||
|
||||
using boost::property_tree::ptree;
|
||||
|
||||
#include "XmlSerializer.h"
|
||||
|
||||
namespace {
|
||||
struct MAKE_TYPE_NAME(factory_t) {
|
||||
XmlSerializer* operator()(IfcParse::IfcFile* file, const std::string& xml_filename) const {
|
||||
MAKE_TYPE_NAME(XmlSerializer)* s = new MAKE_TYPE_NAME(XmlSerializer)(file, xml_filename);
|
||||
s->setFile(file);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void MAKE_INIT_FN(XmlSerializer)(XmlSerializerFactory::Factory* mapping) {
|
||||
static const std::string schema_name = STRINGIFY(IfcSchema);
|
||||
MAKE_TYPE_NAME(factory_t) factory;
|
||||
mapping->bind(schema_name, factory);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO: Make this a member of XmlSerializer?
|
||||
std::map<std::string, std::string> MAKE_TYPE_NAME(argument_name_map);
|
||||
|
||||
// Format an IFC attribute and maybe returns as string. Only literal scalar
|
||||
// values are converted. Things like entity instances and lists are omitted.
|
||||
boost::optional<std::string> format_attribute(const Argument* argument, IfcUtil::ArgumentType argument_type, const std::string& argument_name) {
|
||||
boost::optional<std::string> value;
|
||||
|
||||
// Hard-code lat-lon as it represents an array
|
||||
// of integers best emitted as a single decimal
|
||||
if (argument_name == "IfcSite.RefLatitude" ||
|
||||
argument_name == "IfcSite.RefLongitude")
|
||||
{
|
||||
std::vector<int> angle = *argument;
|
||||
double deg;
|
||||
if (angle.size() >= 3) {
|
||||
deg = angle[0] + angle[1] / 60. + angle[2] / 3600.;
|
||||
int prec = 8;
|
||||
if (angle.size() == 4) {
|
||||
deg += angle[3] / (1000000. * 3600.);
|
||||
prec = 14;
|
||||
}
|
||||
std::stringstream stream;
|
||||
stream << std::setprecision(prec) << deg;
|
||||
value = stream.str();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
switch (argument_type) {
|
||||
case IfcUtil::Argument_BOOL: {
|
||||
const bool b = *argument;
|
||||
value = b ? "true" : "false";
|
||||
break; }
|
||||
case IfcUtil::Argument_DOUBLE: {
|
||||
const double d = *argument;
|
||||
std::stringstream stream;
|
||||
stream << d;
|
||||
value = stream.str();
|
||||
break; }
|
||||
case IfcUtil::Argument_STRING:
|
||||
case IfcUtil::Argument_ENUMERATION: {
|
||||
value = static_cast<std::string>(*argument);
|
||||
break; }
|
||||
case IfcUtil::Argument_INT: {
|
||||
const int v = *argument;
|
||||
std::stringstream stream;
|
||||
stream << v;
|
||||
value = stream.str();
|
||||
break; }
|
||||
case IfcUtil::Argument_ENTITY_INSTANCE: {
|
||||
IfcUtil::IfcBaseClass* e = *argument;
|
||||
if (!e->declaration().as_entity()) {
|
||||
IfcUtil::IfcBaseType* f = (IfcUtil::IfcBaseType*) e;
|
||||
value = format_attribute(f->data().getArgument(0), f->data().getArgument(0)->type(), argument_name);
|
||||
}
|
||||
else if (e->declaration().is(IfcSchema::IfcSIUnit::Class()) || e->declaration().is(IfcSchema::IfcConversionBasedUnit::Class())) {
|
||||
// Some string concatenation to have a unit name as a XML attribute.
|
||||
|
||||
std::string unit_name;
|
||||
|
||||
if (e->declaration().is(IfcSchema::IfcSIUnit::Class())) {
|
||||
IfcSchema::IfcSIUnit* unit = (IfcSchema::IfcSIUnit*) e;
|
||||
unit_name = IfcSchema::IfcSIUnitName::ToString(unit->Name());
|
||||
if (unit->hasPrefix()) {
|
||||
unit_name = IfcSchema::IfcSIPrefix::ToString(unit->Prefix()) + unit_name;
|
||||
}
|
||||
}
|
||||
else {
|
||||
IfcSchema::IfcConversionBasedUnit* unit = (IfcSchema::IfcConversionBasedUnit*) e;
|
||||
unit_name = unit->Name();
|
||||
}
|
||||
|
||||
value = unit_name;
|
||||
}
|
||||
else if (e->declaration().is(IfcSchema::IfcLocalPlacement::Class())) {
|
||||
IfcSchema::IfcLocalPlacement* placement = e->as<IfcSchema::IfcLocalPlacement>();
|
||||
gp_Trsf trsf;
|
||||
IfcGeom::MAKE_TYPE_NAME(Kernel) kernel;
|
||||
|
||||
if (kernel.convert(placement, trsf)) {
|
||||
std::stringstream stream;
|
||||
for (int i = 1; i < 5; ++i) {
|
||||
for (int j = 1; j < 4; ++j) {
|
||||
const double trsf_value = trsf.Value(j, i);
|
||||
stream << trsf_value << " ";
|
||||
}
|
||||
stream << ((i == 4) ? "1" : "0 ");
|
||||
}
|
||||
value = stream.str();
|
||||
}
|
||||
}
|
||||
break; }
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Appends to a node with possibly existing attributes
|
||||
ptree* format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& child, ptree& tree, bool as_link = false) {
|
||||
const unsigned n = instance->declaration().attribute_count();
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
try {
|
||||
instance->data().getArgument(i);
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
Logger::Error("Expected " + boost::lexical_cast<std::string>(n) + " attributes for:", instance);
|
||||
break;
|
||||
}
|
||||
const Argument* argument = instance->data().getArgument(i);
|
||||
if (argument->isNull()) continue;
|
||||
|
||||
std::string argument_name = instance->declaration().attribute_by_index(i)->name();
|
||||
std::map<std::string, std::string>::const_iterator argument_name_it;
|
||||
argument_name_it = MAKE_TYPE_NAME(argument_name_map).find(argument_name);
|
||||
if (argument_name_it != MAKE_TYPE_NAME(argument_name_map).end()) {
|
||||
argument_name = argument_name_it->second;
|
||||
}
|
||||
const IfcUtil::ArgumentType argument_type = instance->data().getArgument(i)->type();
|
||||
|
||||
const std::string qualified_name = instance->declaration().name() + "." + argument_name;
|
||||
boost::optional<std::string> value;
|
||||
try {
|
||||
value = format_attribute(argument, argument_type, qualified_name);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
}
|
||||
catch (const Standard_ConstructionError& e) {
|
||||
Logger::Error(e.GetMessageString(), instance);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (as_link) {
|
||||
if (argument_name == "id") {
|
||||
child.put("<xmlattr>.xlink:href", std::string("#") + *value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::stringstream stream;
|
||||
stream << "<xmlattr>." << argument_name;
|
||||
child.put(stream.str(), *value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return &tree.add_child(instance->declaration().name(), child);
|
||||
}
|
||||
|
||||
// Formats an entity instances as a ptree node, and insert into the DOM. Recurses
|
||||
// over the entity attributes and writes them as xml attributes of the node.
|
||||
ptree* format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& tree, bool as_link = false) {
|
||||
ptree child;
|
||||
return format_entity_instance(instance, child, tree, as_link);
|
||||
}
|
||||
|
||||
std::string qualify_unrooted_instance(IfcUtil::IfcBaseClass* inst) {
|
||||
return inst->declaration().name() + "_" + boost::lexical_cast<std::string>(inst->data().id());
|
||||
}
|
||||
|
||||
// A function to be called recursively. Template specialization is used
|
||||
// to descend into decomposition, containment and property relationships.
|
||||
template <typename A>
|
||||
ptree* descend(A* instance, ptree& tree, IfcUtil::IfcBaseClass* parent = nullptr) {
|
||||
if (instance->declaration().is(IfcSchema::IfcObjectDefinition::Class())) {
|
||||
return descend(instance->template as<IfcSchema::IfcObjectDefinition>(), tree, parent);
|
||||
}
|
||||
else {
|
||||
return format_entity_instance(instance, tree);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns related entity instances using IFC's objectified relationship
|
||||
// model. The second and third argument require a member function pointer.
|
||||
template <typename T, typename U, typename V, typename F, typename G>
|
||||
typename V::list::ptr get_related(T* t, F f, G g) {
|
||||
typename U::list::ptr li = (*t.*f)()->template as<U>();
|
||||
typename V::list::ptr acc(new typename V::list);
|
||||
for (typename U::list::it it = li->begin(); it != li->end(); ++it) {
|
||||
U* u = *it;
|
||||
acc->push((*u.*g)()->template as<V>());
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
// Descends into the tree by recursing into IfcRelContainedInSpatialStructure,
|
||||
// IfcRelDecomposes, IfcRelDefinesByType, IfcRelDefinesByProperties relations.
|
||||
template <>
|
||||
ptree* descend(IfcSchema::IfcObjectDefinition* product, ptree& tree, IfcUtil::IfcBaseClass* parent) {
|
||||
if (product->declaration().is(IfcSchema::IfcElement::Class())) {
|
||||
auto voids = product->as<IfcSchema::IfcElement>()->FillsVoids();
|
||||
if (voids && voids->size() == 1 && (*voids->begin())->RelatingOpeningElement() != parent) {
|
||||
// Fills are placed under their corresponding opening, return early to avoid duplication.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ptree& child = *format_entity_instance(product, tree);
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcOpeningElement::Class())) {
|
||||
IfcSchema::IfcOpeningElement* opening = static_cast<IfcSchema::IfcOpeningElement*>(product);
|
||||
IfcSchema::IfcElement::list::ptr fills = get_related<IfcSchema::IfcOpeningElement, IfcSchema::IfcRelFillsElement, IfcSchema::IfcElement>(
|
||||
opening, &IfcSchema::IfcOpeningElement::HasFillings, &IfcSchema::IfcRelFillsElement::RelatedBuildingElement);
|
||||
|
||||
for (IfcSchema::IfcElement::list::it it = fills->begin(); it != fills->end(); ++it) {
|
||||
descend(*it, child, product);
|
||||
}
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcSpatialStructureElement::Class())) {
|
||||
IfcSchema::IfcSpatialStructureElement* structure = (IfcSchema::IfcSpatialStructureElement*) product;
|
||||
|
||||
IfcSchema::IfcObjectDefinition::list::ptr elements = get_related
|
||||
<IfcSchema::IfcSpatialStructureElement, IfcSchema::IfcRelContainedInSpatialStructure, IfcSchema::IfcObjectDefinition>
|
||||
(structure, &IfcSchema::IfcSpatialStructureElement::ContainsElements, &IfcSchema::IfcRelContainedInSpatialStructure::RelatedElements);
|
||||
|
||||
for (IfcSchema::IfcObjectDefinition::list::it it = elements->begin(); it != elements->end(); ++it) {
|
||||
descend(*it, child, product);
|
||||
}
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcElement::Class())) {
|
||||
IfcSchema::IfcElement* element = static_cast<IfcSchema::IfcElement*>(product);
|
||||
IfcSchema::IfcOpeningElement::list::ptr openings = get_related<IfcSchema::IfcElement, IfcSchema::IfcRelVoidsElement, IfcSchema::IfcOpeningElement>(
|
||||
element, &IfcSchema::IfcElement::HasOpenings, &IfcSchema::IfcRelVoidsElement::RelatedOpeningElement);
|
||||
|
||||
for (IfcSchema::IfcOpeningElement::list::it it = openings->begin(); it != openings->end(); ++it) {
|
||||
descend(*it, child, product);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SCHEMA_IfcRelDecomposes_HAS_RelatedObjects
|
||||
IfcSchema::IfcObjectDefinition::list::ptr structures = get_related
|
||||
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelDecomposes, IfcSchema::IfcObjectDefinition>
|
||||
(product, &IfcSchema::IfcObjectDefinition::IsDecomposedBy, &IfcSchema::IfcRelDecomposes::RelatedObjects);
|
||||
#else
|
||||
IfcSchema::IfcObjectDefinition::list::ptr structures = get_related
|
||||
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelAggregates, IfcSchema::IfcObjectDefinition>
|
||||
(product, &IfcSchema::IfcObjectDefinition::IsDecomposedBy, &IfcSchema::IfcRelAggregates::RelatedObjects);
|
||||
|
||||
structures->push(get_related
|
||||
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelNests, IfcSchema::IfcObjectDefinition>
|
||||
(product, &IfcSchema::IfcObjectDefinition::IsNestedBy, &IfcSchema::IfcRelNests::RelatedObjects));
|
||||
#endif
|
||||
|
||||
for (IfcSchema::IfcObjectDefinition::list::it it = structures->begin(); it != structures->end(); ++it) {
|
||||
IfcSchema::IfcObjectDefinition* ob = *it;
|
||||
descend(ob, child, product);
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcObject::Class())) {
|
||||
IfcSchema::IfcObject* object = product->as<IfcSchema::IfcObject>();
|
||||
|
||||
IfcSchema::IfcPropertySetDefinition::list::ptr property_sets = get_related
|
||||
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByProperties, IfcSchema::IfcPropertySetDefinition>
|
||||
(object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByProperties::RelatingPropertyDefinition);
|
||||
|
||||
for (IfcSchema::IfcPropertySetDefinition::list::it it = property_sets->begin(); it != property_sets->end(); ++it) {
|
||||
IfcSchema::IfcPropertySetDefinition* pset = *it;
|
||||
if (pset->declaration().is(IfcSchema::IfcPropertySet::Class())) {
|
||||
format_entity_instance(pset, child, true);
|
||||
}
|
||||
else if (pset->declaration().is(IfcSchema::IfcElementQuantity::Class())) {
|
||||
format_entity_instance(pset, child, true);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SCHEMA_IfcObject_HAS_IsTypedBy
|
||||
IfcSchema::IfcTypeObject::list::ptr types = get_related
|
||||
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByType, IfcSchema::IfcTypeObject>
|
||||
(object, &IfcSchema::IfcObject::IsTypedBy, &IfcSchema::IfcRelDefinesByType::RelatingType);
|
||||
#else
|
||||
IfcSchema::IfcTypeObject::list::ptr types = get_related
|
||||
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByType, IfcSchema::IfcTypeObject>
|
||||
(object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByType::RelatingType);
|
||||
#endif
|
||||
|
||||
for (IfcSchema::IfcTypeObject::list::it it = types->begin(); it != types->end(); ++it) {
|
||||
IfcSchema::IfcTypeObject* type = *it;
|
||||
format_entity_instance(type, child, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcProduct::Class())) {
|
||||
std::map<std::string, IfcUtil::IfcBaseEntity*> layers = IfcGeom::Kernel::get_layers(product);
|
||||
for (std::map<std::string, IfcUtil::IfcBaseEntity*>::const_iterator it = layers.begin(); it != layers.end(); ++it) {
|
||||
// IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier) so use name as the ID.
|
||||
// Note that the IfcPresentationLayerAssignment passed here doesn't really matter as as_link is true
|
||||
// for the format_entity_instance() call.
|
||||
ptree node;
|
||||
node.put("<xmlattr>.xlink:href", "#" + it->first);
|
||||
format_entity_instance(it->second, node, child, true);
|
||||
}
|
||||
|
||||
IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations();
|
||||
for (IfcSchema::IfcRelAssociates::list::it it = associations->begin(); it != associations->end(); ++it) {
|
||||
if ((*it)->as<IfcSchema::IfcRelAssociatesMaterial>()) {
|
||||
IfcSchema::IfcMaterialSelect* mat = (*it)->as<IfcSchema::IfcRelAssociatesMaterial>()->RelatingMaterial();
|
||||
ptree node;
|
||||
node.put("<xmlattr>.xlink:href", "#" + qualify_unrooted_instance(mat));
|
||||
format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, child, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &child;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Format IfcProperty instances and insert into the DOM. IfcComplexProperties are flattened out.
|
||||
void format_properties(IfcSchema::IfcProperty::list::ptr properties, ptree& node) {
|
||||
for (IfcSchema::IfcProperty::list::it it = properties->begin(); it != properties->end(); ++it) {
|
||||
IfcSchema::IfcProperty* p = *it;
|
||||
if (p->declaration().is(IfcSchema::IfcComplexProperty::Class())) {
|
||||
IfcSchema::IfcComplexProperty* complex = (IfcSchema::IfcComplexProperty*) p;
|
||||
format_properties(complex->HasProperties(), node);
|
||||
}
|
||||
else {
|
||||
format_entity_instance(p, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format IfcElementQuantity instances and insert into the DOM.
|
||||
void format_quantities(IfcSchema::IfcPhysicalQuantity::list::ptr quantities, ptree& node) {
|
||||
for (IfcSchema::IfcPhysicalQuantity::list::it it = quantities->begin(); it != quantities->end(); ++it) {
|
||||
IfcSchema::IfcPhysicalQuantity* p = *it;
|
||||
ptree* node2 = format_entity_instance(p, node);
|
||||
if (node2 && p->declaration().is(IfcSchema::IfcPhysicalComplexQuantity::Class())) {
|
||||
IfcSchema::IfcPhysicalComplexQuantity* complex = (IfcSchema::IfcPhysicalComplexQuantity*)p;
|
||||
format_quantities(complex->HasQuantities(), *node2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // ~unnamed namespace
|
||||
|
||||
void MAKE_TYPE_NAME(XmlSerializer)::finalize() {
|
||||
MAKE_TYPE_NAME(argument_name_map).insert(std::make_pair("GlobalId", "id"));
|
||||
|
||||
IfcSchema::IfcProject::list::ptr projects = file->instances_by_type<IfcSchema::IfcProject>();
|
||||
if (projects->size() != 1) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Expected a single IfcProject");
|
||||
return;
|
||||
}
|
||||
IfcSchema::IfcProject* project = *projects->begin();
|
||||
|
||||
ptree root, header, units, decomposition, properties, quantities, types, layers, materials, groups;
|
||||
|
||||
// Write the SPF header as XML nodes.
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_description().description()) {
|
||||
header.add_child("file_description.description", ptree(s));
|
||||
}
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_name().author()) {
|
||||
header.add_child("file_name.author", ptree(s));
|
||||
}
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_name().organization()) {
|
||||
header.add_child("file_name.organization", ptree(s));
|
||||
}
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_schema().schema_identifiers()) {
|
||||
header.add_child("file_schema.schema_identifiers", ptree(s));
|
||||
}
|
||||
try {
|
||||
header.put("file_description.implementation_level", file->header().file_description().implementation_level());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_description implementation_level, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.name", file->header().file_name().name());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name name, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.time_stamp", file->header().file_name().time_stamp());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name time_stamp, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.preprocessor_version", file->header().file_name().preprocessor_version());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name preprocessor_version, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.originating_system", file->header().file_name().originating_system());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name originating_system, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.authorization", file->header().file_name().authorization());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name authorization, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
|
||||
// Descend into the decomposition structure of the IFC file.
|
||||
descend(project, decomposition);
|
||||
|
||||
// Write all property sets and values as XML nodes.
|
||||
IfcSchema::IfcPropertySet::list::ptr psets = file->instances_by_type<IfcSchema::IfcPropertySet>();
|
||||
for (IfcSchema::IfcPropertySet::list::it it = psets->begin(); it != psets->end(); ++it) {
|
||||
IfcSchema::IfcPropertySet* pset = *it;
|
||||
ptree* node = format_entity_instance(pset, properties);
|
||||
if (node) {
|
||||
format_properties(pset->HasProperties(), *node);
|
||||
}
|
||||
}
|
||||
|
||||
// Write all group sets and values as XML nodes.
|
||||
IfcSchema::IfcGroup::list::ptr gsets = file->instances_by_type<IfcSchema::IfcGroup>();
|
||||
std::set<std::string> notRootGroups;//selfname, fathername
|
||||
std::function<void(IfcSchema::IfcGroup* group, ptree& node)> writeGroupToNode =
|
||||
[&](IfcSchema::IfcGroup* group, ptree& node)->void
|
||||
{
|
||||
if (notRootGroups.find(group->Name()) != notRootGroups.end()) {
|
||||
return;
|
||||
}
|
||||
ptree* node2 = descend(group, node);//write one group to root
|
||||
auto father = group->IsGroupedBy();
|
||||
for (auto iter = father->begin(); iter != father->end(); iter++)
|
||||
{
|
||||
IfcSchema::IfcRelAssigns* ii = *iter;
|
||||
auto objs = ii->RelatedObjects();
|
||||
for (auto objit = objs->begin(); objit != objs->end(); objit++) {
|
||||
auto entity = *objit;
|
||||
if (entity->declaration().is(IfcSchema::IfcGroup::Class())) {
|
||||
writeGroupToNode(entity->as<IfcSchema::IfcGroup>(), *node2);
|
||||
notRootGroups.emplace(entity->Name());
|
||||
}
|
||||
else {
|
||||
descend(entity, *node2);//write son to father group
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
for (IfcSchema::IfcGroup::list::it it = gsets->begin(); it != gsets->end(); ++it) {
|
||||
writeGroupToNode(*it, groups);
|
||||
}
|
||||
for (auto it = groups.begin(); it != groups.end();)//root
|
||||
{
|
||||
if (notRootGroups.find(it->second.get<std::string>("<xmlattr>.Name")) != notRootGroups.end()) {
|
||||
it = groups.erase(it);
|
||||
}
|
||||
else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Write all quantities and values as XML nodes.
|
||||
IfcSchema::IfcElementQuantity::list::ptr qtosets = file->instances_by_type<IfcSchema::IfcElementQuantity>();
|
||||
for (IfcSchema::IfcElementQuantity::list::it it = qtosets->begin(); it != qtosets->end(); ++it) {
|
||||
IfcSchema::IfcElementQuantity* qto = *it;
|
||||
ptree* node = format_entity_instance(qto, quantities);
|
||||
if (node) {
|
||||
format_quantities(qto->Quantities(), *node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Write all type objects as XML nodes.
|
||||
IfcSchema::IfcTypeObject::list::ptr type_objects = file->instances_by_type<IfcSchema::IfcTypeObject>();
|
||||
for (IfcSchema::IfcTypeObject::list::it it = type_objects->begin(); it != type_objects->end(); ++it) {
|
||||
IfcSchema::IfcTypeObject* type_object = *it;
|
||||
ptree* node = descend(type_object, types);
|
||||
|
||||
if (node && type_object->hasHasPropertySets()) {
|
||||
IfcSchema::IfcPropertySetDefinition::list::ptr property_sets = type_object->HasPropertySets();
|
||||
for (IfcSchema::IfcPropertySetDefinition::list::it jt = property_sets->begin(); jt != property_sets->end(); ++jt) {
|
||||
IfcSchema::IfcPropertySetDefinition* pset = *jt;
|
||||
if (pset->declaration().is(IfcSchema::IfcPropertySet::Class())) {
|
||||
format_entity_instance(pset, *node, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write all assigned units as XML nodes.
|
||||
IfcEntityList::ptr unit_assignments = project->UnitsInContext()->Units();
|
||||
for (IfcEntityList::it it = unit_assignments->begin(); it != unit_assignments->end(); ++it) {
|
||||
if ((*it)->declaration().is(IfcSchema::IfcNamedUnit::Class())) {
|
||||
IfcSchema::IfcNamedUnit* named_unit = (*it)->as<IfcSchema::IfcNamedUnit>();
|
||||
ptree* node = format_entity_instance(named_unit, units);
|
||||
if (node) {
|
||||
node->put("<xmlattr>.SI_equivalent", IfcParse::get_SI_equivalent<IfcSchema>(named_unit));
|
||||
}
|
||||
}
|
||||
else if ((*it)->declaration().is(IfcSchema::IfcMonetaryUnit::Class())) {
|
||||
format_entity_instance((*it)->as<IfcSchema::IfcMonetaryUnit>(), units);
|
||||
}
|
||||
}
|
||||
|
||||
// Layer assignments. IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier)
|
||||
// so use names as the IDs and only insert those with unique names. In case of possible duplicate names/IDs
|
||||
// the first IfcPresentationLayerAssignment occurrence takes precedence.
|
||||
std::set<std::string> layer_names;
|
||||
IfcSchema::IfcPresentationLayerAssignment::list::ptr layer_assignments = file->instances_by_type<IfcSchema::IfcPresentationLayerAssignment>();
|
||||
for (IfcSchema::IfcPresentationLayerAssignment::list::it it = layer_assignments->begin(); it != layer_assignments->end(); ++it) {
|
||||
const std::string& name = (*it)->Name();
|
||||
if (layer_names.find(name) == layer_names.end()) {
|
||||
layer_names.insert(name);
|
||||
ptree node;
|
||||
node.put("<xmlattr>.id", name);
|
||||
format_entity_instance(*it, node, layers);
|
||||
}
|
||||
}
|
||||
|
||||
IfcSchema::IfcRelAssociatesMaterial::list::ptr materal_associations = file->instances_by_type<IfcSchema::IfcRelAssociatesMaterial>();
|
||||
std::set<IfcSchema::IfcMaterialSelect*> emitted_materials;
|
||||
for (IfcSchema::IfcRelAssociatesMaterial::list::it it = materal_associations->begin(); it != materal_associations->end(); ++it) {
|
||||
IfcSchema::IfcMaterialSelect* mat = (**it).RelatingMaterial();
|
||||
if (emitted_materials.find(mat) == emitted_materials.end()) {
|
||||
emitted_materials.insert(mat);
|
||||
ptree node;
|
||||
node.put("<xmlattr>.id", qualify_unrooted_instance(mat));
|
||||
if (mat->as<IfcSchema::IfcMaterialLayerSetUsage>() || mat->as<IfcSchema::IfcMaterialLayerSet>()) {
|
||||
IfcSchema::IfcMaterialLayerSet* layerset = mat->as<IfcSchema::IfcMaterialLayerSet>();
|
||||
if (!layerset) {
|
||||
layerset = mat->as<IfcSchema::IfcMaterialLayerSetUsage>()->ForLayerSet();
|
||||
}
|
||||
if (layerset->hasLayerSetName()) {
|
||||
node.put("<xmlattr>.LayerSetName", layerset->LayerSetName());
|
||||
}
|
||||
IfcSchema::IfcMaterialLayer::list::ptr ls = layerset->MaterialLayers();
|
||||
for (IfcSchema::IfcMaterialLayer::list::it jt = ls->begin(); jt != ls->end(); ++jt) {
|
||||
ptree subnode;
|
||||
if ((*jt)->hasMaterial()) {
|
||||
subnode.put("<xmlattr>.Name", (*jt)->Material()->Name());
|
||||
}
|
||||
format_entity_instance(*jt, subnode, node);
|
||||
}
|
||||
}
|
||||
else if (mat->as<IfcSchema::IfcMaterialList>()) {
|
||||
IfcSchema::IfcMaterial::list::ptr mats = mat->as<IfcSchema::IfcMaterialList>()->Materials();
|
||||
for (IfcSchema::IfcMaterial::list::it jt = mats->begin(); jt != mats->end(); ++jt) {
|
||||
ptree subnode;
|
||||
format_entity_instance(*jt, subnode, node);
|
||||
}
|
||||
}
|
||||
format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, materials);
|
||||
}
|
||||
}
|
||||
|
||||
root.add_child("ifc.header", header);
|
||||
root.add_child("ifc.units", units);
|
||||
root.add_child("ifc.properties", properties);
|
||||
root.add_child("ifc.quantities", quantities);
|
||||
root.add_child("ifc.types", types);
|
||||
root.add_child("ifc.layers", layers);
|
||||
root.add_child("ifc.groups", groups);
|
||||
root.add_child("ifc.materials", materials);
|
||||
root.add_child("ifc.decomposition", decomposition);
|
||||
|
||||
root.put("ifc.<xmlattr>.xmlns:xlink", "http://www.w3.org/1999/xlink");
|
||||
|
||||
#if BOOST_VERSION >= 105600
|
||||
boost::property_tree::xml_writer_settings<ptree::key_type> settings = boost::property_tree::xml_writer_make_settings<ptree::key_type>('\t', 1);
|
||||
#else
|
||||
boost::property_tree::xml_writer_settings<char> settings('\t', 1);
|
||||
#endif
|
||||
|
||||
std::ofstream f(IfcUtil::path::from_utf8(xml_filename).c_str());
|
||||
boost::property_tree::write_xml(f, root, settings);
|
||||
}
|
||||
@@ -0,0 +1,647 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the Lesser GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3.0 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 *
|
||||
* Lesser GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/xml_parser.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include "XmlSerializer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "../../ifcparse/IfcSIPrefix.h"
|
||||
#include "../../ifcgeom/IfcGeom.h"
|
||||
#include "../../ifcparse/utils.h"
|
||||
|
||||
using boost::property_tree::ptree;
|
||||
|
||||
#include "XmlSerializer.h"
|
||||
|
||||
namespace {
|
||||
struct MAKE_TYPE_NAME(factory_t) {
|
||||
XmlSerializer* operator()(IfcParse::IfcFile* file, const std::string& xml_filename) const {
|
||||
MAKE_TYPE_NAME(XmlSerializer)* s = new MAKE_TYPE_NAME(XmlSerializer)(file, xml_filename);
|
||||
s->setFile(file);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void MAKE_INIT_FN(XmlSerializer)(XmlSerializerFactory::Factory* mapping) {
|
||||
static const std::string schema_name = STRINGIFY(IfcSchema);
|
||||
MAKE_TYPE_NAME(factory_t) factory;
|
||||
mapping->bind(schema_name, factory);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO: Make this a member of XmlSerializer?
|
||||
std::map<std::string, std::string> MAKE_TYPE_NAME(argument_name_map);
|
||||
|
||||
// Format an IFC attribute and maybe returns as string. Only literal scalar
|
||||
// values are converted. Things like entity instances and lists are omitted.
|
||||
boost::optional<std::string> format_attribute(const Argument* argument, IfcUtil::ArgumentType argument_type, const std::string& argument_name) {
|
||||
boost::optional<std::string> value;
|
||||
|
||||
// Hard-code lat-lon as it represents an array
|
||||
// of integers best emitted as a single decimal
|
||||
if (argument_name == "IfcSite.RefLatitude" ||
|
||||
argument_name == "IfcSite.RefLongitude")
|
||||
{
|
||||
std::vector<int> angle = *argument;
|
||||
double deg;
|
||||
if (angle.size() >= 3) {
|
||||
deg = angle[0] + angle[1] / 60. + angle[2] / 3600.;
|
||||
int prec = 8;
|
||||
if (angle.size() == 4) {
|
||||
deg += angle[3] / (1000000. * 3600.);
|
||||
prec = 14;
|
||||
}
|
||||
std::stringstream stream;
|
||||
stream << std::setprecision(prec) << deg;
|
||||
value = stream.str();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
switch (argument_type) {
|
||||
case IfcUtil::Argument_BOOL: {
|
||||
const bool b = *argument;
|
||||
value = b ? "true" : "false";
|
||||
break; }
|
||||
case IfcUtil::Argument_DOUBLE: {
|
||||
const double d = *argument;
|
||||
std::stringstream stream;
|
||||
stream << d;
|
||||
value = stream.str();
|
||||
break; }
|
||||
case IfcUtil::Argument_STRING:
|
||||
case IfcUtil::Argument_ENUMERATION: {
|
||||
value = static_cast<std::string>(*argument);
|
||||
break; }
|
||||
case IfcUtil::Argument_INT: {
|
||||
const int v = *argument;
|
||||
std::stringstream stream;
|
||||
stream << v;
|
||||
value = stream.str();
|
||||
break; }
|
||||
case IfcUtil::Argument_ENTITY_INSTANCE: {
|
||||
IfcUtil::IfcBaseClass* e = *argument;
|
||||
if (!e->declaration().as_entity()) {
|
||||
IfcUtil::IfcBaseType* f = (IfcUtil::IfcBaseType*) e;
|
||||
value = format_attribute(f->data().getArgument(0), f->data().getArgument(0)->type(), argument_name);
|
||||
}
|
||||
else if (e->declaration().is(IfcSchema::IfcSIUnit::Class()) || e->declaration().is(IfcSchema::IfcConversionBasedUnit::Class())) {
|
||||
// Some string concatenation to have a unit name as a XML attribute.
|
||||
|
||||
std::string unit_name;
|
||||
|
||||
if (e->declaration().is(IfcSchema::IfcSIUnit::Class())) {
|
||||
IfcSchema::IfcSIUnit* unit = (IfcSchema::IfcSIUnit*) e;
|
||||
unit_name = IfcSchema::IfcSIUnitName::ToString(unit->Name());
|
||||
if (unit->hasPrefix()) {
|
||||
unit_name = IfcSchema::IfcSIPrefix::ToString(unit->Prefix()) + unit_name;
|
||||
}
|
||||
}
|
||||
else {
|
||||
IfcSchema::IfcConversionBasedUnit* unit = (IfcSchema::IfcConversionBasedUnit*) e;
|
||||
unit_name = unit->Name();
|
||||
}
|
||||
|
||||
value = unit_name;
|
||||
}
|
||||
else if (e->declaration().is(IfcSchema::IfcLocalPlacement::Class())) {
|
||||
IfcSchema::IfcLocalPlacement* placement = e->as<IfcSchema::IfcLocalPlacement>();
|
||||
gp_Trsf trsf;
|
||||
IfcGeom::MAKE_TYPE_NAME(Kernel) kernel;
|
||||
|
||||
if (kernel.convert(placement, trsf)) {
|
||||
std::stringstream stream;
|
||||
for (int i = 1; i < 5; ++i) {
|
||||
for (int j = 1; j < 4; ++j) {
|
||||
const double trsf_value = trsf.Value(j, i);
|
||||
stream << trsf_value << " ";
|
||||
}
|
||||
stream << ((i == 4) ? "1" : "0 ");
|
||||
}
|
||||
value = stream.str();
|
||||
}
|
||||
}
|
||||
break; }
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Appends to a node with possibly existing attributes
|
||||
ptree* format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& child, ptree& tree, bool as_link = false) {
|
||||
const unsigned n = instance->declaration().attribute_count();
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
try {
|
||||
instance->data().getArgument(i);
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
Logger::Error("Expected " + boost::lexical_cast<std::string>(n) + " attributes for:", instance);
|
||||
break;
|
||||
}
|
||||
const Argument* argument = instance->data().getArgument(i);
|
||||
if (argument->isNull()) continue;
|
||||
|
||||
std::string argument_name = instance->declaration().attribute_by_index(i)->name();
|
||||
std::map<std::string, std::string>::const_iterator argument_name_it;
|
||||
argument_name_it = MAKE_TYPE_NAME(argument_name_map).find(argument_name);
|
||||
if (argument_name_it != MAKE_TYPE_NAME(argument_name_map).end()) {
|
||||
argument_name = argument_name_it->second;
|
||||
}
|
||||
const IfcUtil::ArgumentType argument_type = instance->data().getArgument(i)->type();
|
||||
|
||||
const std::string qualified_name = instance->declaration().name() + "." + argument_name;
|
||||
boost::optional<std::string> value;
|
||||
try {
|
||||
value = format_attribute(argument, argument_type, qualified_name);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
}
|
||||
catch (const Standard_ConstructionError& e) {
|
||||
Logger::Error(e.GetMessageString(), instance);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (as_link) {
|
||||
if (argument_name == "id") {
|
||||
child.put("<xmlattr>.xlink:href", std::string("#") + *value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::stringstream stream;
|
||||
stream << "<xmlattr>." << argument_name;
|
||||
child.put(stream.str(), *value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return &tree.add_child(instance->declaration().name(), child);
|
||||
}
|
||||
|
||||
// Formats an entity instances as a ptree node, and insert into the DOM. Recurses
|
||||
// over the entity attributes and writes them as xml attributes of the node.
|
||||
ptree* format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& tree, bool as_link = false) {
|
||||
ptree child;
|
||||
return format_entity_instance(instance, child, tree, as_link);
|
||||
}
|
||||
|
||||
std::string qualify_unrooted_instance(IfcUtil::IfcBaseClass* inst) {
|
||||
return inst->declaration().name() + "_" + boost::lexical_cast<std::string>(inst->data().id());
|
||||
}
|
||||
|
||||
// A function to be called recursively. Template specialization is used
|
||||
// to descend into decomposition, containment and property relationships.
|
||||
template <typename A>
|
||||
ptree* descend(A* instance, ptree& tree, IfcUtil::IfcBaseClass* parent = nullptr) {
|
||||
if (instance->declaration().is(IfcSchema::IfcObjectDefinition::Class())) {
|
||||
return descend(instance->template as<IfcSchema::IfcObjectDefinition>(), tree, parent);
|
||||
}
|
||||
else {
|
||||
return format_entity_instance(instance, tree);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns related entity instances using IFC's objectified relationship
|
||||
// model. The second and third argument require a member function pointer.
|
||||
template <typename T, typename U, typename V, typename F, typename G>
|
||||
typename V::list::ptr get_related(T* t, F f, G g) {
|
||||
typename U::list::ptr li = (*t.*f)()->template as<U>();
|
||||
typename V::list::ptr acc(new typename V::list);
|
||||
for (typename U::list::it it = li->begin(); it != li->end(); ++it) {
|
||||
U* u = *it;
|
||||
acc->push((*u.*g)()->template as<V>());
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
// Descends into the tree by recursing into IfcRelContainedInSpatialStructure,
|
||||
// IfcRelDecomposes, IfcRelDefinesByType, IfcRelDefinesByProperties relations.
|
||||
template <>
|
||||
ptree* descend(IfcSchema::IfcObjectDefinition* product, ptree& tree, IfcUtil::IfcBaseClass* parent) {
|
||||
if (product->declaration().is(IfcSchema::IfcElement::Class())) {
|
||||
auto voids = product->as<IfcSchema::IfcElement>()->FillsVoids();
|
||||
if (voids && voids->size() == 1 && (*voids->begin())->RelatingOpeningElement() != parent) {
|
||||
// Fills are placed under their corresponding opening, return early to avoid duplication.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ptree& child = *format_entity_instance(product, tree);
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcOpeningElement::Class())) {
|
||||
IfcSchema::IfcOpeningElement* opening = static_cast<IfcSchema::IfcOpeningElement*>(product);
|
||||
IfcSchema::IfcElement::list::ptr fills = get_related<IfcSchema::IfcOpeningElement, IfcSchema::IfcRelFillsElement, IfcSchema::IfcElement>(
|
||||
opening, &IfcSchema::IfcOpeningElement::HasFillings, &IfcSchema::IfcRelFillsElement::RelatedBuildingElement);
|
||||
|
||||
for (IfcSchema::IfcElement::list::it it = fills->begin(); it != fills->end(); ++it) {
|
||||
descend(*it, child, product);
|
||||
}
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcSpatialStructureElement::Class())) {
|
||||
IfcSchema::IfcSpatialStructureElement* structure = (IfcSchema::IfcSpatialStructureElement*) product;
|
||||
|
||||
IfcSchema::IfcObjectDefinition::list::ptr elements = get_related
|
||||
<IfcSchema::IfcSpatialStructureElement, IfcSchema::IfcRelContainedInSpatialStructure, IfcSchema::IfcObjectDefinition>
|
||||
(structure, &IfcSchema::IfcSpatialStructureElement::ContainsElements, &IfcSchema::IfcRelContainedInSpatialStructure::RelatedElements);
|
||||
|
||||
for (IfcSchema::IfcObjectDefinition::list::it it = elements->begin(); it != elements->end(); ++it) {
|
||||
descend(*it, child, product);
|
||||
}
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcElement::Class())) {
|
||||
IfcSchema::IfcElement* element = static_cast<IfcSchema::IfcElement*>(product);
|
||||
IfcSchema::IfcOpeningElement::list::ptr openings = get_related<IfcSchema::IfcElement, IfcSchema::IfcRelVoidsElement, IfcSchema::IfcOpeningElement>(
|
||||
element, &IfcSchema::IfcElement::HasOpenings, &IfcSchema::IfcRelVoidsElement::RelatedOpeningElement);
|
||||
|
||||
for (IfcSchema::IfcOpeningElement::list::it it = openings->begin(); it != openings->end(); ++it) {
|
||||
descend(*it, child, product);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SCHEMA_IfcRelDecomposes_HAS_RelatedObjects
|
||||
IfcSchema::IfcObjectDefinition::list::ptr structures = get_related
|
||||
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelDecomposes, IfcSchema::IfcObjectDefinition>
|
||||
(product, &IfcSchema::IfcObjectDefinition::IsDecomposedBy, &IfcSchema::IfcRelDecomposes::RelatedObjects);
|
||||
#else
|
||||
IfcSchema::IfcObjectDefinition::list::ptr structures = get_related
|
||||
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelAggregates, IfcSchema::IfcObjectDefinition>
|
||||
(product, &IfcSchema::IfcObjectDefinition::IsDecomposedBy, &IfcSchema::IfcRelAggregates::RelatedObjects);
|
||||
|
||||
structures->push(get_related
|
||||
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelNests, IfcSchema::IfcObjectDefinition>
|
||||
(product, &IfcSchema::IfcObjectDefinition::IsNestedBy, &IfcSchema::IfcRelNests::RelatedObjects));
|
||||
#endif
|
||||
|
||||
for (IfcSchema::IfcObjectDefinition::list::it it = structures->begin(); it != structures->end(); ++it) {
|
||||
IfcSchema::IfcObjectDefinition* ob = *it;
|
||||
descend(ob, child, product);
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcObject::Class())) {
|
||||
IfcSchema::IfcObject* object = product->as<IfcSchema::IfcObject>();
|
||||
|
||||
IfcSchema::IfcPropertySetDefinition::list::ptr property_sets = get_related
|
||||
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByProperties, IfcSchema::IfcPropertySetDefinition>
|
||||
(object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByProperties::RelatingPropertyDefinition);
|
||||
|
||||
for (IfcSchema::IfcPropertySetDefinition::list::it it = property_sets->begin(); it != property_sets->end(); ++it) {
|
||||
IfcSchema::IfcPropertySetDefinition* pset = *it;
|
||||
if (pset->declaration().is(IfcSchema::IfcPropertySet::Class())) {
|
||||
format_entity_instance(pset, child, true);
|
||||
}
|
||||
else if (pset->declaration().is(IfcSchema::IfcElementQuantity::Class())) {
|
||||
format_entity_instance(pset, child, true);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SCHEMA_IfcObject_HAS_IsTypedBy
|
||||
IfcSchema::IfcTypeObject::list::ptr types = get_related
|
||||
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByType, IfcSchema::IfcTypeObject>
|
||||
(object, &IfcSchema::IfcObject::IsTypedBy, &IfcSchema::IfcRelDefinesByType::RelatingType);
|
||||
#else
|
||||
IfcSchema::IfcTypeObject::list::ptr types = get_related
|
||||
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByType, IfcSchema::IfcTypeObject>
|
||||
(object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByType::RelatingType);
|
||||
#endif
|
||||
|
||||
for (IfcSchema::IfcTypeObject::list::it it = types->begin(); it != types->end(); ++it) {
|
||||
IfcSchema::IfcTypeObject* type = *it;
|
||||
format_entity_instance(type, child, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (product->declaration().is(IfcSchema::IfcProduct::Class())) {
|
||||
std::map<std::string, IfcUtil::IfcBaseEntity*> layers = IfcGeom::Kernel::get_layers(product);
|
||||
for (std::map<std::string, IfcUtil::IfcBaseEntity*>::const_iterator it = layers.begin(); it != layers.end(); ++it) {
|
||||
// IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier) so use name as the ID.
|
||||
// Note that the IfcPresentationLayerAssignment passed here doesn't really matter as as_link is true
|
||||
// for the format_entity_instance() call.
|
||||
ptree node;
|
||||
node.put("<xmlattr>.xlink:href", "#" + it->first);
|
||||
format_entity_instance(it->second, node, child, true);
|
||||
}
|
||||
|
||||
IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations();
|
||||
for (IfcSchema::IfcRelAssociates::list::it it = associations->begin(); it != associations->end(); ++it) {
|
||||
if ((*it)->as<IfcSchema::IfcRelAssociatesMaterial>()) {
|
||||
IfcSchema::IfcMaterialSelect* mat = (*it)->as<IfcSchema::IfcRelAssociatesMaterial>()->RelatingMaterial();
|
||||
ptree node;
|
||||
node.put("<xmlattr>.xlink:href", "#" + qualify_unrooted_instance(mat));
|
||||
format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, child, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &child;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Format IfcProperty instances and insert into the DOM. IfcComplexProperties are flattened out.
|
||||
void format_properties(IfcSchema::IfcProperty::list::ptr properties, ptree& node) {
|
||||
for (IfcSchema::IfcProperty::list::it it = properties->begin(); it != properties->end(); ++it) {
|
||||
IfcSchema::IfcProperty* p = *it;
|
||||
if (p->declaration().is(IfcSchema::IfcComplexProperty::Class())) {
|
||||
IfcSchema::IfcComplexProperty* complex = (IfcSchema::IfcComplexProperty*) p;
|
||||
format_properties(complex->HasProperties(), node);
|
||||
}
|
||||
else {
|
||||
format_entity_instance(p, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format IfcElementQuantity instances and insert into the DOM.
|
||||
void format_quantities(IfcSchema::IfcPhysicalQuantity::list::ptr quantities, ptree& node) {
|
||||
for (IfcSchema::IfcPhysicalQuantity::list::it it = quantities->begin(); it != quantities->end(); ++it) {
|
||||
IfcSchema::IfcPhysicalQuantity* p = *it;
|
||||
ptree* node2 = format_entity_instance(p, node);
|
||||
if (node2 && p->declaration().is(IfcSchema::IfcPhysicalComplexQuantity::Class())) {
|
||||
IfcSchema::IfcPhysicalComplexQuantity* complex = (IfcSchema::IfcPhysicalComplexQuantity*)p;
|
||||
format_quantities(complex->HasQuantities(), *node2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // ~unnamed namespace
|
||||
|
||||
void MAKE_TYPE_NAME(XmlSerializer)::finalize() {
|
||||
MAKE_TYPE_NAME(argument_name_map).insert(std::make_pair("GlobalId", "id"));
|
||||
|
||||
IfcSchema::IfcProject::list::ptr projects = file->instances_by_type<IfcSchema::IfcProject>();
|
||||
if (projects->size() != 1) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Expected a single IfcProject");
|
||||
return;
|
||||
}
|
||||
IfcSchema::IfcProject* project = *projects->begin();
|
||||
|
||||
ptree root, header, units, decomposition, properties, quantities, types, layers, materials, groups;
|
||||
|
||||
// Write the SPF header as XML nodes.
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_description().description()) {
|
||||
header.add_child("file_description.description", ptree(s));
|
||||
}
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_name().author()) {
|
||||
header.add_child("file_name.author", ptree(s));
|
||||
}
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_name().organization()) {
|
||||
header.add_child("file_name.organization", ptree(s));
|
||||
}
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_schema().schema_identifiers()) {
|
||||
header.add_child("file_schema.schema_identifiers", ptree(s));
|
||||
}
|
||||
try {
|
||||
header.put("file_description.implementation_level", file->header().file_description().implementation_level());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_description implementation_level, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.name", file->header().file_name().name());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name name, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.time_stamp", file->header().file_name().time_stamp());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name time_stamp, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.preprocessor_version", file->header().file_name().preprocessor_version());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name preprocessor_version, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.originating_system", file->header().file_name().originating_system());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name originating_system, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.authorization", file->header().file_name().authorization());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name authorization, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
|
||||
// Descend into the decomposition structure of the IFC file.
|
||||
descend(project, decomposition);
|
||||
|
||||
// Write all property sets and values as XML nodes.
|
||||
IfcSchema::IfcPropertySet::list::ptr psets = file->instances_by_type<IfcSchema::IfcPropertySet>();
|
||||
for (IfcSchema::IfcPropertySet::list::it it = psets->begin(); it != psets->end(); ++it) {
|
||||
IfcSchema::IfcPropertySet* pset = *it;
|
||||
ptree* node = format_entity_instance(pset, properties);
|
||||
if (node) {
|
||||
format_properties(pset->HasProperties(), *node);
|
||||
}
|
||||
}
|
||||
|
||||
// Write all group sets and values as XML nodes.
|
||||
IfcSchema::IfcGroup::list::ptr gsets = file->instances_by_type<IfcSchema::IfcGroup>();
|
||||
std::set<unsigned> notRootGroups;//selfname, fathername
|
||||
std::map<unsigned, int> rootGroups;
|
||||
std::function<void(IfcSchema::IfcGroup* group, ptree& node)> writeGroupToNode =
|
||||
[&](IfcSchema::IfcGroup* group, ptree& node)->void
|
||||
{
|
||||
ptree* node2 = descend(group, node);//write one group to root
|
||||
auto father = group->IsGroupedBy();
|
||||
for (auto iter = father->begin(); iter != father->end(); iter++)
|
||||
{
|
||||
IfcSchema::IfcRelAssigns* ii = *iter;
|
||||
auto objs = ii->RelatedObjects();
|
||||
for (auto objit = objs->begin(); objit != objs->end(); objit++) {
|
||||
auto entity = *objit;
|
||||
if (entity->declaration().is(IfcSchema::IfcGroup::Class())) {
|
||||
writeGroupToNode(entity->as<IfcSchema::IfcGroup>(), *node2);
|
||||
notRootGroups.emplace(entity->data().id());
|
||||
}
|
||||
else {
|
||||
descend(entity, *node2);//write son to father group
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
int idx = 0;
|
||||
for (IfcSchema::IfcGroup::list::it it = gsets->begin(); it != gsets->end(); ++it) {
|
||||
if (notRootGroups.find((*it)->data().id()) != notRootGroups.end()) {
|
||||
continue;
|
||||
}
|
||||
writeGroupToNode(*it, groups);
|
||||
rootGroups.emplace((*it)->data().id(), idx++);//map record the index of each group id in xml
|
||||
}
|
||||
std::set<int> idxNeedToDelete;
|
||||
for (auto it = notRootGroups.begin(); it != notRootGroups.end(); it++) {//delete double writes in root
|
||||
auto mapIt = rootGroups.find(*it);
|
||||
if (mapIt != rootGroups.end())//need to delete somebody
|
||||
{
|
||||
idxNeedToDelete.emplace(mapIt->second);
|
||||
}
|
||||
}
|
||||
idx = 0;
|
||||
for (auto it = groups.begin(); it != groups.end(); idx++)//root
|
||||
{
|
||||
if (idxNeedToDelete.find(idx) != idxNeedToDelete.end()){
|
||||
it = groups.erase(it);
|
||||
}
|
||||
else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Write all quantities and values as XML nodes.
|
||||
IfcSchema::IfcElementQuantity::list::ptr qtosets = file->instances_by_type<IfcSchema::IfcElementQuantity>();
|
||||
for (IfcSchema::IfcElementQuantity::list::it it = qtosets->begin(); it != qtosets->end(); ++it) {
|
||||
IfcSchema::IfcElementQuantity* qto = *it;
|
||||
ptree* node = format_entity_instance(qto, quantities);
|
||||
if (node) {
|
||||
format_quantities(qto->Quantities(), *node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Write all type objects as XML nodes.
|
||||
IfcSchema::IfcTypeObject::list::ptr type_objects = file->instances_by_type<IfcSchema::IfcTypeObject>();
|
||||
for (IfcSchema::IfcTypeObject::list::it it = type_objects->begin(); it != type_objects->end(); ++it) {
|
||||
IfcSchema::IfcTypeObject* type_object = *it;
|
||||
ptree* node = descend(type_object, types);
|
||||
|
||||
if (node && type_object->hasHasPropertySets()) {
|
||||
IfcSchema::IfcPropertySetDefinition::list::ptr property_sets = type_object->HasPropertySets();
|
||||
for (IfcSchema::IfcPropertySetDefinition::list::it jt = property_sets->begin(); jt != property_sets->end(); ++jt) {
|
||||
IfcSchema::IfcPropertySetDefinition* pset = *jt;
|
||||
if (pset->declaration().is(IfcSchema::IfcPropertySet::Class())) {
|
||||
format_entity_instance(pset, *node, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write all assigned units as XML nodes.
|
||||
IfcEntityList::ptr unit_assignments = project->UnitsInContext()->Units();
|
||||
for (IfcEntityList::it it = unit_assignments->begin(); it != unit_assignments->end(); ++it) {
|
||||
if ((*it)->declaration().is(IfcSchema::IfcNamedUnit::Class())) {
|
||||
IfcSchema::IfcNamedUnit* named_unit = (*it)->as<IfcSchema::IfcNamedUnit>();
|
||||
ptree* node = format_entity_instance(named_unit, units);
|
||||
if (node) {
|
||||
node->put("<xmlattr>.SI_equivalent", IfcParse::get_SI_equivalent<IfcSchema>(named_unit));
|
||||
}
|
||||
}
|
||||
else if ((*it)->declaration().is(IfcSchema::IfcMonetaryUnit::Class())) {
|
||||
format_entity_instance((*it)->as<IfcSchema::IfcMonetaryUnit>(), units);
|
||||
}
|
||||
}
|
||||
|
||||
// Layer assignments. IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier)
|
||||
// so use names as the IDs and only insert those with unique names. In case of possible duplicate names/IDs
|
||||
// the first IfcPresentationLayerAssignment occurrence takes precedence.
|
||||
std::set<std::string> layer_names;
|
||||
IfcSchema::IfcPresentationLayerAssignment::list::ptr layer_assignments = file->instances_by_type<IfcSchema::IfcPresentationLayerAssignment>();
|
||||
for (IfcSchema::IfcPresentationLayerAssignment::list::it it = layer_assignments->begin(); it != layer_assignments->end(); ++it) {
|
||||
const std::string& name = (*it)->Name();
|
||||
if (layer_names.find(name) == layer_names.end()) {
|
||||
layer_names.insert(name);
|
||||
ptree node;
|
||||
node.put("<xmlattr>.id", name);
|
||||
format_entity_instance(*it, node, layers);
|
||||
}
|
||||
}
|
||||
|
||||
IfcSchema::IfcRelAssociatesMaterial::list::ptr materal_associations = file->instances_by_type<IfcSchema::IfcRelAssociatesMaterial>();
|
||||
std::set<IfcSchema::IfcMaterialSelect*> emitted_materials;
|
||||
for (IfcSchema::IfcRelAssociatesMaterial::list::it it = materal_associations->begin(); it != materal_associations->end(); ++it) {
|
||||
IfcSchema::IfcMaterialSelect* mat = (**it).RelatingMaterial();
|
||||
if (emitted_materials.find(mat) == emitted_materials.end()) {
|
||||
emitted_materials.insert(mat);
|
||||
ptree node;
|
||||
node.put("<xmlattr>.id", qualify_unrooted_instance(mat));
|
||||
if (mat->as<IfcSchema::IfcMaterialLayerSetUsage>() || mat->as<IfcSchema::IfcMaterialLayerSet>()) {
|
||||
IfcSchema::IfcMaterialLayerSet* layerset = mat->as<IfcSchema::IfcMaterialLayerSet>();
|
||||
if (!layerset) {
|
||||
layerset = mat->as<IfcSchema::IfcMaterialLayerSetUsage>()->ForLayerSet();
|
||||
}
|
||||
if (layerset->hasLayerSetName()) {
|
||||
node.put("<xmlattr>.LayerSetName", layerset->LayerSetName());
|
||||
}
|
||||
IfcSchema::IfcMaterialLayer::list::ptr ls = layerset->MaterialLayers();
|
||||
for (IfcSchema::IfcMaterialLayer::list::it jt = ls->begin(); jt != ls->end(); ++jt) {
|
||||
ptree subnode;
|
||||
if ((*jt)->hasMaterial()) {
|
||||
subnode.put("<xmlattr>.Name", (*jt)->Material()->Name());
|
||||
}
|
||||
format_entity_instance(*jt, subnode, node);
|
||||
}
|
||||
}
|
||||
else if (mat->as<IfcSchema::IfcMaterialList>()) {
|
||||
IfcSchema::IfcMaterial::list::ptr mats = mat->as<IfcSchema::IfcMaterialList>()->Materials();
|
||||
for (IfcSchema::IfcMaterial::list::it jt = mats->begin(); jt != mats->end(); ++jt) {
|
||||
ptree subnode;
|
||||
format_entity_instance(*jt, subnode, node);
|
||||
}
|
||||
}
|
||||
format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, materials);
|
||||
}
|
||||
}
|
||||
|
||||
root.add_child("ifc.header", header);
|
||||
root.add_child("ifc.units", units);
|
||||
root.add_child("ifc.properties", properties);
|
||||
root.add_child("ifc.quantities", quantities);
|
||||
root.add_child("ifc.types", types);
|
||||
root.add_child("ifc.layers", layers);
|
||||
root.add_child("ifc.groups", groups);
|
||||
root.add_child("ifc.materials", materials);
|
||||
root.add_child("ifc.decomposition", decomposition);
|
||||
|
||||
root.put("ifc.<xmlattr>.xmlns:xlink", "http://www.w3.org/1999/xlink");
|
||||
|
||||
#if BOOST_VERSION >= 105600
|
||||
boost::property_tree::xml_writer_settings<ptree::key_type> settings = boost::property_tree::xml_writer_make_settings<ptree::key_type>('\t', 1);
|
||||
#else
|
||||
boost::property_tree::xml_writer_settings<char> settings('\t', 1);
|
||||
#endif
|
||||
|
||||
std::ofstream f(IfcUtil::path::from_utf8(xml_filename).c_str());
|
||||
boost::property_tree::write_xml(f, root, settings);
|
||||
}
|
||||
@@ -143,6 +143,14 @@ IF(WIN32 AND ("$ENV{CONDA_BUILD}" STREQUAL ""))
|
||||
SET(Boost_USE_STATIC_LIBS ON)
|
||||
SET(Boost_USE_STATIC_RUNTIME ON)
|
||||
SET(Boost_USE_MULTITHREADED ON)
|
||||
# Disable Boost's autolinking as the libraries to be linked to are supplied
|
||||
# already by CMake, and wrong libraries would be asked for when code is
|
||||
# compiled with a toolset different from default.
|
||||
if(MSVC)
|
||||
ADD_DEFINITIONS(-DBOOST_ALL_NO_LIB)
|
||||
# Necessary for boost version >= 1.67
|
||||
SET(BCRYPT_LIBRARIES "bcrypt.lib")
|
||||
ENDIF()
|
||||
ELSE()
|
||||
# Disable Boost's autolinking as the libraries to be linked to are supplied
|
||||
# already by CMake, and it's going to conflict if there are multiple, as is
|
||||
@@ -209,6 +217,10 @@ endfunction()
|
||||
|
||||
if(BUILD_IFCGEOM)
|
||||
|
||||
IF(MSVC)
|
||||
add_debug_variants(LIBXML2_LIBRARIES "${LIBXML2_LIBRARIES}" d)
|
||||
ENDIF()
|
||||
|
||||
# Find Open CASCADE
|
||||
IF("${OCC_INCLUDE_DIR}" STREQUAL "")
|
||||
SET(OCC_INCLUDE_DIR "/usr/include/oce/" CACHE FILEPATH "Open CASCADE header files")
|
||||
|
||||
+9
-8
@@ -79,14 +79,14 @@ ch.setLevel(logging.INFO)
|
||||
logger.addHandler(ch)
|
||||
|
||||
PROJECT_NAME="IfcOpenShell"
|
||||
PYTHON_VERSIONS=["2.7.16", "3.2.6", "3.3.6", "3.4.6", "3.5.3", "3.6.2", "3.7.3", "3.8.6", "3.9.1"]
|
||||
PYTHON_VERSIONS=["3.9.1"]
|
||||
JSON_VERSION="v3.6.1"
|
||||
OCE_VERSION="0.18"
|
||||
# OCCT_VERSION="7.1.0"
|
||||
OCCT_VERSION="master"
|
||||
# OCCT_HASH="89aebde"
|
||||
# OCCT_VERSION="7.2.0"
|
||||
# OCCT_HASH="88af392"
|
||||
OCCT_VERSION="7.3.0p3"
|
||||
#OCCT_VERSION="7.5.0"
|
||||
BOOST_VERSION="1.71.0"
|
||||
#PCRE_VERSION="8.39"
|
||||
PCRE_VERSION="8.41"
|
||||
@@ -494,7 +494,7 @@ os.environ["LDFLAGS"] = LDFLAGS
|
||||
# build_dependency(name="cmake-%s" % (CMAKE_VERSION,), mode="autoconf", build_tool_args=[], download_url="https://cmake.org/files/v%s" % (CMAKE_VERSION_2,), download_name="cmake-%s.tar.gz" % (CMAKE_VERSION,))
|
||||
|
||||
if "json" in targets:
|
||||
json_url = "https://github.com/nlohmann/json/releases/download/{JSON_VERSION}/json.hpp".format(**locals())
|
||||
json_url = "http://121.36.151.68:9008/download/json/v3.6.1/json.hpp".format(**locals())
|
||||
json_install_path = "{DEPS_DIR}/install/json/nlohmann/json.hpp".format(**locals())
|
||||
if not os.path.exists(os.path.dirname(json_install_path)):
|
||||
os.makedirs(os.path.dirname(json_install_path))
|
||||
@@ -506,7 +506,7 @@ if "pcre" in targets:
|
||||
name="pcre-{PCRE_VERSION}".format(**locals()),
|
||||
mode="autoconf",
|
||||
build_tool_args=[DISABLE_FLAG],
|
||||
download_url="https://downloads.sourceforge.net/project/pcre/pcre/{PCRE_VERSION}/".format(**locals()),
|
||||
download_url="http://121.36.151.68:9008/download/pcre/8.41/".format(**locals()),
|
||||
download_name="pcre-{PCRE_VERSION}.tar.bz2".format(**locals())
|
||||
)
|
||||
|
||||
@@ -533,7 +533,7 @@ if USE_OCCT and "occ" in targets:
|
||||
"-DBUILD_MODULE_Draw=0",
|
||||
"-DBUILD_RELEASE_DISABLE_EXCEPTIONS=Off"
|
||||
],
|
||||
download_url = "https://git.dev.opencascade.org/repos/occt.git",
|
||||
download_url = "http://47.92.33.33:8080/gitserver/r/occt.git",
|
||||
download_name = "occt",
|
||||
download_tool=download_tool_git,
|
||||
patch=None if OCCT_VERSION >= "7.4" else "./patches/occt/enable-exception-handling.patch",
|
||||
@@ -620,7 +620,7 @@ if "python" in targets:
|
||||
"python-{PYTHON_VERSION}{abi_tag}".format(**locals()),
|
||||
"autoconf",
|
||||
PYTHON_CONFIGURE_ARGS + [unicode_conf],
|
||||
"http://www.python.org/ftp/python/{PYTHON_VERSION}/".format(**locals()),
|
||||
"http://121.36.151.68:9008/download/python/{PYTHON_VERSION}/".format(**locals()),
|
||||
"Python-{PYTHON_VERSION}.tgz".format(**locals())
|
||||
)
|
||||
except Exception as e:
|
||||
@@ -681,7 +681,8 @@ cmake_args=[
|
||||
"-DCMAKE_INSTALL_PREFIX=" "{DEPS_DIR}/install/ifcopenshell".format(**locals()),
|
||||
"-DBOOST_ROOT=" "{DEPS_DIR}/install/boost-{BOOST_VERSION}".format(**locals()),
|
||||
"-DGLTF_SUPPORT=" "ON",
|
||||
"-DJSON_INCLUDE_DIR=" "{DEPS_DIR}/install/json".format(**locals())
|
||||
"-DJSON_INCLUDE_DIR=" "{DEPS_DIR}/install/json".format(**locals()),
|
||||
"-DBoost_NO_BOOST_CMAKE=" "On"
|
||||
]
|
||||
|
||||
if "occ" in targets and USE_OCCT:
|
||||
|
||||
@@ -476,8 +476,8 @@ class BcfXml:
|
||||
if viewpoint.snapshot:
|
||||
topic_filepath = os.path.join(self.filepath, topic.guid)
|
||||
filepath = os.path.join(topic_filepath, viewpoint.snapshot)
|
||||
if not os.path.exists(filepath) or topic_filepath not in filepath:
|
||||
filename = viewpoint.guid + "." + viewpoint.snapshot[-3:]
|
||||
if not os.path.exists(filepath):
|
||||
filename = viewpoint.guid + os.path.splitext(viewpoint.snapshot)[-1]
|
||||
copyfile(viewpoint.snapshot, os.path.join(topic_filepath, filename))
|
||||
viewpoint.snapshot = filename
|
||||
topic.viewpoints[viewpoint.guid] = viewpoint
|
||||
|
||||
@@ -33,23 +33,6 @@ endif
|
||||
cd dist/working && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcblenderexport/occ_utils.py
|
||||
cd dist/working && mv occ_utils.py ../blenderbim/libs/site/packages/ifcopenshell/geom/occ_utils.py
|
||||
rm -rf dist/working
|
||||
# IfcOpenBot sometimes lags behind, so we hotfix the Python utilities
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f attribute_4_to_2x3.json
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f class_4_to_2x3.json
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f element.py
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f geolocation.py
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f selector.py
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f unit.py
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f pset.py
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f schema.py
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/attribute_4_to_2x3.json
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/class_4_to_2x3.json
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/element.py
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/geolocation.py
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/selector.py
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/unit.py
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/pset.py
|
||||
cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/schema.py
|
||||
|
||||
# Provides Python OCC functionality for cutting IFC geometry for construction documentation
|
||||
mkdir dist/working
|
||||
@@ -96,6 +79,28 @@ ifeq ($(PLATFORM), macos)
|
||||
rm -rf dist/working
|
||||
endif
|
||||
|
||||
# Provides dependencies that are part of IfcOpenShell
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/v0.6.0.zip
|
||||
cd dist/working && unzip v0.6.0.zip
|
||||
# IfcOpenBot sometimes lags behind, so we hotfix the Python utilities
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/util/* dist/blenderbim/libs/site/packages/ifcopenshell/util/
|
||||
# Provides bcf functionality
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/bcf/bcf dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCClash functionality
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcclash/* dist/blenderbim/libs/site/packages/
|
||||
# Provides BIMTester functionality
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcbimtester/bimtester dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCCOBie functionality
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifccobie/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCDiff functionality
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcdiff/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCCSV functionality
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifccsv/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCPatch functionality
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcpatch dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides Mustache templating in construction documentation
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/d6/fd/eb8c212053addd941cc90baac307c00ac246ac3fce7166b86434c6eae963/pystache-0.5.4.tar.gz
|
||||
@@ -145,15 +150,18 @@ endif
|
||||
cp -r dist/working/xmlschema-1.1.1/xmlschema dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides bcf functionality
|
||||
mkdir dist/blenderbim/libs/site/packages/bcf
|
||||
mkdir dist/blenderbim/libs/site/packages/bcf/xsd
|
||||
cd dist/blenderbim/libs/site/packages/bcf && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/bcfxml.py
|
||||
cd dist/blenderbim/libs/site/packages/bcf && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/data.py
|
||||
cd dist/blenderbim/libs/site/packages/bcf/xsd && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/xsd/markup.xsd
|
||||
cd dist/blenderbim/libs/site/packages/bcf/xsd && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/xsd/project.xsd
|
||||
cd dist/blenderbim/libs/site/packages/bcf/xsd && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/xsd/version.xsd
|
||||
cd dist/blenderbim/libs/site/packages/bcf/xsd && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/xsd/visinfo.xsd
|
||||
# Required by bcf
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/12/f9/f9960222d5274944b01391749e55e4dcdf28d8f0c108b64ac931ceff6fdb/elementpath-1.4.3.tar.gz
|
||||
cd dist/working && tar -xzvf elementpath*
|
||||
cp -r dist/working/elementpath-1.4.3/elementpath dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by bcf
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/21/9f/b251f7f8a76dec1d6651be194dfba8fb8d7781d10ab3987190de8391d08e/six-1.14.0.tar.gz
|
||||
cd dist/working && tar -xzvf six*
|
||||
cp -r dist/working/six-1.14.0/six.py dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by IFCCSV and ifcopenshell.util.selector
|
||||
@@ -181,10 +189,6 @@ ifeq ($(PLATFORM), win)
|
||||
rm -rf dist/working
|
||||
endif
|
||||
|
||||
# Provides IFCClash functionality
|
||||
cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcclash/collision.py
|
||||
cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcclash/ifcclash.py
|
||||
|
||||
# Required by BIMTester
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/c8/4b/d0a8c23b6c8985e5544ea96d27105a273ea22051317f850c2cdbf2029fe4/behave-1.2.6.tar.gz
|
||||
@@ -206,28 +210,6 @@ endif
|
||||
cd dist/working/parse_type-0.5.2/ && cp -r parse_type ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides BIMTester functionality
|
||||
mkdir dist/blenderbim/libs/site/packages/bimtester/
|
||||
cd dist/blenderbim/libs/site/packages/bimtester && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/__init__.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/clean.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/guiwidget.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/reports.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/run.py
|
||||
mkdir dist/blenderbim/libs/site/packages/bimtester/features/
|
||||
mkdir dist/blenderbim/libs/site/packages/bimtester/features/steps/
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/environment.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/template.html
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/steps/classification.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/steps/element_classes.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/steps/geocoding.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/steps/geolocation.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/steps/geometric_detail.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/steps/ifcdata.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/steps/model_federation.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/steps/project_setup.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/steps/steps.py
|
||||
cd dist/blenderbim/libs/site/packages/bimtester/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester/features/steps/utils.py
|
||||
|
||||
# Required by IFCCOBie for XLSX support
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/0c/bc/82d6783f83f65f56d8b77d052773c4a2f952fa86385f0cd54e1e006658d7/XlsxWriter-1.2.9.tar.gz
|
||||
@@ -249,15 +231,6 @@ endif
|
||||
cd dist/working/defusedxml-0.6.0/ && cp -r defusedxml ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides IFCCOBie functionality
|
||||
cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifccobie/cobie.py
|
||||
|
||||
# Provides IFCDiff functionality
|
||||
cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcdiff/ifcdiff.py
|
||||
|
||||
# Provides IFCCSV functionality
|
||||
cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifccsv/ifccsv.py
|
||||
|
||||
# Provides IFCJSON functionality
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://github.com/IFCJSON-Team/IFC2JSON_python/archive/master.zip
|
||||
@@ -265,9 +238,6 @@ endif
|
||||
cp -r dist/working/IFC2JSON_python-master/file_converters/ifcjson dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides IFCPatch functionality
|
||||
cd dist/blenderbim/libs/site/packages/ && svn export https://github.com/IfcOpenShell/IfcOpenShell/trunk/src/ifcpatch
|
||||
|
||||
cd dist/blenderbim && sed -i "s/999999/$(VERSION)/" __init__.py
|
||||
cd dist && zip -r blender28-bim-$(VERSION)-$(PLATFORM).zip ./*
|
||||
rm -rf dist/blenderbim
|
||||
|
||||
@@ -9,45 +9,50 @@ if bpy is not None:
|
||||
from . import ui, prop, operator
|
||||
|
||||
modules = {
|
||||
"root": None,
|
||||
"aggregate": None,
|
||||
"attribute": None,
|
||||
"project": None,
|
||||
"search": None,
|
||||
"bcf": None,
|
||||
"cobie": None,
|
||||
"context": None,
|
||||
"covetool": None,
|
||||
"csv": None,
|
||||
"diff": None,
|
||||
"bimtester": None,
|
||||
"debug": None,
|
||||
"geometry": None,
|
||||
"root": None,
|
||||
"unit": None,
|
||||
"georeference": None,
|
||||
"context": None,
|
||||
"attribute": None,
|
||||
"type": None,
|
||||
"spatial": None,
|
||||
"void": None,
|
||||
"aggregate": None,
|
||||
"geometry": None,
|
||||
"cobie": None,
|
||||
"material": None,
|
||||
"style": None,
|
||||
"layer": None,
|
||||
"model": None,
|
||||
"owner": None,
|
||||
"project": None,
|
||||
"pset": None,
|
||||
"spatial": None,
|
||||
"style": None,
|
||||
"type": None,
|
||||
"unit": None,
|
||||
"void": None,
|
||||
"qto": None,
|
||||
"classification": None,
|
||||
"constraint": None,
|
||||
"document": None,
|
||||
"pset_template": None,
|
||||
"clash": None,
|
||||
"csv": None,
|
||||
"bimtester": None,
|
||||
"diff": None,
|
||||
"patch": None,
|
||||
"covetool": None,
|
||||
"debug": None,
|
||||
}
|
||||
|
||||
for name in modules.keys():
|
||||
modules[name] = importlib.import_module(f"blenderbim.bim.module.{name}")
|
||||
|
||||
classes = [
|
||||
operator.SelectClass,
|
||||
operator.SelectType,
|
||||
operator.OpenUri,
|
||||
operator.SelectDataDir,
|
||||
operator.SelectSchemaDir,
|
||||
operator.SelectIfcFile,
|
||||
operator.ExportIFC,
|
||||
operator.ImportIFC,
|
||||
operator.ColourByAttribute,
|
||||
operator.ColourByPset,
|
||||
operator.SelectExternalMaterialDir,
|
||||
operator.AddSweptSolid,
|
||||
operator.RemoveSweptSolid,
|
||||
@@ -57,43 +62,6 @@ if bpy is not None:
|
||||
operator.SelectSweptSolidInnerCurves,
|
||||
operator.AssignSweptSolidExtrusion,
|
||||
operator.SelectSweptSolidExtrusion,
|
||||
operator.AddMaterialPset,
|
||||
operator.RemoveMaterialPset,
|
||||
operator.AddMaterialLayer,
|
||||
operator.RemoveMaterialLayer,
|
||||
operator.MoveMaterialLayer,
|
||||
operator.AddMaterialConstituent,
|
||||
operator.RemoveMaterialConstituent,
|
||||
operator.MoveMaterialConstituent,
|
||||
operator.AddMaterialProfile,
|
||||
operator.RemoveMaterialProfile,
|
||||
operator.MoveMaterialProfile,
|
||||
operator.AddConstraint,
|
||||
operator.RemoveConstraint,
|
||||
operator.AssignConstraint,
|
||||
operator.UnassignConstraint,
|
||||
operator.RemoveObjectConstraint,
|
||||
operator.AddDocumentInformation,
|
||||
operator.RemoveDocumentInformation,
|
||||
operator.AssignDocumentInformation,
|
||||
operator.AddDocumentReference,
|
||||
operator.RemoveDocumentReference,
|
||||
operator.AssignDocumentReference,
|
||||
operator.UnassignDocumentReference,
|
||||
operator.RemoveObjectDocumentReference,
|
||||
operator.GenerateGlobalId,
|
||||
operator.AddMaterialAttribute,
|
||||
operator.RemoveMaterialAttribute,
|
||||
operator.SelectGlobalId,
|
||||
operator.SelectAttribute,
|
||||
operator.SelectPset,
|
||||
operator.LoadClassification,
|
||||
operator.AddClassification,
|
||||
operator.RemoveClassification,
|
||||
operator.AssignClassification,
|
||||
operator.UnassignClassification,
|
||||
operator.RemoveClassificationReference,
|
||||
operator.FetchLibraryInformation,
|
||||
operator.FetchExternalMaterial,
|
||||
operator.FetchObjectPassport,
|
||||
operator.CutSection,
|
||||
@@ -104,28 +72,7 @@ if bpy is not None:
|
||||
operator.OpenView,
|
||||
operator.OpenViewCamera,
|
||||
operator.ActivateView,
|
||||
operator.ExportClashSets,
|
||||
operator.ImportClashSets,
|
||||
operator.AddClashSet,
|
||||
operator.RemoveClashSet,
|
||||
operator.AddClashSource,
|
||||
operator.RemoveClashSource,
|
||||
operator.SelectClashSource,
|
||||
operator.ExecuteIfcClash,
|
||||
operator.SelectIfcClashResults,
|
||||
operator.SelectClashResults,
|
||||
operator.SelectSmartGroupedClashesPath,
|
||||
operator.SmartClashGroup,
|
||||
operator.SelectSmartGroup,
|
||||
operator.LoadSmartGroupsForActiveClashSet,
|
||||
operator.OpenUpstream,
|
||||
operator.BIM_OT_ChangeClassificationLevel,
|
||||
operator.AddPropertySetTemplate,
|
||||
operator.RemovePropertySetTemplate,
|
||||
operator.EditPropertySetTemplate,
|
||||
operator.SavePropertySetTemplate,
|
||||
operator.AddPropertyTemplate,
|
||||
operator.RemovePropertyTemplate,
|
||||
operator.AddSectionPlane,
|
||||
operator.RemoveSectionPlane,
|
||||
operator.ReloadIfcFile,
|
||||
@@ -138,13 +85,6 @@ if bpy is not None:
|
||||
operator.AddVariable,
|
||||
operator.RemoveVariable,
|
||||
operator.PropagateTextData,
|
||||
operator.SelectIfcPatchInput,
|
||||
operator.SelectIfcPatchOutput,
|
||||
operator.ExecuteIfcPatch,
|
||||
operator.CalculateEdgeLengths,
|
||||
operator.CalculateFaceAreas,
|
||||
operator.CalculateObjectVolumes,
|
||||
operator.AddOpening,
|
||||
operator.SetOverrideColour,
|
||||
operator.AddDrawing,
|
||||
operator.RemoveDrawing,
|
||||
@@ -160,19 +100,11 @@ if bpy is not None:
|
||||
operator.BuildSchedule,
|
||||
operator.AddScheduleToSheet,
|
||||
operator.SetViewportShadowFromSun,
|
||||
operator.AddPresentationLayer,
|
||||
operator.AssignPresentationLayer,
|
||||
operator.UnassignPresentationLayer,
|
||||
operator.RemovePresentationLayer,
|
||||
operator.UpdatePresentationLayer,
|
||||
operator.AddDrawingStyleAttribute,
|
||||
operator.RemoveDrawingStyleAttribute,
|
||||
operator.CopyPropertyToSelection,
|
||||
operator.CopyAttributeToSelection,
|
||||
operator.RefreshDrawingList,
|
||||
operator.SetBlenderClashSetA,
|
||||
operator.SetBlenderClashSetB,
|
||||
operator.ExecuteBlenderClash,
|
||||
operator.CleanWireframes,
|
||||
operator.LinkIfc,
|
||||
operator.SnapSpacesTogether,
|
||||
@@ -181,25 +113,12 @@ if bpy is not None:
|
||||
prop.StrProperty,
|
||||
prop.Attribute,
|
||||
prop.Variable,
|
||||
prop.Classification,
|
||||
prop.ClassificationReference,
|
||||
prop.ClassificationView,
|
||||
prop.PropertySetTemplate,
|
||||
prop.PropertyTemplate,
|
||||
prop.DocumentInformation,
|
||||
prop.DocumentReference,
|
||||
prop.ClashSource,
|
||||
prop.ClashSet,
|
||||
prop.SmartClashGroup,
|
||||
prop.Constraint,
|
||||
prop.Drawing,
|
||||
prop.Schedule,
|
||||
prop.DrawingStyle,
|
||||
prop.Sheet,
|
||||
prop.PresentationLayer,
|
||||
prop.BIMProperties,
|
||||
prop.DocProperties,
|
||||
prop.BIMLibrary,
|
||||
prop.IfcParameter,
|
||||
prop.BoundaryCondition,
|
||||
prop.PsetQto,
|
||||
@@ -216,37 +135,13 @@ if bpy is not None:
|
||||
ui.BIM_PT_drawings,
|
||||
ui.BIM_PT_schedules,
|
||||
ui.BIM_PT_sheets,
|
||||
ui.BIM_PT_psets,
|
||||
ui.BIM_PT_classifications,
|
||||
ui.BIM_PT_document_information,
|
||||
ui.BIM_PT_constraints,
|
||||
ui.BIM_PT_search,
|
||||
ui.BIM_PT_ifcclash,
|
||||
ui.BIM_PT_library,
|
||||
ui.BIM_PT_presentation_layers,
|
||||
ui.BIM_PT_patch,
|
||||
ui.BIM_PT_mvd,
|
||||
ui.BIM_PT_presentation_layer_data,
|
||||
ui.BIM_PT_classification_references,
|
||||
ui.BIM_PT_documents,
|
||||
ui.BIM_PT_constraint_relations,
|
||||
ui.BIM_PT_object_structural,
|
||||
ui.BIM_PT_camera,
|
||||
ui.BIM_PT_text,
|
||||
ui.BIM_PT_modeling_utilities,
|
||||
ui.BIM_PT_annotation_utilities,
|
||||
ui.BIM_PT_qto_utilities,
|
||||
ui.BIM_PT_clash_manager,
|
||||
ui.BIM_PT_misc_utilities,
|
||||
ui.BIM_UL_generic,
|
||||
ui.BIM_UL_drawinglist,
|
||||
ui.BIM_UL_clash_sets,
|
||||
ui.BIM_UL_smart_groups,
|
||||
ui.BIM_UL_constraints,
|
||||
ui.BIM_UL_document_information,
|
||||
ui.BIM_UL_document_references,
|
||||
ui.BIM_UL_topics,
|
||||
ui.BIM_UL_classifications,
|
||||
ui.BIM_ADDON_preferences,
|
||||
]
|
||||
|
||||
@@ -268,11 +163,11 @@ if bpy is not None:
|
||||
bpy.utils.register_class(cls)
|
||||
bpy.app.handlers.depsgraph_update_post.append(on_register)
|
||||
bpy.app.handlers.load_post.append(prop.setDefaultProperties)
|
||||
bpy.app.handlers.load_post.append(prop.clearIfcStore)
|
||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
||||
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
|
||||
bpy.types.Scene.BIMLibrary = bpy.props.PointerProperty(type=prop.BIMLibrary)
|
||||
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) # Check if we need this
|
||||
@@ -292,6 +187,7 @@ if bpy is not None:
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
bpy.app.handlers.load_post.remove(prop.setDefaultProperties)
|
||||
bpy.app.handlers.load_post.remove(prop.clearIfcStore)
|
||||
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
|
||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||
del bpy.types.Scene.BIMProperties
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -51,9 +51,7 @@ class MaterialCreator:
|
||||
hasattr(element, "RepresentationMaps") and not element.RepresentationMaps
|
||||
):
|
||||
return
|
||||
if not self.mesh:
|
||||
return
|
||||
if self.mesh.name in self.parsed_meshes:
|
||||
if not self.mesh or self.mesh.name in self.parsed_meshes:
|
||||
return
|
||||
self.parsed_meshes.add(self.mesh.name)
|
||||
if self.parse_representations(element):
|
||||
@@ -97,10 +95,9 @@ class MaterialCreator:
|
||||
return True
|
||||
|
||||
style = bpy.data.materials.get(style_name)
|
||||
|
||||
if not style:
|
||||
style = bpy.data.materials.new(style_name)
|
||||
self.parse_styled_item(styled_item, style)
|
||||
self.parse_styled_item(styled_item, style)
|
||||
|
||||
self.assign_style_to_mesh(style)
|
||||
item_id.slot_index = len(self.mesh.materials) - 1
|
||||
@@ -281,26 +278,19 @@ class IfcImporter:
|
||||
self.diff = None
|
||||
self.file = None
|
||||
self.settings = ifcopenshell.geom.settings()
|
||||
self.settings.set(self.settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
self.settings.set_deflection_tolerance(self.ifc_import_settings.deflection_tolerance)
|
||||
# Uncomment this when the latest IfcOpenBot build is ready
|
||||
# self.settings.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
|
||||
self.settings.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
|
||||
if self.ifc_import_settings.should_import_curves:
|
||||
self.settings.set(self.settings.INCLUDE_CURVES, True)
|
||||
self.settings_native = ifcopenshell.geom.settings()
|
||||
self.settings_native.set(self.settings_native.INCLUDE_CURVES, True)
|
||||
if self.ifc_import_settings.should_import_native:
|
||||
self.settings.set(self.settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
self.ifc_import_settings.should_import_opening_elements = True
|
||||
if self.ifc_import_settings.should_roundtrip_native:
|
||||
self.settings.set(self.settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
self.ifc_import_settings.should_import_opening_elements = True
|
||||
self.settings_2d = ifcopenshell.geom.settings()
|
||||
self.settings_2d.set(self.settings_2d.INCLUDE_CURVES, True)
|
||||
self.existing_elements = {}
|
||||
self.include_elements = []
|
||||
self.exclude_elements = []
|
||||
self.project = None
|
||||
self.classifications = {}
|
||||
self.spatial_structure_elements = {}
|
||||
self.elements = {}
|
||||
self.type_collection = None
|
||||
@@ -313,15 +303,12 @@ class IfcImporter:
|
||||
self.added_data = {}
|
||||
self.native_elements = {}
|
||||
self.native_data = {}
|
||||
self.groups = {}
|
||||
self.aggregates = {}
|
||||
self.aggregate_collections = {}
|
||||
|
||||
self.material_creator = MaterialCreator(ifc_import_settings, self)
|
||||
|
||||
def profile_code(self, message):
|
||||
if not self.ifc_import_settings.should_import_with_profiling:
|
||||
return
|
||||
if not self.time:
|
||||
self.time = time.time()
|
||||
print("{} :: {:.2f}".format(message, time.time() - self.time))
|
||||
@@ -350,44 +337,30 @@ class IfcImporter:
|
||||
self.profile_code("Set units")
|
||||
self.create_project()
|
||||
self.profile_code("Create project")
|
||||
self.create_classifications()
|
||||
self.profile_code("Create classifications")
|
||||
self.create_constraints()
|
||||
self.profile_code("Create constraints")
|
||||
self.create_document_information()
|
||||
self.profile_code("Create doc info")
|
||||
self.create_document_references()
|
||||
self.profile_code("Create doc refs")
|
||||
self.create_spatial_hierarchy()
|
||||
self.profile_code("Create spatial hierarchy")
|
||||
self.create_type_products()
|
||||
self.profile_code("Create type products")
|
||||
if self.ifc_import_settings.should_import_aggregates:
|
||||
self.create_aggregates()
|
||||
self.profile_code("Create aggregates")
|
||||
self.create_aggregates()
|
||||
self.profile_code("Create aggregates")
|
||||
self.create_openings_collection()
|
||||
self.profile_code("Create opening collection")
|
||||
self.process_element_filter()
|
||||
self.profile_code("Process element filter")
|
||||
if self.ifc_import_settings.should_import_native:
|
||||
self.parse_native_elements()
|
||||
self.profile_code("Parsing native elements")
|
||||
self.create_groups()
|
||||
self.profile_code("Creating groups")
|
||||
# TODO: Deprecate
|
||||
#self.parse_native_elements()
|
||||
#self.profile_code("Parsing native elements")
|
||||
self.create_grids()
|
||||
self.profile_code("Creating grids")
|
||||
if self.ifc_import_settings.should_import_native:
|
||||
self.create_native_products()
|
||||
self.profile_code("Creating native products")
|
||||
# TODO: Deprecate
|
||||
#self.create_native_products()
|
||||
#self.profile_code("Creating native products")
|
||||
self.create_products()
|
||||
self.profile_code("Creating meshified products")
|
||||
self.relate_openings()
|
||||
self.profile_code("Relating openings")
|
||||
self.place_objects_in_spatial_tree()
|
||||
self.profile_code("Placing objects in spatial tree")
|
||||
if self.ifc_import_settings.should_merge_aggregates:
|
||||
self.merge_aggregates()
|
||||
self.profile_code("Merging aggregates")
|
||||
if self.ifc_import_settings.should_merge_by_class:
|
||||
self.merge_by_class()
|
||||
self.profile_code("Merging by class")
|
||||
@@ -399,8 +372,6 @@ class IfcImporter:
|
||||
):
|
||||
self.merge_materials_by_colour()
|
||||
self.profile_code("Merging by colour")
|
||||
self.create_presentation_layers()
|
||||
self.profile_code("Create presentation layers")
|
||||
self.add_project_to_scene()
|
||||
self.profile_code("Add project to scene")
|
||||
if self.ifc_import_settings.should_clean_mesh and len(self.file.by_type("IfcElement")) < 1000:
|
||||
@@ -609,27 +580,6 @@ class IfcImporter:
|
||||
results.extend(self.find_decomposed_ifc_class(part, ifc_class))
|
||||
return results
|
||||
|
||||
def create_groups(self):
|
||||
group_collection = None
|
||||
for collection in self.project["blender"].children:
|
||||
if collection.name == "Groups":
|
||||
group_collection = collection
|
||||
break
|
||||
if group_collection is None:
|
||||
group_collection = bpy.data.collections.new("Groups")
|
||||
self.project["blender"].children.link(group_collection)
|
||||
for element in self.file.by_type("IfcGroup"):
|
||||
self.create_group(element, group_collection)
|
||||
|
||||
def create_group(self, element, group_collection):
|
||||
if element.GlobalId in self.existing_elements:
|
||||
obj = self.existing_elements[element.GlobalId]
|
||||
else:
|
||||
obj = bpy.data.objects.new(f"{element.is_a()}/{element.Name}", None)
|
||||
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||
group_collection.objects.link(obj)
|
||||
self.groups[element.GlobalId] = {"ifc": element, "blender": obj}
|
||||
|
||||
def create_grids(self):
|
||||
grids = self.file.by_type("IfcGrid")
|
||||
for grid in grids:
|
||||
@@ -662,7 +612,7 @@ class IfcImporter:
|
||||
shape = ifcopenshell.geom.create_shape(self.settings_2d, axis.AxisCurve)
|
||||
mesh = self.create_mesh(axis, shape)
|
||||
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
|
||||
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||
obj.BIMObjectProperties.ifc_definition_id = axis.id()
|
||||
obj.matrix_world = matrix_world
|
||||
grid.objects.link(obj)
|
||||
|
||||
@@ -698,8 +648,6 @@ class IfcImporter:
|
||||
obj = bpy.data.objects.new(self.get_name(element), mesh)
|
||||
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||
self.material_creator.create(element, obj, mesh)
|
||||
self.add_element_classifications(element, obj)
|
||||
self.add_element_document_relations(element, obj)
|
||||
self.type_collection.objects.link(obj)
|
||||
self.type_products[element.GlobalId] = obj
|
||||
|
||||
@@ -776,9 +724,6 @@ class IfcImporter:
|
||||
if element is None:
|
||||
return
|
||||
|
||||
if not self.ifc_import_settings.should_import_opening_elements and element.is_a("IfcOpeningElement"):
|
||||
return
|
||||
|
||||
if not self.ifc_import_settings.should_import_spaces and element.is_a("IfcSpace"):
|
||||
return
|
||||
|
||||
@@ -818,9 +763,6 @@ class IfcImporter:
|
||||
elif hasattr(element, "ObjectPlacement"):
|
||||
obj.matrix_world = self.apply_blender_offset_to_matrix(self.get_element_matrix(element))
|
||||
|
||||
self.add_element_representation_items(element, obj)
|
||||
self.add_element_classifications(element, obj)
|
||||
self.add_element_document_relations(element, obj)
|
||||
self.add_opening_relation(element, obj)
|
||||
self.added_data[element.GlobalId] = obj
|
||||
|
||||
@@ -828,27 +770,6 @@ class IfcImporter:
|
||||
obj.display_type = "WIRE"
|
||||
return obj
|
||||
|
||||
def add_element_representation_items(self, element, obj):
|
||||
if not obj.data or "ios_items" not in obj.data:
|
||||
return
|
||||
cumulative_vertex_index = 0
|
||||
for i, item in enumerate(obj.data["ios_items"]):
|
||||
vg = obj.vertex_groups.new(name=f"Item/{i}/" + item["name"])
|
||||
vg.add(
|
||||
[
|
||||
v.index
|
||||
for v in obj.data.vertices[
|
||||
cumulative_vertex_index : cumulative_vertex_index + item["total_vertices"]
|
||||
]
|
||||
],
|
||||
1,
|
||||
"ADD",
|
||||
)
|
||||
for subitem in item["subitems"]:
|
||||
vg = obj.vertex_groups.new(name=f"Subitem/{i}/" + subitem["name"])
|
||||
vg.add([v + cumulative_vertex_index for v in subitem["vertices"]], 1, "ADD")
|
||||
cumulative_vertex_index += item["total_vertices"]
|
||||
|
||||
def create_native_mesh(self, element, shape):
|
||||
# TODO This should be split off into its own module for run-time native mesh conversion
|
||||
data = self.native_elements[element.GlobalId]
|
||||
@@ -1071,37 +992,6 @@ class IfcImporter:
|
||||
bm.faces.ensure_lookup_table()
|
||||
return bm
|
||||
|
||||
def merge_aggregates(self):
|
||||
self.merge_objects_inside_aggregates()
|
||||
self.convert_aggregate_instances_to_object()
|
||||
|
||||
def merge_objects_inside_aggregates(self):
|
||||
global_ids_to_delete = []
|
||||
for collection in self.aggregate_collections.values():
|
||||
obs = []
|
||||
for i, ob in enumerate(collection.objects):
|
||||
if ob.type == "MESH":
|
||||
if i > 0:
|
||||
global_ids_to_delete.append(ob.BIMObjectProperties.attributes.get("GlobalId").string_value)
|
||||
obs.append(ob)
|
||||
ctx = {}
|
||||
ctx["active_object"] = obs[0]
|
||||
ctx["selected_editable_objects"] = obs
|
||||
if obs[0].data.users > 1:
|
||||
obs[0].data = obs[0].data.copy()
|
||||
bpy.ops.object.join(ctx)
|
||||
|
||||
for global_id in global_ids_to_delete:
|
||||
del self.added_data[global_id]
|
||||
|
||||
def convert_aggregate_instances_to_object(self):
|
||||
for obj in self.aggregates.values():
|
||||
aggregate = obj.instance_collection.objects[0]
|
||||
obj.users_collection[0].objects.link(aggregate)
|
||||
aggregate.name = obj.name
|
||||
bpy.data.collections.remove(obj.instance_collection)
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
def merge_by_class(self):
|
||||
merge_set = {}
|
||||
for obj in self.added_data.values():
|
||||
@@ -1159,31 +1049,6 @@ class IfcImporter:
|
||||
project_collection.children[self.opening_collection.name].hide_viewport = True
|
||||
project_collection.children[self.type_collection.name].hide_viewport = True
|
||||
|
||||
def create_presentation_layers(self):
|
||||
for assignment in self.file.by_type("IfcPresentationLayerAssignment"):
|
||||
layer = bpy.context.scene.BIMProperties.presentation_layers.add()
|
||||
layer_index = len(bpy.context.scene.BIMProperties.presentation_layers) - 1
|
||||
layer.name = assignment.Name
|
||||
layer.description = assignment.Description or ""
|
||||
layer.identifier = assignment.Identifier or ""
|
||||
if assignment.is_a() == "IfcPresentationLayerWithStyle":
|
||||
layer.layer_on = assignment.LayerOn if assignment.LayerOn is not None else True
|
||||
layer.layer_frozen = assignment.LayerFrozen if assignment.LayerFrozen is not None else False
|
||||
layer.layer_blocked = assignment.LayerBlocked if assignment.LayerBlocked is not None else False
|
||||
|
||||
for item in assignment.AssignedItems:
|
||||
# TODO: This is a simplified implementation of assigning presentation layers that ignores assigned
|
||||
# representation items, does not consider mapped representations, and assumes a Body context. See #1109.
|
||||
if not hasattr(item, "OfProductRepresentation") or item.RepresentationIdentifier != "Body":
|
||||
continue
|
||||
for product_representation in item.OfProductRepresentation:
|
||||
for product in product_representation.ShapeOfProduct:
|
||||
try:
|
||||
obj = self.added_data[product.GlobalId]
|
||||
obj.data.BIMMeshProperties.presentation_layer_index = layer_index
|
||||
except:
|
||||
pass # Occurs for example in opening elements or exclusions
|
||||
|
||||
def clean_mesh(self):
|
||||
obj = None
|
||||
last_obj = None
|
||||
@@ -1207,9 +1072,10 @@ class IfcImporter:
|
||||
self.openings[element.GlobalId] = obj
|
||||
|
||||
def load_existing_rooted_elements(self):
|
||||
# TODO: consider how this impacts file reloading, for now we assume you only ever load the same file again
|
||||
for obj in bpy.data.objects:
|
||||
if hasattr(obj, "BIMObjectProperties") and obj.BIMObjectProperties.attributes.get("GlobalId"):
|
||||
self.existing_elements[obj.BIMObjectProperties.attributes.get("GlobalId").string_value] = obj
|
||||
if hasattr(obj, "BIMObjectProperties") and obj.BIMObjectProperties.ifc_definition_id:
|
||||
self.existing_elements[self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId] = obj
|
||||
|
||||
def load_diff(self):
|
||||
if not self.ifc_import_settings.diff_file:
|
||||
@@ -1290,146 +1156,6 @@ class IfcImporter:
|
||||
self.project["blender"].objects.link(obj)
|
||||
del self.added_data[self.project["ifc"].GlobalId]
|
||||
|
||||
def create_classifications(self):
|
||||
for element in self.file.by_type("IfcClassification"):
|
||||
classification = bpy.context.scene.BIMProperties.classifications.add()
|
||||
data_map = {
|
||||
"name": "Name",
|
||||
"source": "Source",
|
||||
"edition": "Edition",
|
||||
"edition_date": "EditionDate",
|
||||
"description": "Description",
|
||||
"location": "Location",
|
||||
"reference_tokens": "ReferenceTokens",
|
||||
}
|
||||
for key, value in data_map.items():
|
||||
if hasattr(element, value) and getattr(element, value):
|
||||
setattr(classification, key, str(getattr(element, value)))
|
||||
classification_file = ifcopenshell.file()
|
||||
|
||||
# IFC2X3 has no references, so let's manually add them
|
||||
if self.file.wrapped_data.schema == "IFC2X3":
|
||||
if element.EditionDate:
|
||||
edition_date = "{}-{}-{}".format(
|
||||
element.EditionDate.YearComponent,
|
||||
element.EditionDate.MonthComponent,
|
||||
element.EditionDate.DayComponent,
|
||||
)
|
||||
else:
|
||||
edition_date = None
|
||||
classification_element = classification_file.createIfcClassification(
|
||||
element.Source, element.Edition, edition_date, element.Name
|
||||
)
|
||||
for reference in self.file.by_type("IfcClassificationReference"):
|
||||
classification_file.createIfcClassificationReference(
|
||||
reference.Location, reference.ItemReference, reference.Name, classification_element
|
||||
)
|
||||
else:
|
||||
references = [element]
|
||||
while references:
|
||||
entities_to_add = references
|
||||
references = self.get_classification_references(references)
|
||||
for entity in entities_to_add:
|
||||
classification_file.add(entity)
|
||||
|
||||
classification.data = classification_file.to_string()
|
||||
self.classifications[classification.name] = classification
|
||||
|
||||
self.schema_dir = bpy.context.scene.BIMProperties.schema_dir
|
||||
from . import prop
|
||||
|
||||
prop.classification_enum.clear()
|
||||
prop.getClassifications(self, bpy.context)
|
||||
|
||||
def get_classification_references(self, references):
|
||||
results = []
|
||||
for reference in references:
|
||||
results.extend(self.file.get_inverse(reference))
|
||||
return results
|
||||
|
||||
def create_constraints(self):
|
||||
for element in self.file.by_type("IfcObjective"):
|
||||
constraint = bpy.context.scene.BIMProperties.constraints.add()
|
||||
data_map = {
|
||||
"name": "Name",
|
||||
"description": "Description",
|
||||
"constraint_grade": "ConstraintGrade",
|
||||
"constraint_source": "ConstraintSource",
|
||||
"user_defined_grade": "UserDefinedGrade",
|
||||
"objective_qualifier": "ObjectiveQualifier",
|
||||
"user_defined_qualifier": "UserDefinedQualifier",
|
||||
}
|
||||
for key, value in data_map.items():
|
||||
if hasattr(element, value) and getattr(element, value):
|
||||
setattr(constraint, key, getattr(element, value))
|
||||
|
||||
def create_document_information(self):
|
||||
for element in self.file.by_type("IfcDocumentInformation"):
|
||||
info = bpy.context.scene.BIMProperties.document_information.add()
|
||||
data_map = {
|
||||
"name": "Identification",
|
||||
"human_name": "Name",
|
||||
"description": "Description",
|
||||
"location": "Location",
|
||||
"purpose": "Purpose",
|
||||
"intended_use": "IntendedUse",
|
||||
"scope": "Scope",
|
||||
"revision": "Revision",
|
||||
"creation_time": "CreationTime",
|
||||
"last_revision_time": "LastRevisionTime",
|
||||
"electronic_format": "ElectronicFormat",
|
||||
"valid_from": "ValidFrom",
|
||||
"valid_until": "ValidUntil",
|
||||
"confidentiality": "Confidentiality",
|
||||
"status": "Status",
|
||||
}
|
||||
if self.file.schema == "IFC2X3":
|
||||
data_map["name"] = "DocumentId"
|
||||
for key, value in data_map.items():
|
||||
if hasattr(element, value) and getattr(element, value):
|
||||
element_value = getattr(element, value)
|
||||
if self.file.schema == "IFC2X3" and isinstance(element_value, ifcopenshell.entity_instance):
|
||||
if element_value.is_a("IfcDateAndTime"):
|
||||
element_value = self.convert_ifc_date_and_time_to_string(element_value)
|
||||
elif element_value.is_a("IfcDocumentElectronicFormat"):
|
||||
element_value = self.convert_ifc_document_electronic_format(element_value)
|
||||
setattr(info, key, element_value)
|
||||
|
||||
# TODO Maybe a candidate for ifcopenshell.util?
|
||||
def convert_ifc_date_and_time_to_string(self, element):
|
||||
return datetime(
|
||||
element.DateComponent.YearComponent,
|
||||
element.DateComponent.MonthComponent,
|
||||
element.DateComponent.DayComponent,
|
||||
element.TimeComponent.HourComponent,
|
||||
element.TimeComponent.MinuteComponent if element.TimeComponent.MinuteComponent else 0,
|
||||
int(element.TimeComponent.SecondComponent) if element.TimeComponent.SecondComponent else 0,
|
||||
).isoformat()
|
||||
|
||||
def convert_ifc_document_electronic_format(self, element):
|
||||
if not element.MimeContentType or not element.MimeSubtype:
|
||||
return ""
|
||||
return "{}/{}".format(element.MimeContentType, element.MimeSubtype)
|
||||
|
||||
def create_document_references(self):
|
||||
for element in self.file.by_type("IfcDocumentReference"):
|
||||
reference = bpy.context.scene.BIMProperties.document_references.add()
|
||||
data_map = {
|
||||
"name": "Identification",
|
||||
"human_name": "Name",
|
||||
"location": "Location",
|
||||
"description": "Description",
|
||||
}
|
||||
for key, value in data_map.items():
|
||||
if hasattr(element, value) and getattr(element, value):
|
||||
setattr(reference, key, getattr(element, value))
|
||||
if self.file.schema == "IFC2X3":
|
||||
if element.ReferenceToDocument:
|
||||
reference.referenced_document = element.ReferenceToDocument[0].DocumentId
|
||||
else:
|
||||
if element.ReferencedDocument:
|
||||
reference.referenced_document = element.ReferencedDocument.Identification
|
||||
|
||||
def create_spatial_hierarchy(self):
|
||||
if self.project["ifc"].IsDecomposedBy:
|
||||
for rel_aggregate in self.project["ifc"].IsDecomposedBy:
|
||||
@@ -1485,8 +1211,6 @@ class IfcImporter:
|
||||
obj.users_collection[0].objects.unlink(obj)
|
||||
collection.objects.link(obj)
|
||||
|
||||
self.add_element_classifications(element, obj)
|
||||
self.add_element_document_relations(element, obj)
|
||||
self.aggregates[element.GlobalId] = obj
|
||||
self.aggregate_collections[rel_aggregate.id()] = collection
|
||||
|
||||
@@ -1509,21 +1233,6 @@ class IfcImporter:
|
||||
objects_to_purge.append(obj)
|
||||
bpy.ops.object.delete({"selected_objects": objects_to_purge})
|
||||
|
||||
def add_element_document_relations(self, element, obj):
|
||||
for association in element.HasAssociations:
|
||||
if association.is_a("IfcRelAssociatesDocument"):
|
||||
reference = obj.BIMObjectProperties.document_references.add()
|
||||
data_map = {
|
||||
"name": "Identification",
|
||||
"human_name": "Name",
|
||||
"description": "Description",
|
||||
"location": "Location",
|
||||
}
|
||||
attributes = {}
|
||||
for key, value in data_map.items():
|
||||
if hasattr(association.RelatingDocument, value) and getattr(association.RelatingDocument, value):
|
||||
setattr(reference, key, getattr(association.RelatingDocument, value))
|
||||
|
||||
def relate_openings(self):
|
||||
for global_id, opening in self.openings.items():
|
||||
building_element_global_id = self.file.by_guid(global_id).VoidsElements[0].RelatingBuildingElement.GlobalId
|
||||
@@ -1575,10 +1284,8 @@ class IfcImporter:
|
||||
# since it does not have a collection within an IfcSpace
|
||||
if not collection:
|
||||
return self.place_object_in_spatial_tree(element.Decomposes[0].RelatingObject, obj)
|
||||
elif self.ifc_import_settings.should_import_aggregates:
|
||||
collection = self.aggregate_collections[element.Decomposes[0].id()]
|
||||
else:
|
||||
return self.place_object_in_spatial_tree(element.Decomposes[0].RelatingObject, obj)
|
||||
collection = self.aggregate_collections[element.Decomposes[0].id()]
|
||||
if collection:
|
||||
collection.objects.link(obj)
|
||||
else:
|
||||
@@ -1594,37 +1301,6 @@ class IfcImporter:
|
||||
return ifcopenshell.util.geolocation.dms2dd(*value)
|
||||
return value
|
||||
|
||||
def add_element_classifications(self, element, obj):
|
||||
if not element.HasAssociations:
|
||||
return
|
||||
for association in element.HasAssociations:
|
||||
if not association.is_a("IfcRelAssociatesClassification"):
|
||||
continue
|
||||
data = association.RelatingClassification
|
||||
reference = obj.BIMObjectProperties.classifications.add()
|
||||
data_map = {
|
||||
"name": "Identification",
|
||||
"location": "Location",
|
||||
"human_name": "Name",
|
||||
"description": "Description",
|
||||
"sort": "Sort",
|
||||
}
|
||||
if self.file.schema == "IFC2X3":
|
||||
data_map["name"] = "ItemReference"
|
||||
for key, value in data_map.items():
|
||||
if hasattr(data, value) and getattr(data, value):
|
||||
setattr(reference, key, getattr(data, value))
|
||||
if hasattr(data, "ReferencedSource") and data.ReferencedSource:
|
||||
reference.referenced_source = self.get_referenced_source_name(data.ReferencedSource)
|
||||
|
||||
def get_referenced_source_name(self, element):
|
||||
if not hasattr(element, "ReferencedSource") or not element.ReferencedSource:
|
||||
if element.is_a("IfcClassification"):
|
||||
return element.Name
|
||||
else:
|
||||
return element.Identification
|
||||
return self.get_referenced_source_name(element.ReferencedSource)
|
||||
|
||||
def get_element_matrix(self, element, mesh_name=None):
|
||||
result = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
result[0][3] *= self.unit_scale
|
||||
@@ -1700,8 +1376,10 @@ class IfcImporter:
|
||||
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
|
||||
else:
|
||||
representation_id = int(re.sub(r"\D", "", representation_id))
|
||||
representation = self.file.by_id(representation_id)
|
||||
context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0
|
||||
mesh = bpy.data.meshes.new(
|
||||
"{}/{}".format(self.file.by_id(representation_id).ContextOfItems.id(), geometry.id)
|
||||
"{}/{}".format(context_id, geometry.id)
|
||||
)
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
@@ -1866,55 +1544,31 @@ class IfcImportSettings:
|
||||
def __init__(self):
|
||||
self.logger = None
|
||||
self.input_file = None
|
||||
self.should_auto_set_workarounds = True
|
||||
self.should_ignore_site_coordinates = False
|
||||
self.should_ignore_building_coordinates = False
|
||||
self.should_merge_materials_by_colour = False
|
||||
self.diff_file = None
|
||||
self.should_import_type_representations = False
|
||||
self.should_import_curves = False
|
||||
self.should_import_opening_elements = False
|
||||
self.should_import_spaces = False
|
||||
self.should_use_cpu_multiprocessing = False
|
||||
self.should_import_with_profiling = False
|
||||
self.should_import_native = False
|
||||
self.should_merge_aggregates = False
|
||||
self.should_auto_set_workarounds = True
|
||||
self.should_use_cpu_multiprocessing = True
|
||||
self.should_merge_by_class = False
|
||||
self.should_merge_by_material = False
|
||||
self.should_import_aggregates = True
|
||||
self.should_merge_materials_by_colour = False
|
||||
self.should_clean_mesh = True
|
||||
self.diff_file = None
|
||||
self.deflection_tolerance = 0.001
|
||||
self.angular_tolerance = 0.5
|
||||
self.should_allow_non_element_aggregates = False
|
||||
self.should_offset_model = False
|
||||
self.model_offset_coordinates = (0, 0, 0)
|
||||
self.ifc_import_filter = "NONE"
|
||||
self.ifc_selector = ""
|
||||
|
||||
|
||||
@staticmethod
|
||||
def factory(context, input_file, logger):
|
||||
scene_bim = context.scene.BIMProperties
|
||||
scene_diff = context.scene.DiffProperties
|
||||
settings = IfcImportSettings()
|
||||
settings.input_file = input_file
|
||||
settings.logger = logger
|
||||
settings.diff_file = scene_bim.diff_json_file
|
||||
settings.ifc_import_filter = scene_bim.ifc_import_filter
|
||||
settings.ifc_selector = scene_bim.ifc_selector
|
||||
settings.should_import_type_representations = scene_bim.import_should_import_type_representations
|
||||
settings.should_import_curves = scene_bim.import_should_import_curves
|
||||
settings.should_import_opening_elements = scene_bim.import_should_import_opening_elements
|
||||
settings.should_import_spaces = scene_bim.import_should_import_spaces
|
||||
settings.should_auto_set_workarounds = scene_bim.import_should_auto_set_workarounds
|
||||
settings.should_use_cpu_multiprocessing = scene_bim.import_should_use_cpu_multiprocessing
|
||||
settings.should_import_with_profiling = scene_bim.import_should_import_with_profiling
|
||||
settings.should_import_native = scene_bim.import_should_import_native
|
||||
settings.should_roundtrip_native = scene_bim.import_export_should_roundtrip_native
|
||||
settings.should_import_aggregates = scene_bim.import_should_import_aggregates
|
||||
settings.should_merge_aggregates = scene_bim.import_should_merge_aggregates
|
||||
settings.should_merge_by_class = scene_bim.import_should_merge_by_class
|
||||
settings.should_merge_by_material = scene_bim.import_should_merge_by_material
|
||||
settings.should_merge_materials_by_colour = scene_bim.import_should_merge_materials_by_colour
|
||||
settings.should_clean_mesh = scene_bim.import_should_clean_mesh
|
||||
settings.should_allow_non_element_aggregates = scene_bim.import_should_allow_non_element_aggregates
|
||||
settings.should_offset_model = scene_bim.import_should_offset_model
|
||||
settings.model_offset_coordinates = (
|
||||
[float(o) for o in scene_bim.import_model_offset_coordinates.split(",")]
|
||||
if scene_bim.import_model_offset_coordinates
|
||||
else (0, 0, 0)
|
||||
)
|
||||
settings.deflection_tolerance = scene_bim.import_deflection_tolerance
|
||||
settings.angular_tolerance = scene_bim.import_angular_tolerance
|
||||
settings.diff_file = scene_diff.diff_json_file
|
||||
return settings
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.module.aggregate.data import Data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_PT_aggregate(Panel):
|
||||
bl_label = "IFC Aggregation"
|
||||
bl_label = "IFC Aggregates"
|
||||
bl_idname = "BIM_PT_aggregate"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
@@ -14,6 +16,8 @@ class BIM_PT_aggregate(Panel):
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||
return False
|
||||
if props.ifc_definition_id not in Data.products:
|
||||
Data.load(props.ifc_definition_id)
|
||||
if not Data.products[props.ifc_definition_id]:
|
||||
@@ -38,7 +42,7 @@ class BIM_PT_aggregate(Panel):
|
||||
Data.products[props.ifc_definition_id]["type"], Data.products[props.ifc_definition_id]["Name"]
|
||||
)
|
||||
if name == "None/None":
|
||||
name = "This object is not part of an aggregation"
|
||||
name = "No Aggregate Found"
|
||||
row.label(text=name)
|
||||
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
|
||||
row.operator("bim.add_aggregate", icon="ADD", text="")
|
||||
|
||||
@@ -5,6 +5,7 @@ classes = (
|
||||
operator.EnableEditingAttributes,
|
||||
operator.DisableEditingAttributes,
|
||||
operator.EditAttributes,
|
||||
operator.GenerateGlobalId,
|
||||
prop.BIMAttributeProperties,
|
||||
ui.BIM_PT_object_attributes,
|
||||
ui.BIM_PT_material_attributes,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import blenderbim.bim.module.attribute.edit_attributes as edit_attributes
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.attribute.data import Data
|
||||
@@ -100,3 +101,19 @@ class EditAttributes(bpy.types.Operator):
|
||||
Data.load(oprops.ifc_definition_id)
|
||||
bpy.ops.bim.disable_editing_attributes(obj=self.obj, obj_type=self.obj_type)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GenerateGlobalId(bpy.types.Operator):
|
||||
bl_idname = "bim.generate_global_id"
|
||||
bl_label = "Regenerate GlobalId"
|
||||
|
||||
def execute(self, context):
|
||||
index = bpy.context.active_object.BIMAttributeProperties.attributes.find("GlobalId")
|
||||
if index >= 0:
|
||||
global_id = bpy.context.active_object.BIMAttributeProperties.attributes[index]
|
||||
else:
|
||||
global_id = bpy.context.active_object.BIMAttributeProperties.attributes.add()
|
||||
global_id.name = "GlobalId"
|
||||
global_id.data_type = "string"
|
||||
global_id.string_value = ifcopenshell.guid.new()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -2,6 +2,7 @@ import os
|
||||
import bpy
|
||||
import bcf
|
||||
from . import bcfstore
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from math import radians, degrees, atan, tan, cos, sin
|
||||
from mathutils import Vector, Matrix, Euler, geometry
|
||||
|
||||
@@ -624,6 +625,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
bl_label = "Activate BCF Viewpoint"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
props = bpy.context.scene.BCFProperties
|
||||
blender_topic = props.topics[props.active_topic_index]
|
||||
@@ -713,10 +715,9 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
global_id_colours.setdefault(component.ifc_guid, coloring.color)
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
|
||||
if not global_id:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
global_id = global_id.string_value
|
||||
global_id = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId
|
||||
is_visible = viewpoint.components.visibility.default_visibility
|
||||
if global_id in exception_global_ids:
|
||||
is_visible = not is_visible
|
||||
@@ -724,9 +725,15 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
obj.hide_set(True)
|
||||
continue
|
||||
if "IfcSpace" in obj.name:
|
||||
is_visible = viewpoint.components.viewSetuphints.spacesVisible
|
||||
if viewpoint.components.view_setup_hints:
|
||||
is_visible = viewpoint.components.view_setup_hints.spaces_visible
|
||||
else:
|
||||
is_visible = False
|
||||
elif "IfcOpeningElement" in obj.name:
|
||||
is_visible = viewpoint.components.viewSetuphints.openingsVisible
|
||||
if viewpoint.components.view_setup_hints:
|
||||
is_visible = viewpoint.components.view_setup_hints.openings_visible
|
||||
else:
|
||||
is_visible = False
|
||||
obj.hide_set(not is_visible)
|
||||
if not is_visible:
|
||||
continue
|
||||
|
||||
@@ -4,7 +4,7 @@ from . import bcfstore
|
||||
from bpy.types import Panel
|
||||
|
||||
class BIM_PT_bcf(Panel):
|
||||
bl_label = "BIM Collaboration Format (BCF)"
|
||||
bl_label = "BCF Project"
|
||||
bl_idname = "BIM_PT_bcf"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
|
||||
@@ -4,10 +4,8 @@ from . import ui, prop, operator
|
||||
classes = (
|
||||
operator.ExecuteBIMTester,
|
||||
operator.BIMTesterPurge,
|
||||
operator.SelectFeaturesDir,
|
||||
operator.RejectElement,
|
||||
operator.ColourByClass,
|
||||
operator.ResetObjectColours,
|
||||
operator.SelectFeaturesDir,
|
||||
operator.RejectElement,
|
||||
operator.ApproveClass,
|
||||
operator.RejectClass,
|
||||
operator.SelectAudited,
|
||||
@@ -22,4 +20,3 @@ def register():
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BimTesterProperties
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import bimtester
|
||||
import bimtester.run
|
||||
import bimtester.reports
|
||||
import os
|
||||
import webbrowser
|
||||
from pathlib import Path
|
||||
from itertools import cycle
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class ExecuteBIMTester(bpy.types.Operator):
|
||||
@@ -14,11 +17,21 @@ class ExecuteBIMTester(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
|
||||
filename = os.path.join(
|
||||
bpy.context.scene.BimTesterProperties.features_dir, bpy.context.scene.BimTesterProperties.features_file + ".feature"
|
||||
bpy.context.scene.BimTesterProperties.features_dir,
|
||||
bpy.context.scene.BimTesterProperties.features_file + ".feature",
|
||||
)
|
||||
cwd = os.getcwd()
|
||||
os.chdir(bpy.context.scene.BimTesterProperties.features_dir)
|
||||
bimtester.run.run_tests({"feature": filename, "advanced_arguments": None, "console": False})
|
||||
bimtester.run.run_tests({
|
||||
"advanced_arguments": "",
|
||||
"console": False,
|
||||
"featuresdir": "",
|
||||
"feature": filename,
|
||||
"gui": False,
|
||||
"ifcfile": "",
|
||||
"purge": False,
|
||||
"path": "",
|
||||
})
|
||||
bimtester.reports.generate_report()
|
||||
webbrowser.open(
|
||||
"file://"
|
||||
@@ -31,6 +44,7 @@ class ExecuteBIMTester(bpy.types.Operator):
|
||||
os.chdir(cwd)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class BIMTesterPurge(bpy.types.Operator):
|
||||
bl_idname = "bim.bim_tester_purge"
|
||||
bl_label = "Purge Tests"
|
||||
@@ -38,7 +52,8 @@ class BIMTesterPurge(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
|
||||
filename = os.path.join(
|
||||
bpy.context.scene.BimTesterProperties.features_dir, bpy.context.scene.BimTesterProperties.features_file + ".feature"
|
||||
bpy.context.scene.BimTesterProperties.features_dir,
|
||||
bpy.context.scene.BimTesterProperties.features_file + ".feature",
|
||||
)
|
||||
cwd = os.getcwd()
|
||||
os.chdir(bpy.context.scene.BimTesterProperties.features_dir)
|
||||
@@ -46,6 +61,7 @@ class BIMTesterPurge(bpy.types.Operator):
|
||||
os.chdir(cwd)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectFeaturesDir(bpy.types.Operator):
|
||||
bl_idname = "bim.select_features_dir"
|
||||
bl_label = "Select Features Directory"
|
||||
@@ -61,50 +77,24 @@ class SelectFeaturesDir(bpy.types.Operator):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class RejectElement(bpy.types.Operator):
|
||||
bl_idname = "bim.reject_element"
|
||||
bl_label = "Reject Element"
|
||||
|
||||
def execute(self, context):
|
||||
lines = []
|
||||
for object in bpy.context.selected_objects:
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in bpy.context.selected_objects:
|
||||
lines.append(
|
||||
" * The element {} should not exist because {}".format(
|
||||
object.BIMObjectProperties.attributes[
|
||||
object.BIMObjectProperties.attributes.find("GlobalId")
|
||||
].string_value,
|
||||
self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId,
|
||||
bpy.context.scene.BimTesterProperties.qa_reject_element_reason,
|
||||
)
|
||||
)
|
||||
QAHelper.append_to_scenario(lines)
|
||||
return {"FINISHED"}
|
||||
|
||||
class ColourByClass(bpy.types.Operator):
|
||||
bl_idname = "bim.colour_by_class"
|
||||
bl_label = "Colour by Class"
|
||||
|
||||
def execute(self, context):
|
||||
colours = cycle(colour_list)
|
||||
ifc_classes = {}
|
||||
for obj in bpy.context.visible_objects:
|
||||
if "/" not in obj.name:
|
||||
continue
|
||||
ifc_class = obj.name.split("/")[0]
|
||||
if ifc_class not in ifc_classes:
|
||||
ifc_classes[ifc_class] = next(colours)
|
||||
obj.color = ifc_classes[ifc_class]
|
||||
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
||||
area.spaces[0].shading.color_type = "OBJECT"
|
||||
return {"FINISHED"}
|
||||
|
||||
class ResetObjectColours(bpy.types.Operator):
|
||||
bl_idname = "bim.reset_object_colours"
|
||||
bl_label = "Reset Colours"
|
||||
|
||||
def execute(self, context):
|
||||
for object in bpy.context.selected_objects:
|
||||
object.color = (1, 1, 1, 1)
|
||||
return {"FINISHED"}
|
||||
|
||||
class ApproveClass(bpy.types.Operator):
|
||||
bl_idname = "bim.approve_class"
|
||||
@@ -112,41 +102,43 @@ class ApproveClass(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
lines = []
|
||||
for object in bpy.context.selected_objects:
|
||||
index = object.BIMObjectProperties.attributes.find("GlobalId")
|
||||
if index != -1:
|
||||
lines.append(
|
||||
" * The element {} is an {}".format(
|
||||
object.BIMObjectProperties.attributes[index].string_value, object.name.split("/")[0]
|
||||
)
|
||||
)
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in bpy.context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
lines.append(" * The element {} is an {}".format(element.GlobalId, element.is_a()))
|
||||
QAHelper.append_to_scenario(lines)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RejectClass(bpy.types.Operator):
|
||||
bl_idname = "bim.reject_class"
|
||||
bl_label = "Reject Class"
|
||||
|
||||
def execute(self, context):
|
||||
lines = []
|
||||
for object in bpy.context.selected_objects:
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in bpy.context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
lines.append(
|
||||
" * The element {} is an {}".format(
|
||||
object.BIMObjectProperties.attributes[
|
||||
object.BIMObjectProperties.attributes.find("GlobalId")
|
||||
].string_value,
|
||||
self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId,
|
||||
bpy.context.scene.BimTesterProperties.audit_ifc_class,
|
||||
)
|
||||
)
|
||||
QAHelper.append_to_scenario(lines)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectAudited(bpy.types.Operator):
|
||||
bl_idname = "bim.select_audited"
|
||||
bl_label = "Select Audited"
|
||||
|
||||
def execute(self, context):
|
||||
audited_global_ids = []
|
||||
self.file = IfcStore.get_file()
|
||||
for filename in Path(bpy.context.scene.BimTesterProperties.features_dir).glob("*.feature"):
|
||||
with open(filename, "r") as feature_file:
|
||||
lines = feature_file.readlines()
|
||||
@@ -155,20 +147,23 @@ class SelectAudited(bpy.types.Operator):
|
||||
for word in words:
|
||||
if self.is_a_global_id(word):
|
||||
audited_global_ids.append(word)
|
||||
for object in bpy.context.visible_objects:
|
||||
index = object.BIMObjectProperties.attributes.find("GlobalId")
|
||||
if index != -1 and object.BIMObjectProperties.attributes[index].string_value in audited_global_ids:
|
||||
object.select_set(True)
|
||||
for obj in bpy.context.visible_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
if self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId in audited_global_ids:
|
||||
obj.select_set(True)
|
||||
return {"FINISHED"}
|
||||
|
||||
def is_a_global_id(self, word):
|
||||
return word[0] in ["0", "1", "2", "3"] and len(word) == 22
|
||||
|
||||
|
||||
class QAHelper:
|
||||
@classmethod
|
||||
def append_to_scenario(cls, lines):
|
||||
filename = os.path.join(
|
||||
bpy.context.scene.BimTesterProperties.features_dir, bpy.context.scene.BimTesterProperties.features_file + ".feature"
|
||||
bpy.context.scene.BimTesterProperties.features_dir,
|
||||
bpy.context.scene.BimTesterProperties.features_file + ".feature",
|
||||
)
|
||||
if os.path.exists(filename + "~"):
|
||||
os.remove(filename + "~")
|
||||
@@ -189,6 +184,7 @@ class QAHelper:
|
||||
destination.write(source_line)
|
||||
os.remove(filename + "~")
|
||||
|
||||
|
||||
colour_list = [
|
||||
(0.651, 0.81, 0.892, 1),
|
||||
(0.121, 0.471, 0.706, 1),
|
||||
|
||||
@@ -3,7 +3,7 @@ from bpy.types import Panel
|
||||
|
||||
|
||||
class BIM_PT_qa(Panel):
|
||||
bl_label = "BIMTester Quality Auditing"
|
||||
bl_label = "BIMTester"
|
||||
bl_idname = "BIM_PT_qa"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
@@ -46,10 +46,6 @@ class BIM_PT_qa(Panel):
|
||||
row = layout.row()
|
||||
row.operator("bim.reject_element")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.colour_by_class")
|
||||
row.operator("bim.reset_object_colours")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(bimtester_properties, "audit_ifc_class")
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.ExportClashSets,
|
||||
operator.ImportClashSets,
|
||||
operator.AddClashSet,
|
||||
operator.RemoveClashSet,
|
||||
operator.AddClashSource,
|
||||
operator.RemoveClashSource,
|
||||
operator.SelectClashSource,
|
||||
operator.ExecuteIfcClash,
|
||||
operator.SelectIfcClashResults,
|
||||
operator.SelectClashResults,
|
||||
operator.SelectSmartGroupedClashesPath,
|
||||
operator.SmartClashGroup,
|
||||
operator.SelectSmartGroup,
|
||||
operator.LoadSmartGroupsForActiveClashSet,
|
||||
operator.SetBlenderClashSetA,
|
||||
operator.SetBlenderClashSetB,
|
||||
operator.ExecuteBlenderClash,
|
||||
prop.ClashSource,
|
||||
prop.ClashSet,
|
||||
prop.SmartClashGroup,
|
||||
prop.BIMClashProperties,
|
||||
ui.BIM_PT_ifcclash,
|
||||
ui.BIM_PT_clash_manager,
|
||||
ui.BIM_UL_clash_sets,
|
||||
ui.BIM_UL_smart_groups,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMClashProperties = bpy.props.PointerProperty(type=prop.BIMClashProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMClashProperties
|
||||
@@ -0,0 +1,456 @@
|
||||
import os
|
||||
import bpy
|
||||
import json
|
||||
import bmesh
|
||||
import logging
|
||||
import numpy as np
|
||||
from mathutils import Matrix
|
||||
from math import radians
|
||||
|
||||
|
||||
class ExportClashSets(bpy.types.Operator):
|
||||
bl_idname = "bim.export_clash_sets"
|
||||
bl_label = "Export Clash Sets"
|
||||
filename_ext = ".json"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
|
||||
WindowManager = context.window_manager
|
||||
WindowManager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def execute(self, context):
|
||||
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
||||
clash_sets = []
|
||||
for clash_set in bpy.context.scene.BIMClashProperties.clash_sets:
|
||||
self.a = []
|
||||
self.b = []
|
||||
for ab in ["a", "b"]:
|
||||
for data in getattr(clash_set, ab):
|
||||
clash_source = {"file": data.name}
|
||||
if data.selector:
|
||||
clash_source["selector"] = data.selector
|
||||
clash_source["mode"] = data.mode
|
||||
getattr(self, ab).append(clash_source)
|
||||
clash_sets.append({"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a, "b": self.b})
|
||||
with open(self.filepath, "w") as destination:
|
||||
destination.write(json.dumps(clash_sets, indent=4))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ImportClashSets(bpy.types.Operator):
|
||||
bl_idname = "bim.import_clash_sets"
|
||||
bl_label = "Import Clash Sets"
|
||||
filename_ext = ".json"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
|
||||
WindowManager = context.window_manager
|
||||
WindowManager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def execute(self, context):
|
||||
with open(self.filepath) as f:
|
||||
clash_sets = json.load(f)
|
||||
for clash_set in clash_sets:
|
||||
new = bpy.context.scene.BIMClashProperties.clash_sets.add()
|
||||
new.name = clash_set["name"]
|
||||
new.tolerance = clash_set["tolerance"]
|
||||
for clash_source in clash_set["a"]:
|
||||
new_source = new.a.add()
|
||||
new_source.name = clash_source["file"]
|
||||
if "selector" in clash_source:
|
||||
new_source.selector = clash_source["selector"]
|
||||
new_source.mode = clash_source["mode"]
|
||||
if clash_set["b"]:
|
||||
for clash_source in clash_set["b"]:
|
||||
new_source = new.b.add()
|
||||
new_source.name = clash_source["file"]
|
||||
if "selector" in clash_source:
|
||||
new_source.selector = clash_source["selector"]
|
||||
new_source.mode = clash_source["mode"]
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddClashSet(bpy.types.Operator):
|
||||
bl_idname = "bim.add_clash_set"
|
||||
bl_label = "Add Clash Set"
|
||||
|
||||
def execute(self, context):
|
||||
new = bpy.context.scene.BIMClashProperties.clash_sets.add()
|
||||
new.name = "New Clash Set"
|
||||
new.tolerance = 0.01
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveClashSet(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_clash_set"
|
||||
bl_label = "Remove Clash Set"
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
bpy.context.scene.BIMClashProperties.clash_sets.remove(self.index)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddClashSource(bpy.types.Operator):
|
||||
bl_idname = "bim.add_clash_source"
|
||||
bl_label = "Add Clash Source"
|
||||
group: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
clash_set = bpy.context.scene.BIMClashProperties.clash_sets[bpy.context.scene.BIMClashProperties.active_clash_set_index]
|
||||
source = getattr(clash_set, self.group).add()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveClashSource(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_clash_source"
|
||||
bl_label = "Remove Clash Source"
|
||||
index: bpy.props.IntProperty()
|
||||
group: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
clash_set = bpy.context.scene.BIMClashProperties.clash_sets[bpy.context.scene.BIMClashProperties.active_clash_set_index]
|
||||
getattr(clash_set, self.group).remove(self.index)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectClashSource(bpy.types.Operator):
|
||||
bl_idname = "bim.select_clash_source"
|
||||
bl_label = "Select Clash Source"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
index: bpy.props.IntProperty()
|
||||
group: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
clash_set = bpy.context.scene.BIMClashProperties.clash_sets[bpy.context.scene.BIMClashProperties.active_clash_set_index]
|
||||
getattr(clash_set, self.group)[self.index].name = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class SelectClashResults(bpy.types.Operator):
|
||||
bl_idname = "bim.select_clash_results"
|
||||
bl_label = "Select Clash Results"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
bpy.context.scene.BIMClashProperties.clash_results_path = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class SelectSmartGroupedClashesPath(bpy.types.Operator):
|
||||
bl_idname = "bim.select_smart_grouped_clashes_path"
|
||||
bl_label = "Select Smart-Grouped Clashes Path"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
bpy.context.scene.BIMClashProperties.smart_grouped_clashes_path = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class ExecuteIfcClash(bpy.types.Operator):
|
||||
bl_idname = "bim.execute_ifc_clash"
|
||||
bl_label = "Execute IFC Clash"
|
||||
filename_ext = ".bcf"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def invoke(self, context, event):
|
||||
if ".json" not in bpy.data.filepath:
|
||||
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".bcf")
|
||||
WindowManager = context.window_manager
|
||||
WindowManager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def execute(self, context):
|
||||
import ifcclash
|
||||
|
||||
settings = ifcclash.IfcClashSettings()
|
||||
if ".json" not in self.filepath:
|
||||
self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf")
|
||||
settings.output = self.filepath
|
||||
settings.logger = logging.getLogger("Clash")
|
||||
settings.logger.setLevel(logging.DEBUG)
|
||||
ifc_clasher = ifcclash.IfcClasher(settings)
|
||||
|
||||
if bpy.context.scene.BIMClashProperties.should_create_clash_snapshots:
|
||||
|
||||
def get_viewpoint_snapshot(self, viewpoint, mat):
|
||||
camera = bpy.data.objects.get("IFC Clash Camera")
|
||||
if not camera:
|
||||
camera = bpy.data.objects.new("IFC Clash Camera", bpy.data.cameras.new("IFC Clash Camera"))
|
||||
bpy.context.scene.collection.objects.link(camera)
|
||||
camera.matrix_world = Matrix(mat)
|
||||
bpy.context.scene.camera = camera
|
||||
camera.data.angle = radians(60)
|
||||
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
||||
area.spaces[0].region_3d.view_perspective = "CAMERA"
|
||||
area.spaces[0].shading.show_xray = True
|
||||
bpy.context.scene.render.resolution_x = 480
|
||||
bpy.context.scene.render.resolution_y = 270
|
||||
bpy.context.scene.render.image_settings.file_format = "PNG"
|
||||
bpy.context.scene.render.filepath = os.path.join(
|
||||
bpy.context.scene.BIMProperties.data_dir, "snapshot.png"
|
||||
)
|
||||
bpy.ops.render.opengl(write_still=True)
|
||||
return bpy.context.scene.render.filepath
|
||||
|
||||
ifcclash.IfcClasher.get_viewpoint_snapshot = get_viewpoint_snapshot
|
||||
|
||||
ifc_clasher.clash_sets = []
|
||||
for clash_set in bpy.context.scene.BIMClashProperties.clash_sets:
|
||||
self.a = []
|
||||
self.b = []
|
||||
for ab in ["a", "b"]:
|
||||
for data in getattr(clash_set, ab):
|
||||
clash_source = {"file": data.name}
|
||||
if data.selector:
|
||||
clash_source["selector"] = data.selector
|
||||
clash_source["mode"] = data.mode
|
||||
getattr(self, ab).append(clash_source)
|
||||
ifc_clasher.clash_sets.append(
|
||||
{"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a, "b": self.b}
|
||||
)
|
||||
ifc_clasher.clash()
|
||||
ifc_clasher.export()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectIfcClashResults(bpy.types.Operator):
|
||||
bl_idname = "bim.select_ifc_clash_results"
|
||||
bl_label = "Select IFC Clash Results"
|
||||
filename_ext = ".json"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
|
||||
WindowManager = context.window_manager
|
||||
WindowManager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def execute(self, context):
|
||||
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
||||
with open(self.filepath) as f:
|
||||
clash_sets = json.load(f)
|
||||
clash_set_name = bpy.context.scene.BIMClashProperties.clash_sets[
|
||||
bpy.context.scene.BIMClashProperties.active_clash_set_index
|
||||
].name
|
||||
global_ids = []
|
||||
for clash_set in clash_sets:
|
||||
if clash_set["name"] != clash_set_name:
|
||||
continue
|
||||
if not "clashes" in clash_set.keys():
|
||||
self.report({"WARNING"}, "No clashes found for the selected Clash Set.")
|
||||
return {"CANCELLED"}
|
||||
for clash in clash_set["clashes"].values():
|
||||
global_ids.extend([clash["a_global_id"], clash["b_global_id"]])
|
||||
for obj in bpy.context.visible_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
if element.GlobalId in global_ids:
|
||||
obj.select_set(True)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SmartClashGroup(bpy.types.Operator):
|
||||
bl_idname = "bim.smart_clash_group"
|
||||
bl_label = "Smart Group Clashes"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
import ifcclash
|
||||
|
||||
settings = ifcclash.IfcClashSettings()
|
||||
self.filepath = bpy.path.ensure_ext(bpy.context.scene.BIMClashProperties.clash_results_path, ".json")
|
||||
settings.output = self.filepath
|
||||
settings.logger = logging.getLogger("Clash")
|
||||
settings.logger.setLevel(logging.DEBUG)
|
||||
ifc_clasher = ifcclash.IfcClasher(settings)
|
||||
|
||||
with open(self.filepath) as f:
|
||||
clash_sets = json.load(f)
|
||||
|
||||
# execute the smart grouping
|
||||
save_path = bpy.path.ensure_ext(bpy.context.scene.BIMClashProperties.smart_grouped_clashes_path, ".json")
|
||||
smart_grouped_clashes = ifc_clasher.smart_group_clashes(
|
||||
clash_sets, bpy.context.scene.BIMClashProperties.smart_clash_grouping_max_distance
|
||||
)
|
||||
|
||||
# save smart_groups to json
|
||||
with open(save_path, "w") as f:
|
||||
f.write(json.dumps(smart_grouped_clashes))
|
||||
|
||||
clash_set_name = bpy.context.scene.BIMClashProperties.clash_sets[
|
||||
bpy.context.scene.BIMClashProperties.active_clash_set_index
|
||||
].name
|
||||
|
||||
# Reset the list of smart_clash_groups for the UI
|
||||
bpy.context.scene.BIMClashProperties.smart_clash_groups.clear()
|
||||
|
||||
for clash_set, smart_groups in smart_grouped_clashes.items():
|
||||
# Only select the clashes that correspond to the actively selected IFC Clash Set
|
||||
if clash_set != clash_set_name:
|
||||
continue
|
||||
else:
|
||||
for smart_group, global_id_pairs in smart_groups[0].items():
|
||||
new_group = bpy.context.scene.BIMClashProperties.smart_clash_groups.add()
|
||||
new_group.number = f"{smart_group}"
|
||||
|
||||
for pair in global_id_pairs:
|
||||
for id in pair:
|
||||
new_global_id = new_group.global_ids.add()
|
||||
new_global_id.name = id
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
|
||||
bl_idname = "bim.load_smart_groups_for_active_clash_set"
|
||||
bl_label = "Load Smart Groups for Active Clash Set"
|
||||
|
||||
def execute(self, context):
|
||||
smart_groups_path = bpy.path.ensure_ext(bpy.context.scene.BIMClashProperties.smart_grouped_clashes_path, ".json")
|
||||
|
||||
clash_set_name = bpy.context.scene.BIMClashProperties.clash_sets[
|
||||
bpy.context.scene.BIMClashProperties.active_clash_set_index
|
||||
].name
|
||||
|
||||
with open(smart_groups_path) as f:
|
||||
smart_grouped_clashes = json.load(f)
|
||||
|
||||
# Reset the list of smart_clash_groups for the UI
|
||||
bpy.context.scene.BIMClashProperties.smart_clash_groups.clear()
|
||||
|
||||
for clash_set, smart_groups in smart_grouped_clashes.items():
|
||||
# Only select the clashes that correspond to the actively selected IFC Clash Set
|
||||
if clash_set != clash_set_name:
|
||||
continue
|
||||
else:
|
||||
for smart_group, global_id_pairs in smart_groups[0].items():
|
||||
new_group = bpy.context.scene.BIMClashProperties.smart_clash_groups.add()
|
||||
new_group.number = f"{smart_group}"
|
||||
for pair in global_id_pairs:
|
||||
for id in pair:
|
||||
new_global_id = new_group.global_ids.add()
|
||||
new_global_id.name = id
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectSmartGroup(bpy.types.Operator):
|
||||
bl_idname = "bim.select_smart_group"
|
||||
bl_label = "Select Smart Group"
|
||||
|
||||
def execute(self, context):
|
||||
# Select smart group in view
|
||||
selected_smart_group = bpy.context.scene.BIMClashProperties.smart_clash_groups[
|
||||
bpy.context.scene.BIMCLashProperties.active_smart_group_index
|
||||
]
|
||||
# print(selected_smart_group.number)
|
||||
|
||||
for obj in bpy.context.visible_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
for id in selected_smart_group.global_ids:
|
||||
# print("Id: ", id)
|
||||
# print("Global id: ", element.GlobalId)
|
||||
if element.GlobalId in id.name:
|
||||
# print("object match: ", global_id)
|
||||
obj.select_set(True)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class BlenderClasher:
|
||||
def process_clash_set(self):
|
||||
import collision
|
||||
|
||||
a_cm = collision.CollisionManager()
|
||||
b_cm = collision.CollisionManager()
|
||||
self.add_to_cm(a_cm, bpy.context.scene.BIMClashProperties.blender_clash_set_a)
|
||||
self.add_to_cm(b_cm, bpy.context.scene.BIMClashProperties.blender_clash_set_b)
|
||||
results = a_cm.in_collision_other(b_cm, return_data=True)
|
||||
if not results[0]:
|
||||
print("No clashes")
|
||||
return
|
||||
for contact in results[1]:
|
||||
if contact.raw.penetration_depth < 0.01:
|
||||
continue
|
||||
print("-----")
|
||||
print(contact.names)
|
||||
print(contact.raw.normal)
|
||||
print(contact.raw.pos)
|
||||
|
||||
def add_to_cm(self, cm, object_names):
|
||||
import ifcclash
|
||||
|
||||
for object_name in object_names:
|
||||
name = object_name.name
|
||||
obj = bpy.data.objects[name]
|
||||
triangulated_mesh = self.triangulate_mesh(obj)
|
||||
mesh = ifcclash.Mesh()
|
||||
mesh.vertices = np.array([tuple(obj.matrix_world @ v.co) for v in triangulated_mesh.vertices])
|
||||
mesh.faces = np.array([tuple(p.vertices) for p in triangulated_mesh.polygons])
|
||||
cm.add_object(name, mesh)
|
||||
|
||||
def triangulate_mesh(self, obj):
|
||||
mesh = obj.evaluated_get(bpy.context.evaluated_depsgraph_get()).to_mesh()
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
del bm
|
||||
return mesh
|
||||
|
||||
|
||||
class SetBlenderClashSetA(bpy.types.Operator):
|
||||
bl_idname = "bim.set_blender_clash_set_a"
|
||||
bl_label = "Set Blender Clash Set A"
|
||||
|
||||
def execute(self, context):
|
||||
while len(bpy.context.scene.BIMClashProperties.blender_clash_set_a) > 0:
|
||||
bpy.context.scene.BIMClashProperties.blender_clash_set_a.remove(0)
|
||||
for obj in bpy.context.selected_objects:
|
||||
new = bpy.context.scene.BIMClashProperties.blender_clash_set_a.add()
|
||||
new.name = obj.name
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SetBlenderClashSetB(bpy.types.Operator):
|
||||
bl_idname = "bim.set_blender_clash_set_b"
|
||||
bl_label = "Set Blender Clash Set B"
|
||||
|
||||
def execute(self, context):
|
||||
while len(bpy.context.scene.BIMClashProperties.blender_clash_set_b) > 0:
|
||||
bpy.context.scene.BIMClashProperties.blender_clash_set_b.remove(0)
|
||||
for obj in bpy.context.selected_objects:
|
||||
new = bpy.context.scene.BIMClashProperties.blender_clash_set_b.add()
|
||||
new.name = obj.name
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ExecuteBlenderClash(bpy.types.Operator):
|
||||
bl_idname = "bim.execute_blender_clash"
|
||||
bl_label = "Execute Blender Clash"
|
||||
|
||||
def execute(self, context):
|
||||
blender_clasher = BlenderClasher()
|
||||
blender_clasher.process_clash_set()
|
||||
return {"FINISHED"}
|
||||
@@ -0,0 +1,53 @@
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.module.owner.data import Data
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
class ClashSource(PropertyGroup):
|
||||
name: StringProperty(name="File")
|
||||
selector: StringProperty(name="Selector")
|
||||
mode: EnumProperty(
|
||||
items=[
|
||||
("i", "Include", "Only the selected objects are included for clashing"),
|
||||
("e", "Exclude", "All objects except the selected objects are included for clashing"),
|
||||
],
|
||||
name="Mode",
|
||||
)
|
||||
|
||||
|
||||
class ClashSet(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
tolerance: FloatProperty(name="Tolerance")
|
||||
a: CollectionProperty(name="Group A", type=ClashSource)
|
||||
b: CollectionProperty(name="Group B", type=ClashSource)
|
||||
|
||||
|
||||
class SmartClashGroup(PropertyGroup):
|
||||
number: StringProperty(name="Number")
|
||||
global_ids: CollectionProperty(name="GlobalIDs", type=StrProperty)
|
||||
|
||||
|
||||
class BIMClashProperties(PropertyGroup):
|
||||
blender_clash_set_a: CollectionProperty(name="Blender Clash Set A", type=StrProperty)
|
||||
blender_clash_set_b: CollectionProperty(name="Blender Clash Set B", type=StrProperty)
|
||||
clash_sets: CollectionProperty(name="Clash Sets", type=ClashSet)
|
||||
should_create_clash_snapshots: BoolProperty(name="Create Snapshots", default=True)
|
||||
clash_results_path: StringProperty(name="Clash Results Path")
|
||||
smart_grouped_clashes_path: StringProperty(name="Smart Grouped Clashes Path")
|
||||
active_clash_set_index: IntProperty(name="Active Clash Set Index")
|
||||
smart_clash_groups: CollectionProperty(name="Smart Clash Groups", type=SmartClashGroup)
|
||||
active_smart_group_index: IntProperty(name="Active Smart Group Index")
|
||||
smart_clash_grouping_max_distance: IntProperty(
|
||||
name="Smart Clash Grouping Max Distance", default=3, soft_min=1, soft_max=10
|
||||
)
|
||||
@@ -0,0 +1,148 @@
|
||||
import bpy
|
||||
from bpy.types import Panel
|
||||
|
||||
|
||||
class BIM_PT_ifcclash(Panel):
|
||||
bl_label = "IFC Clash Sets"
|
||||
bl_idname = "BIM_PT_ifcclash"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
props = scene.BIMClashProperties
|
||||
|
||||
layout.label(text="Blender Clash:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.set_blender_clash_set_a")
|
||||
row.operator("bim.set_blender_clash_set_b")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.execute_blender_clash")
|
||||
|
||||
layout.label(text="IFC Clash:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.add_clash_set")
|
||||
row.operator("bim.import_clash_sets", text="", icon="IMPORT")
|
||||
row.operator("bim.export_clash_sets", text="", icon="EXPORT")
|
||||
|
||||
if not props.clash_sets:
|
||||
return
|
||||
|
||||
layout.template_list("BIM_UL_clash_sets", "", props, "clash_sets", props, "active_clash_set_index")
|
||||
|
||||
if props.active_clash_set_index < len(props.clash_sets):
|
||||
clash_set = props.clash_sets[props.active_clash_set_index]
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(clash_set, "name")
|
||||
row.operator("bim.remove_clash_set", icon="X", text="").index = props.active_clash_set_index
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(clash_set, "tolerance")
|
||||
|
||||
layout.label(text="Group A:")
|
||||
row = layout.row()
|
||||
row.operator("bim.add_clash_source").group = "a"
|
||||
|
||||
for index, source in enumerate(clash_set.a):
|
||||
row = layout.row(align=True)
|
||||
row.prop(source, "name", text="")
|
||||
op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="")
|
||||
op.index = index
|
||||
op.group = "a"
|
||||
op = row.operator("bim.remove_clash_source", icon="X", text="")
|
||||
op.index = index
|
||||
op.group = "a"
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(source, "mode", text="")
|
||||
row.prop(source, "selector", text="")
|
||||
|
||||
layout.label(text="Group B:")
|
||||
row = layout.row()
|
||||
row.operator("bim.add_clash_source").group = "b"
|
||||
|
||||
for index, source in enumerate(clash_set.b):
|
||||
row = layout.row(align=True)
|
||||
row.prop(source, "name", text="")
|
||||
op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="")
|
||||
op.index = index
|
||||
op.group = "b"
|
||||
op = row.operator("bim.remove_clash_source", icon="X", text="")
|
||||
op.index = index
|
||||
op.group = "b"
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(source, "mode", text="")
|
||||
row.prop(source, "selector", text="")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "should_create_clash_snapshots")
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.execute_ifc_clash")
|
||||
row.operator("bim.select_ifc_clash_results")
|
||||
|
||||
|
||||
class BIM_PT_clash_manager(Panel):
|
||||
bl_idname = "BIM_PT_clash_manager"
|
||||
bl_label = "Clash Manager"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "BlenderBIM"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.BIMClashProperties
|
||||
|
||||
row = layout.row()
|
||||
layout.label(text="Select clash results to group:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "clash_results_path", text="")
|
||||
op = row.operator("bim.select_clash_results", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row()
|
||||
layout.label(text="Select output path for smart-grouped clashes:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "smart_grouped_clashes_path", text="")
|
||||
op = row.operator("bim.select_smart_grouped_clashes_path", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "smart_clash_grouping_max_distance")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.smart_clash_group")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.load_smart_groups_for_active_clash_set")
|
||||
|
||||
layout.template_list("BIM_UL_smart_groups", "", props, "smart_clash_groups", props, "active_smart_group_index")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.select_smart_group")
|
||||
|
||||
|
||||
class BIM_UL_clash_sets(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
ob = data
|
||||
if item:
|
||||
layout.prop(item, "name", text="", emboss=False)
|
||||
else:
|
||||
layout.label(text="", translate=False)
|
||||
|
||||
|
||||
class BIM_UL_smart_groups(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
ob = data
|
||||
if item:
|
||||
layout.label(text=str(item.number), translate=False, icon="NONE", icon_value=0)
|
||||
else:
|
||||
layout.label(text="", translate=False)
|
||||
@@ -0,0 +1,35 @@
|
||||
import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.LoadClassificationLibrary,
|
||||
operator.AddClassification,
|
||||
operator.RemoveClassification,
|
||||
operator.EnableEditingClassification,
|
||||
operator.DisableEditingClassification,
|
||||
operator.EditClassification,
|
||||
operator.RemoveClassificationReference,
|
||||
operator.EnableEditingClassificationReference,
|
||||
operator.DisableEditingClassificationReference,
|
||||
operator.EditClassificationReference,
|
||||
operator.AddClassificationReference,
|
||||
operator.ChangeClassificationLevel,
|
||||
prop.ClassificationReference,
|
||||
prop.BIMClassificationProperties,
|
||||
prop.BIMClassificationReferenceProperties,
|
||||
ui.BIM_PT_classifications,
|
||||
ui.BIM_PT_classification_references,
|
||||
ui.BIM_UL_classifications,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMClassificationProperties = bpy.props.PointerProperty(type=prop.BIMClassificationProperties)
|
||||
bpy.types.Object.BIMClassificationReferenceProperties = bpy.props.PointerProperty(
|
||||
type=prop.BIMClassificationReferenceProperties
|
||||
)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMClassificationProperties
|
||||
del bpy.types.Object.BIMClassificationReferenceProperties
|
||||
@@ -0,0 +1,39 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.schema
|
||||
import ifcopenshell.util.date
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"classification": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
edition_date = None
|
||||
if self.settings["classification"].EditionDate:
|
||||
edition_date = ifcopenshell.util.date.ifc2datetime(self.settings["classification"].EditionDate)
|
||||
self.settings["classification"].EditionDate = None
|
||||
|
||||
migrator = ifcopenshell.util.schema.Migrator()
|
||||
result = migrator.migrate(self.settings["classification"], self.file)
|
||||
|
||||
# TODO: should auto date migration be part of the migrator?
|
||||
if self.file.schema == "IFC2X3" and edition_date:
|
||||
result.EditionDate = ifcopenshell.util.date.datetime2ifc(edition_date, "IfcCalendarDate")
|
||||
else:
|
||||
result.EditionDate = ifcopenshell.util.date.datetime2ifc(edition_date, "IfcDate")
|
||||
|
||||
self.file.create_entity("IfcRelAssociatesClassification", **{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"RelatedObjects": [self.file.by_type("IfcProject")[0]],
|
||||
"RelatingClassification": result
|
||||
})
|
||||
return # See bug #1272
|
||||
try:
|
||||
result = self.file.add(self.settings["classification"])
|
||||
except:
|
||||
migrator = ifcopenshell.util.schema.Migrator()
|
||||
result = migrator.migrate(self.settings["classification"], self.file)
|
||||
@@ -0,0 +1,63 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.schema
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": None,
|
||||
"reference": None,
|
||||
"classification": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
relating_classification = None
|
||||
|
||||
if hasattr(self.settings["reference"], "ItemReference"):
|
||||
identification = self.settings["reference"].ItemReference # IFC2X3
|
||||
else:
|
||||
identification = self.settings["reference"].Identification
|
||||
|
||||
for reference in self.file.by_type("IfcClassificationReference"):
|
||||
if self.file.schema == "IFC2X3":
|
||||
if reference.ItemReference == identification:
|
||||
relating_classification = reference
|
||||
break
|
||||
else:
|
||||
if reference.Identification == identification:
|
||||
relating_classification = reference
|
||||
break
|
||||
|
||||
if relating_classification:
|
||||
association = self.get_association(relating_classification)
|
||||
related_objects = set(association.RelatedObjects)
|
||||
related_objects.add(self.settings["product"])
|
||||
association.RelatedObjects = list(related_objects)
|
||||
return
|
||||
|
||||
migrator = ifcopenshell.util.schema.Migrator()
|
||||
# This removal patch is to support a lightweight classification
|
||||
old_referenced_source = self.settings["reference"].ReferencedSource
|
||||
self.settings["reference"].ReferencedSource = None
|
||||
relating_classification = migrator.migrate(self.settings["reference"], self.file)
|
||||
relating_classification.ReferencedSource = self.settings["classification"]
|
||||
self.settings["reference"].ReferencedSource = old_referenced_source
|
||||
self.file.create_entity(
|
||||
"IfcRelAssociatesClassification",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"RelatedObjects": [self.settings["product"]],
|
||||
"RelatingClassification": relating_classification,
|
||||
}
|
||||
)
|
||||
|
||||
def get_association(self, reference):
|
||||
if self.file.schema == "IFC2X3":
|
||||
for association in self.file.by_type("IfcRelAssociatesClassification"):
|
||||
if association.RelatingClassification == reference:
|
||||
return association
|
||||
elif reference.ClassificationRefForObjects:
|
||||
return reference.ClassificationRefForObjects[0]
|
||||
@@ -0,0 +1,67 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.date
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class Data:
|
||||
is_loaded = False
|
||||
products = {}
|
||||
classifications = {}
|
||||
references = {}
|
||||
library_file = None
|
||||
library_classifications = {}
|
||||
library_references = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls, product_id=None):
|
||||
cls._file = IfcStore.get_file()
|
||||
if not cls._file:
|
||||
return
|
||||
if product_id:
|
||||
return cls.load_product_classifications(product_id)
|
||||
cls.load_classifications()
|
||||
cls.load_references()
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def load_product_classifications(cls, product_id):
|
||||
product = cls._file.by_id(product_id)
|
||||
cls.products[product_id] = []
|
||||
if not product.HasAssociations:
|
||||
return
|
||||
for association in product.HasAssociations:
|
||||
if association.is_a("IfcRelAssociatesClassification"):
|
||||
cls.products[product_id].append(association.RelatingClassification.id())
|
||||
|
||||
@classmethod
|
||||
def load_classifications(cls):
|
||||
cls.classifications = {}
|
||||
for classification in cls._file.by_type("IfcClassification"):
|
||||
data = classification.get_info()
|
||||
if cls._file.schema == "IFC2X3" and data["EditionDate"]:
|
||||
data["EditionDate"] = ifcopenshell.util.date(data.EditionDate).isoformat()
|
||||
cls.classifications[classification.id()] = data
|
||||
|
||||
@classmethod
|
||||
def load_references(cls):
|
||||
cls.references = {}
|
||||
for reference in cls._file.by_type("IfcClassificationReference"):
|
||||
data = reference.get_info()
|
||||
if reference.ReferencedSource:
|
||||
#data["ReferencedSource"] = cls.get_referenced_source(reference.ReferencedSource)
|
||||
data["ReferencedSource"] = reference.ReferencedSource.id()
|
||||
cls.references[reference.id()] = data
|
||||
|
||||
@classmethod
|
||||
def get_referenced_source(cls, reference):
|
||||
if reference.is_a("IfcClassification"):
|
||||
return reference
|
||||
elif reference.is_a("IfcClassificationReference") and reference.ReferencedSource:
|
||||
return cls.get_referenced_source(reference.ReferencedSource)
|
||||
|
||||
@classmethod
|
||||
def load_library(cls, filepath):
|
||||
cls.library_file = ifcopenshell.open(filepath)
|
||||
cls.library_classifications = {}
|
||||
for classification in cls.library_file.by_type("IfcClassification"):
|
||||
cls.library_classifications[classification.id()] = classification.Name
|
||||
@@ -0,0 +1,13 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"classification": 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["classification"], name, value)
|
||||
@@ -0,0 +1,13 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"reference": 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["reference"], name, value)
|
||||
@@ -0,0 +1,229 @@
|
||||
import bpy
|
||||
import json
|
||||
import blenderbim.bim.module.classification.add_classification as add_classification
|
||||
import blenderbim.bim.module.classification.remove_classification as remove_classification
|
||||
import blenderbim.bim.module.classification.edit_classification as edit_classification
|
||||
import blenderbim.bim.module.classification.add_reference as add_reference
|
||||
import blenderbim.bim.module.classification.remove_reference as remove_reference
|
||||
import blenderbim.bim.module.classification.edit_reference as edit_reference
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.classification.data import Data
|
||||
from blenderbim.bim.module.classification.prop import getClassifications, getReferences
|
||||
|
||||
|
||||
class LoadClassificationLibrary(bpy.types.Operator):
|
||||
bl_idname = "bim.load_classification_library"
|
||||
bl_label = "Load Classification Library"
|
||||
filename_ext = ".ifc"
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
Data.load_library(self.filepath)
|
||||
getClassifications(self, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class AddClassification(bpy.types.Operator):
|
||||
bl_idname = "bim.add_classification"
|
||||
bl_label = "Add Classification"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMClassificationProperties
|
||||
add_classification.Usecase(
|
||||
IfcStore.get_file(), {"classification": Data.library_file.by_id(int(props.available_classifications))}
|
||||
).execute()
|
||||
Data.load()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingClassification(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_classification"
|
||||
bl_label = "Enable Editing Classification"
|
||||
classification: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMClassificationProperties
|
||||
while len(props.classification_attributes) > 0:
|
||||
props.classification_attributes.remove(0)
|
||||
classification_data = Data.classifications[self.classification]
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcClassification").all_attributes():
|
||||
new = props.classification_attributes.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = classification_data[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
if attribute.name() == "ReferenceTokens":
|
||||
new.string_value = "" if new.is_null else json.dumps(classification_data[attribute.name()])
|
||||
else:
|
||||
new.string_value = "" if new.is_null else classification_data[attribute.name()]
|
||||
props.active_classification_id = self.classification
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingClassification(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_classification"
|
||||
bl_label = "Disable Editing Classification"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMClassificationProperties.active_classification_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveClassification(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_classification"
|
||||
bl_label = "Remove Classification"
|
||||
classification: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
remove_classification.Usecase(self.file, {"classification": self.file.by_id(self.classification)}).execute()
|
||||
Data.load()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditClassification(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_classification"
|
||||
bl_label = "Edit Classification"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMClassificationProperties
|
||||
attributes = {}
|
||||
for attribute in props.classification_attributes:
|
||||
if attribute.is_null:
|
||||
attributes[attribute.name] = None
|
||||
elif attribute.name == "ReferenceTokens":
|
||||
attributes[attribute.name] = json.loads(attribute.string_value)
|
||||
else:
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
self.file = IfcStore.get_file()
|
||||
edit_classification.Usecase(
|
||||
self.file, {"classification": self.file.by_id(props.active_classification_id), "attributes": attributes}
|
||||
).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.disable_editing_classification()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingClassificationReference(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_classification_reference"
|
||||
bl_label = "Enable Editing Classification Reference"
|
||||
reference: bpy.props.IntProperty()
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||
props = obj.BIMClassificationReferenceProperties
|
||||
while len(props.reference_attributes) > 0:
|
||||
props.reference_attributes.remove(0)
|
||||
reference_data = Data.references[self.reference]
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcClassificationReference").all_attributes():
|
||||
if attribute.name() == "ReferencedSource":
|
||||
continue
|
||||
new = props.reference_attributes.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = reference_data[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
new.string_value = "" if new.is_null else reference_data[attribute.name()]
|
||||
props.active_reference_id = self.reference
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingClassificationReference(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_classification_reference"
|
||||
bl_label = "Disable Editing Classification Reference"
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||
obj.BIMClassificationReferenceProperties.active_reference_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveClassificationReference(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_classification_reference"
|
||||
bl_label = "Remove Classification Reference"
|
||||
reference: bpy.props.IntProperty()
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
remove_reference.Usecase(
|
||||
self.file,
|
||||
{
|
||||
"reference": self.file.by_id(self.reference),
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
},
|
||||
).execute()
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
Data.load()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditClassificationReference(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_classification_reference"
|
||||
bl_label = "Edit Classification Reference"
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||
props = obj.BIMClassificationReferenceProperties
|
||||
attributes = {}
|
||||
for attribute in props.reference_attributes:
|
||||
if attribute.is_null:
|
||||
attributes[attribute.name] = None
|
||||
else:
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
self.file = IfcStore.get_file()
|
||||
edit_reference.Usecase(
|
||||
self.file, {"reference": self.file.by_id(props.active_reference_id), "attributes": attributes}
|
||||
).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.disable_editing_classification_reference()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddClassificationReference(bpy.types.Operator):
|
||||
bl_idname = "bim.add_classification_reference"
|
||||
bl_label = "Add Classification Reference"
|
||||
reference: bpy.props.IntProperty()
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
|
||||
classification = None
|
||||
|
||||
props = context.scene.BIMClassificationProperties
|
||||
classification_name = Data.library_classifications[int(props.available_classifications)]
|
||||
for classification_id, classification in Data.classifications.items():
|
||||
if classification["Name"] == classification_name:
|
||||
classification = self.file.by_id(classification_id)
|
||||
break
|
||||
|
||||
add_reference.Usecase(
|
||||
self.file,
|
||||
{
|
||||
"reference": Data.library_file.by_id(self.reference),
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"classification": classification
|
||||
},
|
||||
).execute()
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
Data.load()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ChangeClassificationLevel(bpy.types.Operator):
|
||||
bl_idname = "bim.change_classification_level"
|
||||
bl_label = "Change Classification Level"
|
||||
parent_id: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
getReferences(self, context, parent_id=self.parent_id)
|
||||
return {"FINISHED"}
|
||||
@@ -0,0 +1,71 @@
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.module.classification.data import Data
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
classification_enum = []
|
||||
|
||||
|
||||
def getClassifications(self, context):
|
||||
global classification_enum
|
||||
if len(classification_enum) < 1:
|
||||
classification_enum.clear()
|
||||
classification_enum.extend([(str(i), n, "") for i, n in Data.library_classifications.items()])
|
||||
if classification_enum:
|
||||
getReferences(self, context, parent_id=int(classification_enum[0][0]))
|
||||
return classification_enum
|
||||
|
||||
|
||||
def updateClassification(self, context):
|
||||
getReferences(self, context, parent_id=int(self.available_classifications))
|
||||
|
||||
|
||||
def getReferences(self, context, parent_id=None):
|
||||
props = context.scene.BIMClassificationProperties
|
||||
while len(props.available_library_references) > 0:
|
||||
props.available_library_references.remove(0)
|
||||
for reference in Data.library_file.by_id(parent_id).HasReferences:
|
||||
new = props.available_library_references.add()
|
||||
new.identification = reference.Identification or ""
|
||||
new.name = reference.Name or ""
|
||||
new.ifc_definition_id = reference.id()
|
||||
new.has_references = bool(reference.HasReferences)
|
||||
new.referenced_source
|
||||
if reference.ReferencedSource.is_a("IfcClassificationReference"):
|
||||
props.active_library_referenced_source = reference.ReferencedSource.ReferencedSource.id()
|
||||
else:
|
||||
props.active_library_referenced_source = 0
|
||||
|
||||
|
||||
class ClassificationReference(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
identification: StringProperty(name="Identification")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
has_references: BoolProperty(name="Has References")
|
||||
referenced_source: IntProperty(name="IFC Definition ID")
|
||||
|
||||
|
||||
class BIMClassificationProperties(PropertyGroup):
|
||||
available_classifications: EnumProperty(
|
||||
items=getClassifications, name="Available Classifications", update=updateClassification
|
||||
)
|
||||
classification_attributes: CollectionProperty(name="Classification Attributes", type=Attribute)
|
||||
active_classification_id: IntProperty(name="Active Classification Id")
|
||||
available_library_references: CollectionProperty(name="Available Library References", type=ClassificationReference)
|
||||
active_library_referenced_source: IntProperty(name="Active Library Referenced Source")
|
||||
active_library_reference_index: IntProperty(name="Active Library Reference Index")
|
||||
|
||||
|
||||
class BIMClassificationReferenceProperties(PropertyGroup):
|
||||
reference_attributes: CollectionProperty(name="Reference Attributes", type=Attribute)
|
||||
active_reference_id: IntProperty(name="Active Reference Id")
|
||||
@@ -0,0 +1,24 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {"classification": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
references = self.get_references(self.settings["classification"])
|
||||
for reference in references:
|
||||
self.file.remove(reference)
|
||||
self.file.remove(self.settings["classification"])
|
||||
for rel in self.file.by_type("IfcRelAssociatesClassification"):
|
||||
if not rel.RelatingClassification:
|
||||
self.file.remove(rel)
|
||||
|
||||
def get_references(self, classification):
|
||||
results = []
|
||||
if not classification.HasReferences:
|
||||
return results
|
||||
for reference in classification.HasReferences:
|
||||
results.append(reference)
|
||||
results.extend(self.get_references(reference))
|
||||
return results
|
||||
@@ -0,0 +1,28 @@
|
||||
import ifcopenshell.util.schema
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {"reference": None, "product": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
total_related_objects = 0
|
||||
for association in self.file.by_type("IfcRelAssociatesClassification"):
|
||||
if association.RelatingClassification == self.settings["reference"] and association.RelatedObjects:
|
||||
total_related_objects += len(association.RelatedObjects)
|
||||
related_objects = list(association.RelatedObjects)
|
||||
try:
|
||||
related_objects.remove(self.settings["product"])
|
||||
except:
|
||||
continue
|
||||
if len(related_objects):
|
||||
association.RelatedObjects = related_objects
|
||||
else:
|
||||
self.file.remove(association)
|
||||
|
||||
# TODO: we only handle lightweight classifications here
|
||||
if total_related_objects == 1:
|
||||
self.file.remove(self.settings["reference"])
|
||||
@@ -0,0 +1,159 @@
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.classification.data import Data
|
||||
|
||||
|
||||
class BIM_PT_classifications(Panel):
|
||||
bl_label = "IFC Classifications"
|
||||
bl_idname = "BIM_PT_classifications"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
|
||||
self.props = context.scene.BIMClassificationProperties
|
||||
|
||||
if Data.library_file:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "available_classifications", text="")
|
||||
row.operator("bim.load_classification_library", text="", icon="IMPORT")
|
||||
row.operator("bim.add_classification", text="", icon="ADD")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No Active Classification Library")
|
||||
row.operator("bim.load_classification_library", text="", icon="IMPORT")
|
||||
|
||||
for classification_id, classification in Data.classifications.items():
|
||||
if self.props.active_classification_id == classification_id:
|
||||
self.draw_editable_ui(classification)
|
||||
else:
|
||||
self.draw_ui(classification_id, classification)
|
||||
|
||||
def draw_editable_ui(self, classification):
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props.classification_attributes.get("Name"), "string_value", text="", icon="ASSET_MANAGER")
|
||||
row.operator("bim.edit_classification", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_classification", text="", icon="X")
|
||||
|
||||
for attribute in self.props.classification_attributes:
|
||||
if attribute.name == "Name":
|
||||
continue
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
def draw_ui(self, classification_id, classification):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=classification["Name"], icon="ASSET_MANAGER")
|
||||
if not self.props.active_classification_id:
|
||||
op = row.operator("bim.enable_editing_classification", text="", icon="GREASEPENCIL")
|
||||
op.classification = classification_id
|
||||
row.operator("bim.remove_classification", text="", icon="X").classification = classification_id
|
||||
|
||||
|
||||
class BIM_PT_classification_references(Panel):
|
||||
bl_label = "IFC Classification References"
|
||||
bl_idname = "BIM_PT_classification_references"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
|
||||
def draw(self, context):
|
||||
obj = context.active_object
|
||||
self.oprops = obj.BIMObjectProperties
|
||||
self.sprops = context.scene.BIMClassificationProperties
|
||||
self.props = obj.BIMClassificationReferenceProperties
|
||||
self.file = IfcStore.get_file()
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
if self.oprops.ifc_definition_id not in Data.products:
|
||||
Data.load(self.oprops.ifc_definition_id)
|
||||
|
||||
self.draw_add_ui()
|
||||
|
||||
reference_ids = Data.products[self.oprops.ifc_definition_id]
|
||||
if not reference_ids:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No References")
|
||||
|
||||
for reference_id in reference_ids:
|
||||
reference = Data.references[reference_id]
|
||||
if self.props.active_reference_id == reference_id:
|
||||
self.draw_editable_ui(reference)
|
||||
else:
|
||||
self.draw_ui(reference_id, reference)
|
||||
|
||||
def draw_add_ui(self):
|
||||
if not self.sprops.available_classifications:
|
||||
return
|
||||
|
||||
name = Data.library_classifications[int(self.sprops.available_classifications)]
|
||||
if name in [c["Name"] for c in Data.classifications.values()]:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.sprops, "available_classifications", text="")
|
||||
if self.sprops.active_library_referenced_source:
|
||||
op = row.operator("bim.change_classification_level", text="", icon="FRAME_PREV")
|
||||
op.parent_id = self.sprops.active_library_referenced_source
|
||||
op = row.operator("bim.add_classification_reference", text="", icon="ADD")
|
||||
op.reference = self.sprops.available_library_references[
|
||||
self.sprops.active_library_reference_index
|
||||
].ifc_definition_id
|
||||
self.layout.template_list(
|
||||
"BIM_UL_classifications",
|
||||
"",
|
||||
self.sprops,
|
||||
"available_library_references",
|
||||
self.sprops,
|
||||
"active_library_reference_index",
|
||||
)
|
||||
|
||||
def draw_editable_ui(self, reference):
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props.reference_attributes.get("Name"), "string_value", text="", icon="ASSET_MANAGER")
|
||||
row.operator("bim.edit_classification_reference", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_classification_reference", text="", icon="X")
|
||||
|
||||
for attribute in self.props.reference_attributes:
|
||||
if attribute.name == "Name":
|
||||
continue
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
def draw_ui(self, reference_id, reference):
|
||||
row = self.layout.row(align=True)
|
||||
if self.file.schema == "IFC2X3":
|
||||
name = reference["ItemReference"] or "No Identification"
|
||||
else:
|
||||
name = reference["Identification"] or "No Identification"
|
||||
row.label(text=name, icon="ASSET_MANAGER")
|
||||
row.label(text=reference["Name"] or "")
|
||||
if not self.props.active_reference_id:
|
||||
op = row.operator("bim.enable_editing_classification_reference", text="", icon="GREASEPENCIL")
|
||||
op.reference = reference_id
|
||||
row.operator("bim.remove_classification_reference", text="", icon="X").reference = reference_id
|
||||
|
||||
|
||||
class BIM_UL_classifications(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
if item.has_references:
|
||||
op = layout.operator("bim.change_classification_level", text="", icon="DISCLOSURE_TRI_RIGHT")
|
||||
op.parent_id = item.ifc_definition_id
|
||||
layout.label(text=item.identification)
|
||||
layout.label(text=item.name)
|
||||
@@ -0,0 +1,33 @@
|
||||
import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.LoadObjectives,
|
||||
operator.DisableConstraintEditingUI,
|
||||
operator.EnableEditingConstraint,
|
||||
operator.DisableEditingConstraint,
|
||||
operator.AddObjective,
|
||||
operator.EditObjective,
|
||||
operator.RemoveConstraint,
|
||||
operator.EnableAssigningConstraint,
|
||||
operator.DisableAssigningConstraint,
|
||||
operator.AssignConstraint,
|
||||
operator.UnassignConstraint,
|
||||
prop.Constraint,
|
||||
prop.BIMConstraintProperties,
|
||||
prop.BIMObjectConstraintProperties,
|
||||
ui.BIM_PT_constraints,
|
||||
ui.BIM_PT_object_constraints,
|
||||
ui.BIM_UL_constraints,
|
||||
ui.BIM_UL_object_constraints,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMConstraintProperties = bpy.props.PointerProperty(type=prop.BIMConstraintProperties)
|
||||
bpy.types.Object.BIMObjectConstraintProperties = bpy.props.PointerProperty(type=prop.BIMObjectConstraintProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMConstraintProperties
|
||||
del bpy.types.Object.BIMObjectConstraintProperties
|
||||
@@ -0,0 +1,15 @@
|
||||
import ifcopenshell
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings={}):
|
||||
self.file = file
|
||||
self.settings = {}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
return self.file.create_entity("IfcObjective", **{
|
||||
"Name": "Unnamed",
|
||||
"ConstraintGrade": "NOTDEFINED",
|
||||
"ObjectiveQualifier": "NOTDEFINED"
|
||||
})
|
||||
@@ -0,0 +1,28 @@
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": None,
|
||||
"constraint": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
rel = self.get_constraint_rel()
|
||||
related_objects = set(rel.RelatedObjects) if rel.RelatedObjects else set()
|
||||
related_objects.add(self.settings["product"])
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
|
||||
def get_constraint_rel(self):
|
||||
for rel in self.file.by_type("IfcRelAssociatesConstraint"):
|
||||
if rel.RelatingConstraint == self.settings["constraint"]:
|
||||
return rel
|
||||
return self.file.create_entity("IfcRelAssociatesConstraint", **{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
# TODO: owner history
|
||||
"RelatingConstraint": self.settings["constraint"]
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.date
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class Data:
|
||||
is_loaded = False
|
||||
products = {}
|
||||
objectives = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls, product_id=None):
|
||||
cls._file = IfcStore.get_file()
|
||||
if not cls._file:
|
||||
return
|
||||
if product_id:
|
||||
return cls.load_product(product_id)
|
||||
cls.load_objectives()
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def load_product(cls, product_id):
|
||||
product = cls._file.by_id(product_id)
|
||||
cls.products[product_id] = []
|
||||
if not product.HasAssociations:
|
||||
return
|
||||
for association in product.HasAssociations:
|
||||
if association.is_a("IfcRelAssociatesConstraint"):
|
||||
if not association.RelatingConstraint.is_a("IfcObjective"):
|
||||
continue # not yet implemented
|
||||
cls.products[product_id].append(association.RelatingConstraint.id())
|
||||
|
||||
@classmethod
|
||||
def load_objectives(cls):
|
||||
cls.objectives = {}
|
||||
for constraint in cls._file.by_type("IfcObjective"):
|
||||
data = constraint.get_info()
|
||||
if cls._file.schema == "IFC2X3":
|
||||
for attribute in ["CreationTime"]:
|
||||
if data[attribute]:
|
||||
data[attribute] = ifcopenshell.util.date.ifc2datetime(data[attribute]).isoformat()
|
||||
cls.objectives[constraint.id()] = data
|
||||
@@ -0,0 +1,13 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"objective": 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["objective"], name, value)
|
||||
@@ -0,0 +1,188 @@
|
||||
import bpy
|
||||
import json
|
||||
import blenderbim.bim.module.constraint.add_objective as add_objective
|
||||
import blenderbim.bim.module.constraint.edit_objective as edit_objective
|
||||
import blenderbim.bim.module.constraint.remove_constraint as remove_constraint
|
||||
import blenderbim.bim.module.constraint.assign_constraint as assign_constraint
|
||||
import blenderbim.bim.module.constraint.unassign_constraint as unassign_constraint
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.constraint.data import Data
|
||||
|
||||
|
||||
class LoadObjectives(bpy.types.Operator):
|
||||
bl_idname = "bim.load_objectives"
|
||||
bl_label = "Load Objectives"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMConstraintProperties
|
||||
while len(props.constraints) > 0:
|
||||
props.constraints.remove(0)
|
||||
for constraint_id, constraint in Data.objectives.items():
|
||||
new = props.constraints.add()
|
||||
new.name = constraint["Name"] or "Unnamed"
|
||||
new.ifc_definition_id = constraint_id
|
||||
props.is_editing = "IfcObjective"
|
||||
bpy.ops.bim.disable_editing_constraint()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableConstraintEditingUI(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_constraint_editing_ui"
|
||||
bl_label = "Disable Constraint Editing UI"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMConstraintProperties.is_editing = ""
|
||||
bpy.ops.bim.disable_editing_constraint()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingConstraint(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_constraint"
|
||||
bl_label = "Enable Editing Constraint"
|
||||
constraint: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMConstraintProperties
|
||||
while len(props.constraint_attributes) > 0:
|
||||
props.constraint_attributes.remove(0)
|
||||
|
||||
if props.is_editing == "IfcObjective":
|
||||
data = Data.objectives[self.constraint]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name(props.is_editing).all_attributes():
|
||||
data_type = str(attribute.type_of_attribute)
|
||||
if "<entity" in data_type:
|
||||
continue
|
||||
new = props.constraint_attributes.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = data[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
if "<string>" in data_type:
|
||||
new.string_value = "" if new.is_null else data[attribute.name()]
|
||||
new.data_type = "string"
|
||||
elif "<enumeration" in data_type:
|
||||
new.enum_items = json.dumps(attribute.type_of_attribute().declared_type().enumeration_items())
|
||||
new.data_type = "enum"
|
||||
if data[attribute.name()]:
|
||||
new.enum_value = data[attribute.name()]
|
||||
props.active_constraint_id = self.constraint
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingConstraint(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_constraint"
|
||||
bl_label = "Disable Editing Constraint"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMConstraintProperties.active_constraint_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddObjective(bpy.types.Operator):
|
||||
bl_idname = "bim.add_objective"
|
||||
bl_label = "Add Objective"
|
||||
|
||||
def execute(self, context):
|
||||
result = add_objective.Usecase(IfcStore.get_file()).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.load_objectives()
|
||||
bpy.ops.bim.enable_editing_constraint(constraint=result.id())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditObjective(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_objective"
|
||||
bl_label = "Edit Objective"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMConstraintProperties
|
||||
attributes = {}
|
||||
for attribute in props.constraint_attributes:
|
||||
if attribute.is_null:
|
||||
attributes[attribute.name] = None
|
||||
elif attribute.enum_items:
|
||||
attributes[attribute.name] = attribute.enum_value
|
||||
else:
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
self.file = IfcStore.get_file()
|
||||
edit_objective.Usecase(
|
||||
self.file, {"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes}
|
||||
).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.load_objectives()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveConstraint(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_constraint"
|
||||
bl_label = "Remove Constraint"
|
||||
constraint: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMConstraintProperties
|
||||
self.file = IfcStore.get_file()
|
||||
remove_constraint.Usecase(self.file, {"constraint": self.file.by_id(self.constraint)}).execute()
|
||||
Data.load()
|
||||
if props.is_editing == "IfcObjective":
|
||||
bpy.ops.bim.load_objectives()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableAssigningConstraint(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_assigning_constraint"
|
||||
bl_label = "Enable Assigning Constraint"
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
props = obj.BIMObjectConstraintProperties
|
||||
if props.available_constraint_types == "IfcObjective":
|
||||
bpy.ops.bim.load_objectives()
|
||||
props.is_adding = props.available_constraint_types
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableAssigningConstraint(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_assigning_constraint"
|
||||
bl_label = "Disable Assigning Constraint"
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
props = obj.BIMObjectConstraintProperties
|
||||
props.is_adding = ""
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignConstraint(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_constraint"
|
||||
bl_label = "Assign Constraint"
|
||||
obj: bpy.props.StringProperty()
|
||||
constraint: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
assign_constraint.Usecase(self.file, {
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"constraint": self.file.by_id(self.constraint)
|
||||
}).execute()
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class UnassignConstraint(bpy.types.Operator):
|
||||
bl_idname = "bim.unassign_constraint"
|
||||
bl_label = "Unassign Constraint"
|
||||
obj: bpy.props.StringProperty()
|
||||
constraint: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
unassign_constraint.Usecase(self.file, {
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"constraint": self.file.by_id(self.constraint)
|
||||
}).execute()
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
@@ -0,0 +1,33 @@
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
class Constraint(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
|
||||
class BIMConstraintProperties(PropertyGroup):
|
||||
constraint_attributes: CollectionProperty(name="Constraint Attributes", type=Attribute)
|
||||
active_constraint_id: IntProperty(name="Active Constraint Id")
|
||||
constraints: CollectionProperty(name="Constraints", type=Constraint)
|
||||
active_constraint_index: IntProperty(name="Active Constraint Index")
|
||||
is_editing: StringProperty(name="Is Editing")
|
||||
|
||||
|
||||
class BIMObjectConstraintProperties(PropertyGroup):
|
||||
is_adding: StringProperty(name="Is Adding")
|
||||
available_constraint_types: EnumProperty(
|
||||
items=[(c, c, "") for c in ["IfcObjective"]], name="Available Constraint Types"
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {"constraint": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
self.file.remove(self.settings["constraint"])
|
||||
for rel in self.file.by_type("IfcRelAssociatesConstraint"):
|
||||
if not rel.RelatingConstraint:
|
||||
self.file.remove(rel)
|
||||
@@ -0,0 +1,141 @@
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.constraint.data import Data
|
||||
|
||||
|
||||
class BIM_PT_constraints(Panel):
|
||||
bl_label = "IFC Constraints"
|
||||
bl_idname = "BIM_PT_constraints"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
|
||||
self.props = context.scene.BIMConstraintProperties
|
||||
|
||||
if not self.props.is_editing or self.props.is_editing == "IfcObjective":
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Objectives Found".format(len(Data.objectives)), icon="LIGHT")
|
||||
if self.props.is_editing == "IfcObjective":
|
||||
row.operator("bim.disable_constraint_editing_ui", text="", icon="CHECKMARK")
|
||||
row.operator("bim.add_objective", text="", icon="ADD")
|
||||
else:
|
||||
row.operator("bim.load_objectives", text="", icon="GREASEPENCIL")
|
||||
|
||||
if self.props.is_editing:
|
||||
self.layout.template_list(
|
||||
"BIM_UL_constraints",
|
||||
"",
|
||||
self.props,
|
||||
"constraints",
|
||||
self.props,
|
||||
"active_constraint_index",
|
||||
)
|
||||
|
||||
if self.props.active_constraint_id:
|
||||
self.draw_editable_ui(context)
|
||||
return
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
for attribute in self.props.constraint_attributes:
|
||||
row = self.layout.row(align=True)
|
||||
if attribute.data_type == "string":
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
elif attribute.data_type == "enum":
|
||||
row.prop(attribute, "enum_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
|
||||
class BIM_PT_object_constraints(Panel):
|
||||
bl_label = "IFC Constraints"
|
||||
bl_idname = "BIM_PT_object_constraints"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
|
||||
def draw(self, context):
|
||||
obj = context.active_object
|
||||
self.oprops = obj.BIMObjectProperties
|
||||
self.sprops = context.scene.BIMConstraintProperties
|
||||
self.props = obj.BIMObjectConstraintProperties
|
||||
self.file = IfcStore.get_file()
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
if self.oprops.ifc_definition_id not in Data.products:
|
||||
Data.load(self.oprops.ifc_definition_id)
|
||||
|
||||
self.draw_add_ui()
|
||||
|
||||
constraint_ids = Data.products[self.oprops.ifc_definition_id]
|
||||
|
||||
if not constraint_ids:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No Constraints", icon="LIGHT")
|
||||
|
||||
for constraint_id in constraint_ids:
|
||||
try:
|
||||
constraint = Data.objectives[constraint_id]
|
||||
icon = "LIGHT"
|
||||
except:
|
||||
pass # Metric not implemented
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=constraint.get("Name") or "Unnamed")
|
||||
row.operator("bim.unassign_constraint", text="", icon="X").constraint = constraint_id
|
||||
|
||||
def draw_add_ui(self):
|
||||
if self.props.is_adding:
|
||||
row = self.layout.row(align=True)
|
||||
icon = "LIGHT" if self.props.is_adding == "IfcObjective" else "FILE_HIDDEN"
|
||||
row.label(text="Adding {}".format(self.props.is_adding), icon=icon)
|
||||
row.operator("bim.disable_assigning_constraint", text="", icon="X")
|
||||
self.layout.template_list(
|
||||
"BIM_UL_object_constraints",
|
||||
"",
|
||||
self.sprops,
|
||||
"constraints",
|
||||
self.sprops,
|
||||
"active_constraint_index",
|
||||
)
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "available_constraint_types", text="")
|
||||
row.operator("bim.enable_assigning_constraint", text="", icon="ADD")
|
||||
|
||||
|
||||
class BIM_UL_constraints(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.scene.BIMConstraintProperties.active_constraint_id == item.ifc_definition_id:
|
||||
if context.scene.BIMConstraintProperties.is_editing == "IfcObjective":
|
||||
row.operator("bim.edit_objective", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_constraint", text="", icon="X")
|
||||
elif context.scene.BIMConstraintProperties.active_constraint_id:
|
||||
row.operator("bim.remove_constraint", text="", icon="X").constraint = item.ifc_definition_id
|
||||
else:
|
||||
op = row.operator("bim.enable_editing_constraint", text="", icon="GREASEPENCIL")
|
||||
op.constraint = item.ifc_definition_id
|
||||
row.operator("bim.remove_constraint", text="", icon="X").constraint = item.ifc_definition_id
|
||||
|
||||
|
||||
class BIM_UL_object_constraints(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)
|
||||
row.operator("bim.assign_constraint", text="", icon="ADD").constraint = item.ifc_definition_id
|
||||
@@ -0,0 +1,17 @@
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": None,
|
||||
"constraint": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for rel in self.settings["product"].HasAssociations:
|
||||
if rel.is_a("IfcRelAssociatesConstraint") and rel.RelatingConstraint == self.settings["constraint"]:
|
||||
self.file.remove(rel)
|
||||
@@ -1,6 +1,9 @@
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
from math import degrees, atan2
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
from .api import Api
|
||||
|
||||
@@ -70,6 +73,7 @@ class RunAnalysis(bpy.types.Operator):
|
||||
bl_label = "Run Analysis"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.inputs = {
|
||||
"floors": [],
|
||||
"walls": [],
|
||||
@@ -94,16 +98,13 @@ class RunAnalysis(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_rotation_angle(self):
|
||||
if (
|
||||
not bpy.context.scene.BIMProperties.has_georeferencing
|
||||
or not bpy.context.scene.MapConversion.x_axis_abscissa
|
||||
or not bpy.context.scene.MapConversion.x_axis_ordinate
|
||||
):
|
||||
if not self.file.by_type("IfcMapConversion"):
|
||||
return 0
|
||||
map_conversion = self.file.by_type("IfcMapConversion")[0]
|
||||
rotation = -1 * degrees(
|
||||
atan2(
|
||||
float(bpy.context.scene.MapConversion.x_axis_ordinate),
|
||||
float(bpy.context.scene.MapConversion.x_axis_abscissa),
|
||||
float(map_conversion.XAxisOrdinate or 0),
|
||||
float(map_conversion.XAxisAbscissa or 1),
|
||||
)
|
||||
)
|
||||
if rotation < 0:
|
||||
@@ -164,34 +165,36 @@ class RunAnalysis(bpy.types.Operator):
|
||||
def get_covetool_category(self, obj):
|
||||
if not hasattr(obj, "data") or not isinstance(obj.data, bpy.types.Mesh):
|
||||
return
|
||||
if "IfcSlab" in obj.name:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
return
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
ifc_class = element.is_a()
|
||||
if "IfcSlab" in ifc_class:
|
||||
return "floors"
|
||||
elif "IfcRoof" in obj.name:
|
||||
elif "IfcRoof" in ifc_class:
|
||||
return "roofs"
|
||||
elif "IfcWall" in obj.name:
|
||||
if self.is_wall_internal(obj):
|
||||
elif "IfcWall" in ifc_class:
|
||||
if self.is_wall_internal(element):
|
||||
return "interior_walls"
|
||||
return "walls"
|
||||
elif "IfcWindow" in obj.name:
|
||||
if self.is_window_skylight(obj):
|
||||
elif "IfcWindow" in ifc_class:
|
||||
if self.is_window_skylight(element):
|
||||
return "skylights"
|
||||
return "windows"
|
||||
elif "IfcShadingDevice" in obj.name:
|
||||
elif "IfcShadingDevice" in ifc_class:
|
||||
return "shading_devices"
|
||||
|
||||
def is_wall_internal(self, obj):
|
||||
pset_wallcommon = obj.BIMObjectProperties.psets.get("Pset_WallCommon")
|
||||
if pset_wallcommon:
|
||||
is_external = pset_wallcommon.properties.get("IsExternal")
|
||||
if is_external:
|
||||
if is_external.string_value == "True":
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
predefined_type = obj.BIMObjectProperties.attributes.get("PredefinedType")
|
||||
if predefined_type and predefined_type.string_value in ["MOVABLE", "PARTITIONING", "PLUMBINGWALL"]:
|
||||
def is_wall_internal(self, element):
|
||||
psets = ifcopenshell.util.element.get_psets(element)
|
||||
pset = psets.get("Pset_WallCommon")
|
||||
if pset:
|
||||
is_external = pset.get("IsExternal", None)
|
||||
if is_external is not None:
|
||||
return is_external
|
||||
predefined_type = element.get_info().get("PredefinedType")
|
||||
if predefined_type and predefined_type in ["MOVABLE", "PARTITIONING", "PLUMBINGWALL"]:
|
||||
return True
|
||||
|
||||
def is_window_skylight(self, obj):
|
||||
predefined_type = obj.BIMObjectProperties.attributes.get("PredefinedType")
|
||||
def is_window_skylight(self, element):
|
||||
predefined_type = element.get_info().get("PredefinedType")
|
||||
return predefined_type and predefined_type.string_value == "SKYLIGHT"
|
||||
|
||||
@@ -7,6 +7,7 @@ classes = (
|
||||
operator.ExportIfcCsv,
|
||||
operator.ImportIfcCsv,
|
||||
operator.EyedropIfcCsv,
|
||||
operator.SelectCsvIfcFile,
|
||||
prop.CsvProperties,
|
||||
ui.BIM_PT_ifccsv,
|
||||
)
|
||||
|
||||
@@ -9,24 +9,25 @@ import tempfile
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
|
||||
class AddCsvAttribute(bpy.types.Operator):
|
||||
bl_idname = "bim.add_csv_attribute"
|
||||
bl_label = "Add CSV Attribute"
|
||||
|
||||
def execute(self, context):
|
||||
attribute = bpy.context.scene.CsvProperties.csv_attributes.add()
|
||||
attribute = context.scene.CsvProperties.csv_attributes.add()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveCsvAttribute(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_csv_attribute"
|
||||
bl_label = "Remove CSV Attribute"
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
bpy.context.scene.CsvProperties.csv_attributes.remove(self.index)
|
||||
context.scene.CsvProperties.csv_attributes.remove(self.index)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ExportIfcCsv(bpy.types.Operator):
|
||||
bl_idname = "bim.export_ifccsv"
|
||||
bl_label = "Export IFC to CSV"
|
||||
@@ -42,21 +43,26 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
import ifccsv
|
||||
|
||||
props = context.scene.CsvProperties
|
||||
self.filepath = bpy.path.ensure_ext(self.filepath, ".csv")
|
||||
ifc_file = ifcopenshell.open(bpy.context.scene.CsvProperties.csv_ifc_file)
|
||||
if props.should_load_from_memory:
|
||||
ifc_file = IfcStore.get_file()
|
||||
else:
|
||||
ifc_file = ifcopenshell.open(props.csv_ifc_file)
|
||||
selector = ifcopenshell.util.selector.Selector()
|
||||
results = selector.parse(ifc_file, bpy.context.scene.CsvProperties.ifc_selector)
|
||||
results = selector.parse(ifc_file, props.ifc_selector)
|
||||
ifc_csv = ifccsv.IfcCsv()
|
||||
ifc_csv.output = self.filepath
|
||||
ifc_csv.attributes = [a.name for a in bpy.context.scene.CsvProperties.csv_attributes]
|
||||
ifc_csv.attributes = [a.name for a in props.csv_attributes]
|
||||
ifc_csv.selector = selector
|
||||
if bpy.context.scene.CsvProperties.csv_delimiter == "CUSTOM":
|
||||
ifc_csv.delimiter = bpy.context.scene.CsvProperties.csv_custom_delimiter
|
||||
if props.csv_delimiter == "CUSTOM":
|
||||
ifc_csv.delimiter = props.csv_custom_delimiter
|
||||
else:
|
||||
ifc_csv.delimiter = bpy.context.scene.CsvProperties.csv_delimiter
|
||||
ifc_csv.delimiter = props.csv_delimiter
|
||||
ifc_csv.export(ifc_file, results)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ImportIfcCsv(bpy.types.Operator):
|
||||
bl_idname = "bim.import_ifccsv"
|
||||
bl_label = "Import CSV to IFC"
|
||||
@@ -74,18 +80,36 @@ class ImportIfcCsv(bpy.types.Operator):
|
||||
|
||||
ifc_csv = ifccsv.IfcCsv()
|
||||
ifc_csv.output = self.filepath
|
||||
ifc_csv.Import(bpy.context.scene.CsvProperties.csv_ifc_file)
|
||||
ifc_csv.Import(context.scene.CsvProperties.csv_ifc_file)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EyedropIfcCsv(bpy.types.Operator):
|
||||
bl_idname = "bim.eyedrop_ifccsv"
|
||||
bl_label = "Query Selected Items"
|
||||
|
||||
def execute(self, context):
|
||||
global_ids = []
|
||||
for obj in bpy.context.selected_objects:
|
||||
if hasattr(obj, "BIMObjectProperties") and obj.BIMObjectProperties.attributes.get("GlobalId"):
|
||||
global_ids.append("#" + obj.BIMObjectProperties.attributes.get("GlobalId").string_value)
|
||||
bpy.context.scene.CsvProperties.ifc_selector = "|".join(global_ids)
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in context.selected_objects:
|
||||
if hasattr(obj, "BIMObjectProperties") and obj.BIMObjectProperties.ifc_definition_id:
|
||||
global_ids.append("#" + self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId)
|
||||
context.scene.CsvProperties.ifc_selector = "|".join(global_ids)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectCsvIfcFile(bpy.types.Operator):
|
||||
bl_idname = "bim.select_csv_ifc_file"
|
||||
bl_label = "Select CSV IFC File"
|
||||
filename_ext = ".ifc"
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.CsvProperties.csv_ifc_file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
@@ -18,10 +18,14 @@ class CsvProperties(PropertyGroup):
|
||||
ifc_selector: StringProperty(default="", name="IFC Selector")
|
||||
csv_attributes: CollectionProperty(name="CSV Attributes", type=StrProperty)
|
||||
csv_delimiter: EnumProperty(
|
||||
items=[(";", ";", ""), (",", ",", ""), (".", ".", ""), ("CUSTOM", "Custom", ""),],
|
||||
items=[
|
||||
(";", ";", ""),
|
||||
(",", ",", ""),
|
||||
(".", ".", ""),
|
||||
("CUSTOM", "Custom", ""),
|
||||
],
|
||||
name="IFC CSV Delimiter",
|
||||
default=",",
|
||||
)
|
||||
csv_custom_delimiter: StringProperty(default="", name="Custom Delimiter")
|
||||
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
|
||||
|
||||
|
||||
@@ -16,20 +16,20 @@ class BIM_PT_ifccsv(Panel):
|
||||
scene = context.scene
|
||||
props = scene.CsvProperties
|
||||
|
||||
if IfcStore.get_file():
|
||||
if IfcStore.get_file():
|
||||
row = layout.row()
|
||||
row.prop(props, "should_load_from_memory")
|
||||
|
||||
|
||||
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_ifc_file")
|
||||
row.operator("bim.import_ifccsv", icon="FILE_FOLDER", text="")
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "ifc_selector")
|
||||
row.operator("bim.eyedrop_ifccsv", icon="EYEDROPPER", text="")
|
||||
row.operator("bim.select_csv_ifc_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "ifc_selector")
|
||||
row.operator("bim.eyedrop_ifccsv", icon="EYEDROPPER", text="")
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="Add IFC attributes to filter", icon="FILE_BLANK")
|
||||
row.operator("bim.add_csv_attribute")
|
||||
|
||||
for index, attribute in enumerate(props.csv_attributes):
|
||||
@@ -40,11 +40,10 @@ class BIM_PT_ifccsv(Panel):
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_delimiter")
|
||||
|
||||
if(props.csv_delimiter == 'CUSTOM'):
|
||||
if props.csv_delimiter == "CUSTOM":
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_custom_delimiter")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.export_ifccsv", icon="EXPORT")
|
||||
row.operator("bim.import_ifccsv", icon="IMPORT")
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ class VisualiseDiff(bpy.types.Operator):
|
||||
for obj in bpy.context.visible_objects:
|
||||
obj.color = (1.0, 1.0, 1.0, 0.2)
|
||||
global_id = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId
|
||||
#global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
|
||||
if not global_id:
|
||||
continue
|
||||
if global_id.string_value in diff["deleted"]:
|
||||
|
||||
@@ -18,4 +18,3 @@ class DiffProperties(PropertyGroup):
|
||||
diff_old_file: StringProperty(default="", name="Diff Old IFC File")
|
||||
diff_new_file: StringProperty(default="", name="Diff New IFC File")
|
||||
diff_relationships: StringProperty(default="", name="Diff Relationships")
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.LoadInformation,
|
||||
operator.LoadDocumentReferences,
|
||||
operator.DisableDocumentEditingUI,
|
||||
operator.EnableEditingDocument,
|
||||
operator.DisableEditingDocument,
|
||||
operator.AddInformation,
|
||||
operator.AddDocumentReference,
|
||||
operator.EditInformation,
|
||||
operator.EditDocumentReference,
|
||||
operator.RemoveDocument,
|
||||
operator.EnableAssigningDocument,
|
||||
operator.DisableAssigningDocument,
|
||||
operator.AssignDocument,
|
||||
operator.UnassignDocument,
|
||||
prop.Document,
|
||||
prop.BIMDocumentProperties,
|
||||
prop.BIMObjectDocumentProperties,
|
||||
ui.BIM_PT_documents,
|
||||
ui.BIM_PT_object_documents,
|
||||
ui.BIM_UL_documents,
|
||||
ui.BIM_UL_object_documents,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMDocumentProperties = bpy.props.PointerProperty(type=prop.BIMDocumentProperties)
|
||||
bpy.types.Object.BIMObjectDocumentProperties = bpy.props.PointerProperty(type=prop.BIMObjectDocumentProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMDocumentProperties
|
||||
del bpy.types.Object.BIMObjectDocumentProperties
|
||||
@@ -0,0 +1,15 @@
|
||||
import ifcopenshell
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings={}):
|
||||
self.file = file
|
||||
self.settings = {}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
id_attribute = "DocumentId" if self.file.schema == "IFC2X3" else "Identification"
|
||||
return self.file.create_entity("IfcDocumentInformation", **{
|
||||
id_attribute: ifcopenshell.guid.new(),
|
||||
"Name": "Unnamed"
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
import ifcopenshell
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings={}):
|
||||
self.file = file
|
||||
self.settings = {}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
id_attribute = "ItemReference" if self.file.schema == "IFC2X3" else "Identification"
|
||||
return self.file.create_entity("IfcDocumentReference", **{
|
||||
id_attribute: ifcopenshell.guid.new()
|
||||
})
|
||||
@@ -0,0 +1,41 @@
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": None,
|
||||
"document": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
rel = self.get_document_rel()
|
||||
related_objects = set(rel.RelatedObjects) if rel.RelatedObjects else set()
|
||||
related_objects.add(self.settings["product"])
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
|
||||
def get_document_rel(self):
|
||||
if self.file.schema == "IFC2X3":
|
||||
for rel in self.file.by_type("IfcRelAssociatesDocument"):
|
||||
if rel.RelatingDocument == self.settings["document"]:
|
||||
return rel
|
||||
else:
|
||||
if (
|
||||
hasattr(self.settings["document"], "DocumentRefForObjects")
|
||||
and self.settings["document"].DocumentRefForObjects
|
||||
):
|
||||
return self.settings["document"].DocumentRefForObjects[0]
|
||||
elif (
|
||||
hasattr(self.settings["document"], "DocumentInfoForObjects")
|
||||
and self.settings["document"].DocumentInfoForObjects
|
||||
):
|
||||
return self.settings["document"].DocumentInfoForObjects[0]
|
||||
|
||||
return self.file.create_entity("IfcRelAssociatesDocument", **{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
# TODO: owner history
|
||||
"RelatingDocument": self.settings["document"]
|
||||
})
|
||||
@@ -0,0 +1,59 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.date
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class Data:
|
||||
is_loaded = False
|
||||
products = {}
|
||||
references = {}
|
||||
information = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls, product_id=None):
|
||||
cls._file = IfcStore.get_file()
|
||||
if not cls._file:
|
||||
return
|
||||
if product_id:
|
||||
return cls.load_product(product_id)
|
||||
cls.load_references()
|
||||
cls.load_information()
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def load_product(cls, product_id):
|
||||
product = cls._file.by_id(product_id)
|
||||
cls.products[product_id] = []
|
||||
if not product.HasAssociations:
|
||||
return
|
||||
for association in product.HasAssociations:
|
||||
if association.is_a("IfcRelAssociatesDocument"):
|
||||
cls.products[product_id].append(association.RelatingDocument.id())
|
||||
|
||||
@classmethod
|
||||
def load_information(cls):
|
||||
cls.information = {}
|
||||
for information in cls._file.by_type("IfcDocumentInformation"):
|
||||
data = information.get_info()
|
||||
if cls._file.schema == "IFC2X3":
|
||||
for attribute in ["CreationTime", "LastRevisionTime", "ValidFrom", "ValidUntil"]:
|
||||
if data[attribute]:
|
||||
data[attribute] = ifcopenshell.util.date.ifc2datetime(data[attribute]).isoformat()
|
||||
if data["ElectronicFormat"]:
|
||||
data["ElectronicFormat"] = "{}/{}".format(
|
||||
information.ElectronicFormat.MimeContentType, information.ElectronicFormat.MimeSubtype
|
||||
)
|
||||
cls.information[information.id()] = data
|
||||
|
||||
@classmethod
|
||||
def load_references(cls):
|
||||
cls.references = {}
|
||||
for reference in cls._file.by_type("IfcDocumentReference"):
|
||||
data = reference.get_info()
|
||||
if cls._file.schema == "IFC2X3":
|
||||
if reference.ReferenceToDocument:
|
||||
data["ReferencedDocument"] = reference.ReferenceToDocument[0].id()
|
||||
elif reference.ReferencedDocument:
|
||||
data["ReferencedDocument"] = reference.ReferencedDocument.id()
|
||||
cls.references[reference.id()] = data
|
||||
@@ -0,0 +1,13 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"information": 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["information"], name, value)
|
||||
@@ -0,0 +1,13 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"reference": 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["reference"], name, value)
|
||||
@@ -0,0 +1,258 @@
|
||||
import bpy
|
||||
import json
|
||||
import blenderbim.bim.module.document.add_information as add_information
|
||||
import blenderbim.bim.module.document.add_reference as add_reference
|
||||
import blenderbim.bim.module.document.edit_information as edit_information
|
||||
import blenderbim.bim.module.document.edit_reference as edit_reference
|
||||
import blenderbim.bim.module.document.remove_document as remove_document
|
||||
import blenderbim.bim.module.document.assign_document as assign_document
|
||||
import blenderbim.bim.module.document.unassign_document as unassign_document
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.document.data import Data
|
||||
|
||||
|
||||
class LoadInformation(bpy.types.Operator):
|
||||
bl_idname = "bim.load_information"
|
||||
bl_label = "Load Information"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMDocumentProperties
|
||||
while len(props.documents) > 0:
|
||||
props.documents.remove(0)
|
||||
for information_id, information in Data.information.items():
|
||||
new = props.documents.add()
|
||||
new.name = information["Name"] or "Unnamed"
|
||||
if self.file.schema == "IFC2X3":
|
||||
new.identification = information["DocumentId"] or "*"
|
||||
else:
|
||||
new.identification = information["Identification"] or "*"
|
||||
new.ifc_definition_id = information_id
|
||||
props.is_editing = "information"
|
||||
bpy.ops.bim.disable_editing_document()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LoadDocumentReferences(bpy.types.Operator):
|
||||
bl_idname = "bim.load_document_references"
|
||||
bl_label = "Load Document References"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMDocumentProperties
|
||||
while len(props.documents) > 0:
|
||||
props.documents.remove(0)
|
||||
for reference_id, reference in Data.references.items():
|
||||
new = props.documents.add()
|
||||
new.name = reference["Name"] or "Unnamed"
|
||||
if self.file.schema == "IFC2X3":
|
||||
new.identification = reference["ItemReference"] or "*"
|
||||
else:
|
||||
new.identification = reference["Identification"] or "*"
|
||||
new.ifc_definition_id = reference_id
|
||||
props.is_editing = "reference"
|
||||
bpy.ops.bim.disable_editing_document()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableDocumentEditingUI(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_document_editing_ui"
|
||||
bl_label = "Disable Document Editing UI"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMDocumentProperties.is_editing = ""
|
||||
bpy.ops.bim.disable_editing_document()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingDocument(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_document"
|
||||
bl_label = "Enable Editing Document"
|
||||
document: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMDocumentProperties
|
||||
while len(props.document_attributes) > 0:
|
||||
props.document_attributes.remove(0)
|
||||
|
||||
if props.is_editing == "information":
|
||||
data = Data.information[self.document]
|
||||
ifc_class = "IfcDocumentInformation"
|
||||
elif props.is_editing == "reference":
|
||||
data = Data.references[self.document]
|
||||
ifc_class = "IfcDocumentReference"
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
|
||||
data_type = str(attribute.type_of_attribute)
|
||||
if "<entity" in data_type:
|
||||
continue
|
||||
new = props.document_attributes.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = data[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
if "<string>" in data_type:
|
||||
new.string_value = "" if new.is_null else data[attribute.name()]
|
||||
new.data_type = "string"
|
||||
elif "<enumeration" in data_type:
|
||||
new.enum_items = json.dumps(attribute.type_of_attribute().declared_type().enumeration_items())
|
||||
new.data_type = "enum"
|
||||
if data[attribute.name()]:
|
||||
new.enum_value = data[attribute.name()]
|
||||
props.active_document_id = self.document
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingDocument(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_document"
|
||||
bl_label = "Disable Editing Document"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMDocumentProperties.active_document_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddInformation(bpy.types.Operator):
|
||||
bl_idname = "bim.add_information"
|
||||
bl_label = "Add Information"
|
||||
|
||||
def execute(self, context):
|
||||
result = add_information.Usecase(IfcStore.get_file()).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.load_information()
|
||||
bpy.ops.bim.enable_editing_document(document=result.id())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddDocumentReference(bpy.types.Operator):
|
||||
bl_idname = "bim.add_document_reference"
|
||||
bl_label = "Add Document Reference"
|
||||
|
||||
def execute(self, context):
|
||||
result = add_reference.Usecase(IfcStore.get_file()).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.load_document_references()
|
||||
bpy.ops.bim.enable_editing_document(document=result.id())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditInformation(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_information"
|
||||
bl_label = "Edit Information"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMDocumentProperties
|
||||
attributes = {}
|
||||
for attribute in props.document_attributes:
|
||||
if attribute.is_null:
|
||||
attributes[attribute.name] = None
|
||||
elif attribute.enum_items:
|
||||
attributes[attribute.name] = attribute.enum_value
|
||||
else:
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
self.file = IfcStore.get_file()
|
||||
edit_information.Usecase(
|
||||
self.file, {"information": self.file.by_id(props.active_document_id), "attributes": attributes}
|
||||
).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.load_information()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditDocumentReference(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_document_reference"
|
||||
bl_label = "Edit Document Reference"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMDocumentProperties
|
||||
attributes = {}
|
||||
for attribute in props.document_attributes:
|
||||
if attribute.is_null:
|
||||
attributes[attribute.name] = None
|
||||
else:
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
self.file = IfcStore.get_file()
|
||||
edit_reference.Usecase(
|
||||
self.file, {"reference": self.file.by_id(props.active_document_id), "attributes": attributes}
|
||||
).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.load_document_references()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveDocument(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_document"
|
||||
bl_label = "Remove Document"
|
||||
document: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMDocumentProperties
|
||||
self.file = IfcStore.get_file()
|
||||
remove_document.Usecase(self.file, {"document": self.file.by_id(self.document)}).execute()
|
||||
Data.load()
|
||||
if props.is_editing == "information":
|
||||
bpy.ops.bim.load_information()
|
||||
elif props.is_editing == "reference":
|
||||
bpy.ops.bim.load_document_references()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableAssigningDocument(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_assigning_document"
|
||||
bl_label = "Enable Assigning Document"
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
props = obj.BIMObjectDocumentProperties
|
||||
if props.available_document_types == "IfcDocumentInformation":
|
||||
bpy.ops.bim.load_information()
|
||||
elif props.available_document_types == "IfcDocumentReference":
|
||||
bpy.ops.bim.load_document_references()
|
||||
props.is_adding = props.available_document_types
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableAssigningDocument(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_assigning_document"
|
||||
bl_label = "Disable Assigning Document"
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
props = obj.BIMObjectDocumentProperties
|
||||
props.is_adding = ""
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignDocument(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_document"
|
||||
bl_label = "Assign Document"
|
||||
obj: bpy.props.StringProperty()
|
||||
document: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
assign_document.Usecase(self.file, {
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"document": self.file.by_id(self.document)
|
||||
}).execute()
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class UnassignDocument(bpy.types.Operator):
|
||||
bl_idname = "bim.unassign_document"
|
||||
bl_label = "Unassign Document"
|
||||
obj: bpy.props.StringProperty()
|
||||
document: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
unassign_document.Usecase(self.file, {
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"document": self.file.by_id(self.document)
|
||||
}).execute()
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
@@ -0,0 +1,34 @@
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
class Document(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
identification: StringProperty(name="Identification")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
|
||||
class BIMDocumentProperties(PropertyGroup):
|
||||
document_attributes: CollectionProperty(name="Document Attributes", type=Attribute)
|
||||
active_document_id: IntProperty(name="Active Document Id")
|
||||
documents: CollectionProperty(name="Documents", type=Document)
|
||||
active_document_index: IntProperty(name="Active Document Index")
|
||||
is_editing: StringProperty(name="Is Editing")
|
||||
|
||||
|
||||
class BIMObjectDocumentProperties(PropertyGroup):
|
||||
is_adding: StringProperty(name="Is Adding")
|
||||
available_document_types: EnumProperty(
|
||||
items=[(d, d, "") for d in ["IfcDocumentInformation", "IfcDocumentReference"]], name="Available Document Types"
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {"document": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
self.file.remove(self.settings["document"])
|
||||
for rel in self.file.by_type("IfcRelAssociatesDocument"):
|
||||
if not rel.RelatingDocument:
|
||||
self.file.remove(rel)
|
||||
@@ -0,0 +1,163 @@
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.document.data import Data
|
||||
|
||||
|
||||
class BIM_PT_documents(Panel):
|
||||
bl_label = "IFC Documents"
|
||||
bl_idname = "BIM_PT_documents"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
|
||||
self.props = context.scene.BIMDocumentProperties
|
||||
|
||||
if not self.props.is_editing or self.props.is_editing == "information":
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Documents Found".format(len(Data.information)), icon="FILE")
|
||||
if self.props.is_editing == "information":
|
||||
row.operator("bim.add_information", text="", icon="ADD")
|
||||
row.operator("bim.disable_document_editing_ui", text="", icon="X")
|
||||
else:
|
||||
row.operator("bim.load_information", text="", icon="IMPORT")
|
||||
|
||||
if not self.props.is_editing or self.props.is_editing == "reference":
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} References Found".format(len(Data.references)), icon="FILE_HIDDEN")
|
||||
if self.props.is_editing == "reference":
|
||||
row.operator("bim.add_document_reference", text="", icon="ADD")
|
||||
row.operator("bim.disable_document_editing_ui", text="", icon="X")
|
||||
else:
|
||||
row.operator("bim.load_document_references", text="", icon="IMPORT")
|
||||
|
||||
if self.props.is_editing:
|
||||
self.layout.template_list(
|
||||
"BIM_UL_documents",
|
||||
"",
|
||||
self.props,
|
||||
"documents",
|
||||
self.props,
|
||||
"active_document_index",
|
||||
)
|
||||
|
||||
if self.props.active_document_id:
|
||||
self.draw_editable_ui(context)
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
for attribute in self.props.document_attributes:
|
||||
row = self.layout.row(align=True)
|
||||
if attribute.data_type == "string":
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
elif attribute.data_type == "enum":
|
||||
row.prop(attribute, "enum_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
|
||||
class BIM_PT_object_documents(Panel):
|
||||
bl_label = "IFC Documents"
|
||||
bl_idname = "BIM_PT_object_documents"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
|
||||
def draw(self, context):
|
||||
obj = context.active_object
|
||||
self.oprops = obj.BIMObjectProperties
|
||||
self.sprops = context.scene.BIMDocumentProperties
|
||||
self.props = obj.BIMObjectDocumentProperties
|
||||
self.file = IfcStore.get_file()
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
if self.oprops.ifc_definition_id not in Data.products:
|
||||
Data.load(self.oprops.ifc_definition_id)
|
||||
|
||||
self.draw_add_ui()
|
||||
|
||||
document_ids = Data.products[self.oprops.ifc_definition_id]
|
||||
|
||||
if not document_ids:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No Documents", icon="FILE")
|
||||
|
||||
for document_id in document_ids:
|
||||
try:
|
||||
document = Data.information[document_id]
|
||||
document_type = "IfcDocumentInformation"
|
||||
icon = "FILE"
|
||||
except:
|
||||
document = Data.references[document_id]
|
||||
document_type = "IfcDocumentReference"
|
||||
icon = "FILE_HIDDEN"
|
||||
row = self.layout.row(align=True)
|
||||
if self.file.schema == "IFC2X3":
|
||||
if document_type == "IfcDocumentInformation":
|
||||
row.label(text=document.get("DocumentId") or "*", icon=icon)
|
||||
elif document_type == "IfcDocumentReference":
|
||||
row.label(text=document.get("ItemReference") or "*", icon=icon)
|
||||
else:
|
||||
row.label(text=document.get("Identification") or "*", icon=icon)
|
||||
row.label(text=document.get("Name") or "Unnamed")
|
||||
row.operator("bim.unassign_document", text="", icon="X").document = document_id
|
||||
|
||||
def draw_add_ui(self):
|
||||
if self.props.is_adding:
|
||||
row = self.layout.row(align=True)
|
||||
icon = "FILE" if self.props.is_adding == "IfcDocumentInformation" else "FILE_HIDDEN"
|
||||
row.label(text="Adding {}".format(self.props.is_adding), icon=icon)
|
||||
row.operator("bim.disable_assigning_document", text="", icon="X")
|
||||
self.layout.template_list(
|
||||
"BIM_UL_object_documents",
|
||||
"",
|
||||
self.sprops,
|
||||
"documents",
|
||||
self.sprops,
|
||||
"active_document_index",
|
||||
)
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "available_document_types", text="")
|
||||
row.operator("bim.enable_assigning_document", text="", icon="ADD")
|
||||
|
||||
|
||||
class BIM_UL_documents(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.identification)
|
||||
row.label(text=item.name)
|
||||
if context.scene.BIMDocumentProperties.active_document_id == item.ifc_definition_id:
|
||||
if context.scene.BIMDocumentProperties.is_editing == "information":
|
||||
row.operator("bim.edit_information", text="", icon="CHECKMARK")
|
||||
elif context.scene.BIMDocumentProperties.is_editing == "reference":
|
||||
row.operator("bim.edit_document_reference", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_document", text="", icon="X")
|
||||
elif context.scene.BIMDocumentProperties.active_document_id:
|
||||
row.operator("bim.remove_document", text="", icon="X").document = item.ifc_definition_id
|
||||
else:
|
||||
op = row.operator("bim.enable_editing_document", text="", icon="GREASEPENCIL")
|
||||
op.document = item.ifc_definition_id
|
||||
row.operator("bim.remove_document", text="", icon="X").document = item.ifc_definition_id
|
||||
|
||||
|
||||
class BIM_UL_object_documents(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.identification)
|
||||
row.label(text=item.name)
|
||||
row.operator("bim.assign_document", text="", icon="ADD").document = item.ifc_definition_id
|
||||
@@ -0,0 +1,17 @@
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": None,
|
||||
"document": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for rel in self.settings["product"].HasAssociations:
|
||||
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"]:
|
||||
self.file.remove(rel)
|
||||
@@ -1,22 +1,27 @@
|
||||
import bpy
|
||||
from . import ui, operator
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.EditObjectPlacement,
|
||||
operator.AddRepresentation,
|
||||
operator.MapRepresentation,
|
||||
operator.SwitchRepresentation,
|
||||
operator.RemoveRepresentation,
|
||||
operator.UpdateMeshRepresentation,
|
||||
operator.UpdateParametricRepresentation,
|
||||
operator.GetRepresentationIfcParameters,
|
||||
prop.BIMGeometryProperties,
|
||||
ui.BIM_PT_representations,
|
||||
ui.BIM_PT_mesh,
|
||||
ui.BIM_PT_workarounds,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMGeometryProperties = bpy.props.PointerProperty(type=prop.BIMGeometryProperties)
|
||||
bpy.types.OBJECT_PT_transform.append(ui.BIM_PT_transform)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.types.OBJECT_PT_transform.remove(ui.BIM_PT_transform)
|
||||
del bpy.types.Scene.BIMGeometryProperties
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import bpy
|
||||
import bmesh
|
||||
import ifcopenshell.util.unit
|
||||
|
||||
|
||||
@@ -7,10 +9,12 @@ class Usecase:
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"context": None, # IfcGeometricRepresentationContext
|
||||
"blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now
|
||||
"geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now
|
||||
"total_items": 1, # How many representation items to create
|
||||
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
|
||||
"should_force_faceted_brep": False, # If we should force faceted breps for meshes
|
||||
"should_force_triangulation": False, # If we should force triangulation for meshes
|
||||
"is_wireframe": False, # If the geometry is a wireframe
|
||||
"is_curve": False, # If the geometry is a Blender curve
|
||||
"is_point_cloud": False, # If the geometry is a point cloud
|
||||
@@ -20,6 +24,7 @@ class Usecase:
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
self.evaluate_geometry()
|
||||
if self.settings["unit_scale"] is None:
|
||||
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||
if self.settings["context"].ContextType == "Model":
|
||||
@@ -28,6 +33,34 @@ class Usecase:
|
||||
return self.create_plan_representation()
|
||||
return self.create_variable_representation()
|
||||
|
||||
def evaluate_geometry(self):
|
||||
self.boolean_modifiers = []
|
||||
for modifier in self.settings["blender_object"].modifiers:
|
||||
if not modifier.type == "BOOLEAN":
|
||||
continue
|
||||
modifier_data = {}
|
||||
for name in ["operation", "operand_type", "object", "solver", "use_self"]:
|
||||
modifier_data[name] = getattr(modifier, name)
|
||||
self.boolean_modifiers.append(modifier_data)
|
||||
self.settings["blender_object"].modifiers.remove(modifier)
|
||||
|
||||
if self.settings["should_force_triangulation"]:
|
||||
mesh = self.settings["blender_object"].evaluated_get(bpy.context.evaluated_depsgraph_get()).to_mesh()
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
del bm
|
||||
self.settings["geometry"] = mesh
|
||||
else:
|
||||
self.settings["geometry"] = self.settings["blender_object"].evaluated_get(bpy.context.evaluated_depsgraph_get()).to_mesh()
|
||||
|
||||
for modifier in self.boolean_modifiers:
|
||||
new = self.settings["blender_object"].modifiers.new("IfcOpeningElement", "BOOLEAN")
|
||||
for key, value in modifier.items():
|
||||
setattr(new, key, value)
|
||||
|
||||
def create_model_representation(self):
|
||||
if self.settings["context"].is_a() == "IfcGeometricRepresentationContext":
|
||||
return self.create_variable_representation()
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": None,
|
||||
"representation": None
|
||||
}
|
||||
self.settings = {"product": None, "representation": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
definition = self.settings["product"].Representation
|
||||
if not definition:
|
||||
definition = self.file.createIfcProductDefinitionShape()
|
||||
self.settings["product"].Representation = definition
|
||||
representations = list(definition.Representations) if definition.Representations else []
|
||||
representations.append(self.settings["representation"])
|
||||
definition.Representations = representations
|
||||
if self.settings["product"].is_a("IfcProduct"):
|
||||
definition = self.settings["product"].Representation
|
||||
if not definition:
|
||||
definition = self.file.createIfcProductDefinitionShape()
|
||||
self.settings["product"].Representation = definition
|
||||
representations = list(definition.Representations) if definition.Representations else []
|
||||
representations.append(self.settings["representation"])
|
||||
definition.Representations = representations
|
||||
elif self.settings["product"].is_a("IfcTypeProduct"):
|
||||
if self.settings["product"].RepresentationMaps:
|
||||
maps = list(self.settings["product"].RepresentationMaps)
|
||||
else:
|
||||
maps = []
|
||||
self.zero = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))
|
||||
self.x_axis = self.file.createIfcDirection((1.0, 0.0, 0.0))
|
||||
self.z_axis = self.file.createIfcDirection((0.0, 0.0, 1.0))
|
||||
maps.append(
|
||||
self.file.create_entity(
|
||||
"IfcRepresentationMap",
|
||||
**{
|
||||
"MappingOrigin": self.file.createIfcAxis2Placement3D(self.zero, self.z_axis, self.x_axis),
|
||||
"MappedRepresentation": self.settings["representation"],
|
||||
}
|
||||
)
|
||||
)
|
||||
self.settings["product"].RepresentationMaps = maps
|
||||
|
||||
@@ -39,7 +39,11 @@ class Usecase:
|
||||
ifcopenshell.util.element.replace_attribute(
|
||||
inverse, self.settings["product"].ObjectPlacement, placement
|
||||
)
|
||||
self.file.remove(self.settings["product"].ObjectPlacement)
|
||||
old = self.settings["product"].ObjectPlacement
|
||||
old.PlacementRelTo = None
|
||||
self.settings["product"].ObjectPlacement = None
|
||||
if not self.file.get_inverse(old):
|
||||
ifcopenshell.util.element.remove_deep(self.file, old)
|
||||
self.settings["product"].ObjectPlacement = placement
|
||||
|
||||
for settings in dependent_objects:
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": None,
|
||||
"representation": None,
|
||||
}
|
||||
self.ifc_vertices = []
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
self.zero = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))
|
||||
self.x_axis = self.file.createIfcDirection((1.0, 0.0, 0.0))
|
||||
self.y_axis = self.file.createIfcDirection((0.0, 1.0, 0.0))
|
||||
self.z_axis = self.file.createIfcDirection((0.0, 0.0, 1.0))
|
||||
|
||||
self.get_mapping_source()
|
||||
|
||||
mapping_target = self.file.createIfcCartesianTransformationOperator3D(
|
||||
self.x_axis, self.y_axis, self.zero, 1, self.z_axis
|
||||
)
|
||||
mapped_item = self.file.createIfcMappedItem(self.get_mapping_source(), mapping_target)
|
||||
return self.file.create_entity(
|
||||
"IfcShapeRepresentation",
|
||||
**{
|
||||
"ContextOfItems": self.settings["representation"].ContextOfItems,
|
||||
"RepresentationIdentifier": self.settings["representation"].RepresentationIdentifier,
|
||||
"RepresentationType": "MappedRepresentation",
|
||||
"Items": [mapped_item],
|
||||
}
|
||||
)
|
||||
|
||||
def get_mapping_source(self):
|
||||
if self.settings["representation"].RepresentationMap:
|
||||
return self.settings["representation"].RepresentationMap[0]
|
||||
return self.file.create_entity(
|
||||
"IfcRepresentationMap",
|
||||
**{
|
||||
"MappingOrigin": self.file.createIfcAxis2Placement3D(self.zero, self.z_axis, self.x_axis),
|
||||
"MappedRepresentation": self.settings["representation"],
|
||||
}
|
||||
)
|
||||
@@ -4,6 +4,7 @@ import ifcopenshell
|
||||
import logging
|
||||
import blenderbim.bim.module.geometry.edit_object_placement as edit_object_placement
|
||||
import blenderbim.bim.module.geometry.add_representation as add_representation
|
||||
import blenderbim.bim.module.geometry.map_representation as map_representation
|
||||
import blenderbim.bim.module.geometry.assign_styles as assign_styles
|
||||
import blenderbim.bim.module.geometry.assign_representation as assign_representation
|
||||
import blenderbim.bim.module.geometry.remove_representation as remove_representation
|
||||
@@ -18,31 +19,34 @@ class EditObjectPlacement(bpy.types.Operator):
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||
objs = [bpy.data.objects.get(self.obj)] if self.obj else bpy.context.selected_objects
|
||||
self.file = IfcStore.get_file()
|
||||
# TODO: determine how to deal with this module dependency
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
matrix = np.array(obj.matrix_world)
|
||||
if props.has_blender_offset and props.blender_offset_type == "OBJECT_PLACEMENT":
|
||||
self.calculate_unit_scale()
|
||||
# TODO: np.array? Why not matrix?
|
||||
matrix = np.array(
|
||||
ifcopenshell.util.geolocation.local2global(
|
||||
np.matrix(obj.matrix_world),
|
||||
float(props.blender_eastings) * self.unit_scale,
|
||||
float(props.blender_northings) * self.unit_scale,
|
||||
float(props.blender_orthogonal_height) * self.unit_scale,
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
for obj in objs:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
matrix = np.array(obj.matrix_world)
|
||||
if props.has_blender_offset and props.blender_offset_type == "OBJECT_PLACEMENT":
|
||||
self.calculate_unit_scale()
|
||||
# TODO: np.array? Why not matrix?
|
||||
matrix = np.array(
|
||||
ifcopenshell.util.geolocation.local2global(
|
||||
np.matrix(obj.matrix_world),
|
||||
float(props.blender_eastings) * self.unit_scale,
|
||||
float(props.blender_northings) * self.unit_scale,
|
||||
float(props.blender_orthogonal_height) * self.unit_scale,
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
)
|
||||
)
|
||||
)
|
||||
edit_object_placement.Usecase(
|
||||
self.file,
|
||||
{
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"matrix": matrix,
|
||||
},
|
||||
).execute()
|
||||
edit_object_placement.Usecase(
|
||||
self.file,
|
||||
{
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"matrix": matrix,
|
||||
},
|
||||
).execute()
|
||||
return {"FINISHED"}
|
||||
|
||||
def calculate_unit_scale(self):
|
||||
@@ -76,8 +80,11 @@ class AddRepresentation(bpy.types.Operator):
|
||||
self.file,
|
||||
{
|
||||
"context": self.file.by_id(context_id),
|
||||
"blender_object": obj,
|
||||
"geometry": obj.data,
|
||||
"total_items": max(1, len(obj.material_slots)),
|
||||
"should_force_faceted_brep": context.scene.BIMGeometryProperties.should_force_faceted_brep,
|
||||
"should_force_triangulation": context.scene.BIMGeometryProperties.should_force_triangulation,
|
||||
},
|
||||
).execute()
|
||||
if not result:
|
||||
@@ -92,6 +99,7 @@ class AddRepresentation(bpy.types.Operator):
|
||||
for s in obj.material_slots
|
||||
if s.material
|
||||
],
|
||||
"should_use_presentation_style_assignment": context.scene.BIMGeometryProperties.should_use_presentation_style_assignment,
|
||||
},
|
||||
).execute()
|
||||
assign_representation.Usecase(
|
||||
@@ -167,50 +175,90 @@ class RemoveRepresentation(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class MapRepresentation(bpy.types.Operator):
|
||||
bl_idname = "bim.map_representation"
|
||||
bl_label = "Map Representation"
|
||||
obj: bpy.props.StringProperty()
|
||||
obj_data: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
objs = [bpy.data.objects.get(self.obj)] if self.obj else bpy.context.selected_objects
|
||||
obj_data = bpy.data.meshes.get(self.obj_data) if self.obj_data else bpy.context.active_object.data
|
||||
objs = [o for o in objs if o.data != obj_data]
|
||||
self.file = IfcStore.get_file()
|
||||
|
||||
for obj in objs:
|
||||
bpy.ops.bim.edit_object_placement(obj=obj.name)
|
||||
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
if obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
old_representation = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
else:
|
||||
old_representation = None
|
||||
target_representation = self.file.by_id(obj_data.BIMMeshProperties.ifc_definition_id)
|
||||
obj.data = obj_data
|
||||
result = map_representation.Usecase(
|
||||
self.file,
|
||||
{
|
||||
"product": product,
|
||||
"representation": target_representation,
|
||||
},
|
||||
).execute()
|
||||
assign_representation.Usecase(self.file, {"product": product, "representation": result}).execute()
|
||||
if old_representation:
|
||||
bpy.ops.bim.remove_representation(ifc_definition_id=old_representation.id())
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class UpdateMeshRepresentation(bpy.types.Operator):
|
||||
bl_idname = "bim.update_mesh_representation"
|
||||
bl_label = "Update Mesh Representation"
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||
objs = [bpy.data.objects.get(self.obj)] if self.obj else bpy.context.selected_objects
|
||||
self.file = IfcStore.get_file()
|
||||
|
||||
bpy.ops.bim.edit_object_placement(obj=obj.name)
|
||||
for obj in objs:
|
||||
bpy.ops.bim.edit_object_placement(obj=obj.name)
|
||||
|
||||
old_representation = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
new_representation = add_representation.Usecase(
|
||||
self.file,
|
||||
{
|
||||
"context": old_representation.ContextOfItems,
|
||||
"geometry": obj.data,
|
||||
"total_items": max(1, len(obj.material_slots)),
|
||||
},
|
||||
).execute()
|
||||
if not new_representation:
|
||||
print("Failed to write shape representation")
|
||||
return {"FINISHED"}
|
||||
old_representation = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
new_representation = add_representation.Usecase(
|
||||
self.file,
|
||||
{
|
||||
"context": old_representation.ContextOfItems,
|
||||
"blender_object": obj,
|
||||
"geometry": obj.data,
|
||||
"total_items": max(1, len(obj.material_slots)),
|
||||
"should_force_faceted_brep": context.scene.BIMGeometryProperties.should_force_faceted_brep,
|
||||
"should_force_triangulation": context.scene.BIMGeometryProperties.should_force_triangulation,
|
||||
},
|
||||
).execute()
|
||||
if not new_representation:
|
||||
print("Failed to write shape representation")
|
||||
return {"FINISHED"}
|
||||
|
||||
assign_styles.Usecase(
|
||||
self.file,
|
||||
{
|
||||
"shape_representation": new_representation,
|
||||
"styles": [
|
||||
self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id)
|
||||
for s in obj.material_slots
|
||||
if s.material
|
||||
],
|
||||
},
|
||||
).execute()
|
||||
assign_styles.Usecase(
|
||||
self.file,
|
||||
{
|
||||
"shape_representation": new_representation,
|
||||
"styles": [
|
||||
self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id)
|
||||
for s in obj.material_slots
|
||||
if s.material
|
||||
],
|
||||
"should_use_presentation_style_assignment": context.scene.BIMGeometryProperties.should_use_presentation_style_assignment,
|
||||
},
|
||||
).execute()
|
||||
|
||||
# TODO: move this into a replace_representation usecase or something
|
||||
for inverse in self.file.get_inverse(old_representation):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_representation, new_representation)
|
||||
# TODO: move this into a replace_representation usecase or something
|
||||
for inverse in self.file.get_inverse(old_representation):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_representation, new_representation)
|
||||
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = int(new_representation.id())
|
||||
obj.data.name = f"{old_representation.ContextOfItems.id()}/{new_representation.id()}"
|
||||
bpy.ops.bim.remove_representation(ifc_definition_id=old_representation.id())
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = int(new_representation.id())
|
||||
obj.data.name = f"{old_representation.ContextOfItems.id()}/{new_representation.id()}"
|
||||
bpy.ops.bim.remove_representation(ifc_definition_id=old_representation.id())
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
class BIMGeometryProperties(PropertyGroup):
|
||||
# Revit workaround
|
||||
should_use_presentation_style_assignment: BoolProperty(name="Force Presentation Style Assignment", default=False)
|
||||
# RIB iTwo, DESITE BIM workaround
|
||||
should_force_faceted_brep: BoolProperty(name="Force Faceted Breps", default=False)
|
||||
# Navisworks workaround
|
||||
should_force_triangulation: BoolProperty(name="Force Triangulation", default=False)
|
||||
@@ -1,3 +1,5 @@
|
||||
import ifcopenshell.util.element
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
@@ -6,17 +8,29 @@ class Usecase:
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
styles = []
|
||||
dummy_context = self.file.create_entity("IfcRepresentationContext")
|
||||
for subelement in self.file.traverse(self.settings["representation"]):
|
||||
if subelement.is_a("IfcRepresentationItem") and subelement.StyledByItem:
|
||||
styles.append(subelement)
|
||||
for style in styles:
|
||||
self.remove_deep(style)
|
||||
self.remove_deep(self.settings["representation"])
|
||||
[self.file.remove(s) for s in subelement.StyledByItem]
|
||||
elif subelement.is_a("IfcRepresentation"):
|
||||
subelement.ContextOfItems = dummy_context
|
||||
self.purge_representation_inverses(subelement)
|
||||
self.purge_representation_inverses(self.settings["representation"])
|
||||
ifcopenshell.util.element.remove_deep(self.file, self.settings["representation"])
|
||||
|
||||
def remove_deep(self, element):
|
||||
subgraph = list(self.file.traverse(element))
|
||||
subgraph_set = set(subgraph)
|
||||
for ref in subgraph[::-1]:
|
||||
if ref.id() and len(set(self.file.get_inverse(ref)) - subgraph_set) == 0:
|
||||
self.file.remove(ref)
|
||||
def purge_representation_inverses(self, element):
|
||||
for inverse in self.file.get_inverse(element):
|
||||
if inverse.is_a("IfcPresentationLayerAssignment"):
|
||||
assigned_items = set(inverse.AssignedItems)
|
||||
if len(assigned_items) == 1:
|
||||
self.file.remove(inverse)
|
||||
else:
|
||||
assigned_items.remove(element)
|
||||
inverse.AssignedItems == list(assigned_items)
|
||||
elif inverse.is_a("IfcProductRepresentation"):
|
||||
representations = set(inverse.Representations)
|
||||
if len(representations) == 1:
|
||||
self.file.remove(inverse)
|
||||
else:
|
||||
representations.remove(element)
|
||||
inverse.Representations = list(representations)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import bpy
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.module.geometry.data import Data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_PT_representations(Panel):
|
||||
@@ -11,6 +12,10 @@ class BIM_PT_representations(Panel):
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.active_object.BIMObjectProperties
|
||||
@@ -50,6 +55,7 @@ class BIM_PT_mesh(Panel):
|
||||
context.active_object is not None
|
||||
and context.active_object.type == "MESH"
|
||||
and hasattr(context.active_object.data, "BIMMeshProperties")
|
||||
and context.active_object.data.BIMMeshProperties.ifc_definition_id
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
@@ -62,6 +68,8 @@ class BIM_PT_mesh(Panel):
|
||||
row.operator("bim.get_representation_ifc_parameters")
|
||||
row = layout.row()
|
||||
row.operator("bim.update_mesh_representation")
|
||||
row = layout.row()
|
||||
row.operator("bim.map_representation")
|
||||
for index, ifc_parameter in enumerate(props.ifc_parameters):
|
||||
row = layout.row(align=True)
|
||||
row.prop(ifc_parameter, "name", text="")
|
||||
@@ -73,3 +81,30 @@ def BIM_PT_transform(self, context):
|
||||
if context.active_object and context.active_object.BIMObjectProperties.ifc_definition_id:
|
||||
row = self.layout.row()
|
||||
row.operator("bim.edit_object_placement")
|
||||
|
||||
|
||||
class BIM_PT_workarounds(Panel):
|
||||
bl_label = "IFC Vendor Workarounds"
|
||||
bl_idname = "BIM_PT_workarounds"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "data"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
context.active_object is not None
|
||||
and context.active_object.type == "MESH"
|
||||
and hasattr(context.active_object.data, "BIMMeshProperties")
|
||||
and context.active_object.data.BIMMeshProperties.ifc_definition_id
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMGeometryProperties
|
||||
row = self.layout.row()
|
||||
row.prop(props, "should_force_faceted_brep")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "should_force_triangulation")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "should_use_presentation_style_assignment")
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.LoadLayers,
|
||||
operator.DisableLayerEditingUI,
|
||||
operator.EnableEditingLayer,
|
||||
operator.DisableEditingLayer,
|
||||
operator.AddPresentationLayer,
|
||||
operator.EditPresentationLayer,
|
||||
operator.RemovePresentationLayer,
|
||||
operator.AssignPresentationLayer,
|
||||
operator.UnassignPresentationLayer,
|
||||
prop.Layer,
|
||||
prop.BIMLayerProperties,
|
||||
ui.BIM_PT_layers,
|
||||
ui.BIM_UL_layers,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMLayerProperties = bpy.props.PointerProperty(type=prop.BIMLayerProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMLayerProperties
|
||||
@@ -0,0 +1,13 @@
|
||||
import ifcopenshell
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings={}):
|
||||
self.file = file
|
||||
self.settings = {}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
return self.file.create_entity("IfcPresentationLayerAssignment", **{
|
||||
"Name": "Unnamed"
|
||||
})
|
||||
@@ -0,0 +1,17 @@
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"item": None,
|
||||
"layer": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
assigned_items = set(self.settings["layer"].AssignedItems) or set()
|
||||
assigned_items.add(self.settings["item"])
|
||||
self.settings["layer"].AssignedItems = list(assigned_items)
|
||||
@@ -0,0 +1,30 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.date
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class Data:
|
||||
is_loaded = False
|
||||
items = {}
|
||||
layers = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls, item_id=None):
|
||||
cls._file = IfcStore.get_file()
|
||||
if not cls._file:
|
||||
return
|
||||
cls.load_layers()
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def load_layers(cls):
|
||||
cls.layers = {}
|
||||
cls.items = {}
|
||||
for layer in cls._file.by_type("IfcPresentationLayerAssignment"):
|
||||
data = layer.get_info()
|
||||
if layer.AssignedItems:
|
||||
for item in layer.AssignedItems:
|
||||
cls.items.setdefault(item.id(), []).append(layer.id())
|
||||
del data["AssignedItems"]
|
||||
cls.layers[layer.id()] = data
|
||||
@@ -0,0 +1,13 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"layer": 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["layer"], name, value)
|
||||
@@ -0,0 +1,151 @@
|
||||
import bpy
|
||||
import json
|
||||
import blenderbim.bim.module.layer.add_layer as add_layer
|
||||
import blenderbim.bim.module.layer.edit_layer as edit_layer
|
||||
import blenderbim.bim.module.layer.remove_layer as remove_layer
|
||||
import blenderbim.bim.module.layer.assign_layer as assign_layer
|
||||
import blenderbim.bim.module.layer.unassign_layer as unassign_layer
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.layer.data import Data
|
||||
|
||||
|
||||
class LoadLayers(bpy.types.Operator):
|
||||
bl_idname = "bim.load_layers"
|
||||
bl_label = "Load Layers"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMLayerProperties
|
||||
while len(props.layers) > 0:
|
||||
props.layers.remove(0)
|
||||
for layer_id, layer in Data.layers.items():
|
||||
new = props.layers.add()
|
||||
new.name = layer["Name"] or "Unnamed"
|
||||
new.ifc_definition_id = layer_id
|
||||
props.is_editing = True
|
||||
bpy.ops.bim.disable_editing_layer()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableLayerEditingUI(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_layer_editing_ui"
|
||||
bl_label = "Disable Layer Editing UI"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMLayerProperties.is_editing = False
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingLayer(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_layer"
|
||||
bl_label = "Enable Editing Layer"
|
||||
layer: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMLayerProperties
|
||||
while len(props.layer_attributes) > 0:
|
||||
props.layer_attributes.remove(0)
|
||||
|
||||
data = Data.layers[self.layer]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcPresentationLayerAssignment").all_attributes():
|
||||
data_type = str(attribute.type_of_attribute)
|
||||
if "<entity" in data_type:
|
||||
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"}
|
||||
|
||||
|
||||
class DisableEditingLayer(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_layer"
|
||||
bl_label = "Disable Editing Layer"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMLayerProperties.active_layer_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddPresentationLayer(bpy.types.Operator):
|
||||
bl_idname = "bim.add_presentation_layer"
|
||||
bl_label = "Add Layer"
|
||||
|
||||
def execute(self, context):
|
||||
result = add_layer.Usecase(IfcStore.get_file()).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.load_layers()
|
||||
bpy.ops.bim.enable_editing_layer(layer=result.id())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditPresentationLayer(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_presentation_layer"
|
||||
bl_label = "Edit Layer"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMLayerProperties
|
||||
attributes = {}
|
||||
for attribute in props.layer_attributes:
|
||||
if attribute.is_null:
|
||||
attributes[attribute.name] = None
|
||||
else:
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
self.file = IfcStore.get_file()
|
||||
edit_layer.Usecase(
|
||||
self.file, {"layer": self.file.by_id(props.active_layer_id), "attributes": attributes}
|
||||
).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.load_layers()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemovePresentationLayer(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_presentation_layer"
|
||||
bl_label = "Remove Presentation Layer"
|
||||
layer: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMLayerProperties
|
||||
self.file = IfcStore.get_file()
|
||||
remove_layer.Usecase(self.file, {"layer": self.file.by_id(self.layer)}).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.load_layers()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignPresentationLayer(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_presentation_layer"
|
||||
bl_label = "Assign Presentation Layer"
|
||||
item: bpy.props.StringProperty()
|
||||
layer: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
item = bpy.data.meshes.get(self.item) if self.item else context.active_object.data
|
||||
self.file = IfcStore.get_file()
|
||||
assign_layer.Usecase(self.file, {
|
||||
"item": self.file.by_id(item.BIMMeshProperties.ifc_definition_id),
|
||||
"layer": self.file.by_id(self.layer)
|
||||
}).execute()
|
||||
Data.load()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class UnassignPresentationLayer(bpy.types.Operator):
|
||||
bl_idname = "bim.unassign_presentation_layer"
|
||||
bl_label = "Unassign Presentation Layer"
|
||||
item: bpy.props.StringProperty()
|
||||
layer: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
item = bpy.data.meshes.get(self.item) if self.item else context.active_object.data
|
||||
self.file = IfcStore.get_file()
|
||||
unassign_layer.Usecase(self.file, {
|
||||
"item": self.file.by_id(item.BIMMeshProperties.ifc_definition_id),
|
||||
"layer": self.file.by_id(self.layer)
|
||||
}).execute()
|
||||
Data.load()
|
||||
return {"FINISHED"}
|
||||
@@ -0,0 +1,26 @@
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
class Layer(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
|
||||
class BIMLayerProperties(PropertyGroup):
|
||||
layer_attributes: CollectionProperty(name="Layer Attributes", type=Attribute)
|
||||
active_layer_id: IntProperty(name="Active Layer Id")
|
||||
layers: CollectionProperty(name="Layers", type=Layer)
|
||||
active_layer_index: IntProperty(name="Active Layer Index")
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
@@ -0,0 +1,9 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {"layer": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
self.file.remove(self.settings["layer"])
|
||||
@@ -0,0 +1,86 @@
|
||||
from bpy.types import Panel, UIList, Mesh
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.layer.data import Data
|
||||
|
||||
|
||||
class BIM_PT_layers(Panel):
|
||||
bl_label = "IFC Presentation Layers"
|
||||
bl_idname = "BIM_PT_layers"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
|
||||
self.props = context.scene.BIMLayerProperties
|
||||
|
||||
if Data.layers:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Layers Found".format(len(Data.layers.keys())))
|
||||
if self.props.is_editing:
|
||||
row.operator("bim.add_presentation_layer", text="", icon="ADD")
|
||||
row.operator("bim.disable_layer_editing_ui", text="", icon="X")
|
||||
else:
|
||||
row.operator("bim.load_layers", text="", icon="IMPORT")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No Layers")
|
||||
|
||||
if self.props.is_editing:
|
||||
self.layout.template_list(
|
||||
"BIM_UL_layers",
|
||||
"",
|
||||
self.props,
|
||||
"layers",
|
||||
self.props,
|
||||
"active_layer_index",
|
||||
)
|
||||
|
||||
if self.props.active_layer_id:
|
||||
self.draw_editable_ui(context)
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
for attribute in self.props.layer_attributes:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
|
||||
class BIM_UL_layers(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 and isinstance(context.active_object.data, Mesh):
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
if (
|
||||
mprops.ifc_definition_id in Data.items
|
||||
and item.ifc_definition_id in Data.items[mprops.ifc_definition_id]
|
||||
):
|
||||
op = row.operator("bim.unassign_presentation_layer", text="", icon="KEYFRAME_HLT", emboss=False)
|
||||
op.layer = item.ifc_definition_id
|
||||
else:
|
||||
op = row.operator("bim.assign_presentation_layer", text="", icon="KEYFRAME", emboss=False)
|
||||
op.layer = item.ifc_definition_id
|
||||
|
||||
row.operator("bim.disable_editing_layer", text="", icon="HIDE_OFF", emboss=False)
|
||||
row.operator("bim.disable_editing_layer", text="", icon="FREEZE", emboss=False)
|
||||
|
||||
if context.scene.BIMLayerProperties.active_layer_id == item.ifc_definition_id:
|
||||
row.operator("bim.edit_presentation_layer", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_layer", text="", icon="X")
|
||||
elif context.scene.BIMLayerProperties.active_layer_id:
|
||||
row.operator("bim.remove_presentation_layer", text="", icon="X").layer = item.ifc_definition_id
|
||||
else:
|
||||
op = row.operator("bim.enable_editing_layer", text="", icon="GREASEPENCIL")
|
||||
op.layer = item.ifc_definition_id
|
||||
row.operator("bim.remove_presentation_layer", text="", icon="X").layer = item.ifc_definition_id
|
||||
@@ -0,0 +1,17 @@
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"item": None,
|
||||
"layer": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
assigned_items = set(self.settings["layer"].AssignedItems) or set()
|
||||
assigned_items.remove(self.settings["item"])
|
||||
self.settings["layer"].AssignedItems = list(assigned_items)
|
||||
@@ -2,6 +2,8 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddMaterial,
|
||||
operator.RemoveMaterial,
|
||||
operator.AssignMaterial,
|
||||
operator.UnassignMaterial,
|
||||
operator.AddConstituent,
|
||||
@@ -17,6 +19,7 @@ classes = (
|
||||
operator.DisableEditingMaterialSetItem,
|
||||
operator.EditMaterialSetItem,
|
||||
prop.BIMObjectMaterialProperties,
|
||||
ui.BIM_PT_material,
|
||||
ui.BIM_PT_object_material,
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {"Name": "Unnamed"}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
return self.file.create_entity("IfcMaterial", **{"Name": self.settings["Name"] or "Unnamed"})
|
||||
@@ -31,25 +31,33 @@ class Data:
|
||||
|
||||
@classmethod
|
||||
def load_materials(cls):
|
||||
cls.materials = {}
|
||||
cls.load_element("IfcMaterial", cls.materials)
|
||||
|
||||
@classmethod
|
||||
def load_constituents(cls):
|
||||
cls.constituent_sets = {}
|
||||
cls.constituents = {}
|
||||
cls.load_element("IfcMaterialConstituent", cls.constituents)
|
||||
cls.load_element("IfcMaterialConstituentSet", cls.constituent_sets)
|
||||
|
||||
@classmethod
|
||||
def load_layers(cls):
|
||||
cls.layer_sets = {}
|
||||
cls.layers = {}
|
||||
cls.load_element("IfcMaterialLayer", cls.layers)
|
||||
cls.load_element("IfcMaterialLayerSet", cls.layer_sets)
|
||||
|
||||
@classmethod
|
||||
def load_profiles(cls):
|
||||
cls.profile_sets = {}
|
||||
cls.profiles = {}
|
||||
cls.load_element("IfcMaterialProfile", cls.profiles)
|
||||
cls.load_element("IfcMaterialProfileSet", cls.profile_sets)
|
||||
|
||||
@classmethod
|
||||
def load_lists(cls):
|
||||
cls.lists = {}
|
||||
cls.load_element("IfcMaterialList", cls.lists)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import bpy
|
||||
import blenderbim.bim.module.material.add_material as add_material
|
||||
import blenderbim.bim.module.material.remove_material as remove_material
|
||||
import blenderbim.bim.module.material.assign_material as assign_material
|
||||
import blenderbim.bim.module.material.unassign_material as unassign_material
|
||||
import blenderbim.bim.module.material.add_constituent as add_constituent
|
||||
@@ -13,6 +15,36 @@ from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.material.data import Data
|
||||
|
||||
|
||||
class AddMaterial(bpy.types.Operator):
|
||||
bl_idname = "bim.add_material"
|
||||
bl_label = "Add Material"
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.materials.get(self.obj) if self.obj else bpy.context.active_object.active_material
|
||||
self.file = IfcStore.get_file()
|
||||
result = add_material.Usecase(self.file, {"Name": obj.name}).execute()
|
||||
obj.BIMObjectProperties.ifc_definition_id = result.id()
|
||||
Data.load()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveMaterial(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_material"
|
||||
bl_label = "Remove Material"
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.materials.get(self.obj) if self.obj else bpy.context.active_object.active_material
|
||||
self.file = IfcStore.get_file()
|
||||
result = remove_material.Usecase(
|
||||
self.file, {"material": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)}
|
||||
).execute()
|
||||
obj.BIMObjectProperties.ifc_definition_id = 0
|
||||
Data.load()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignMaterial(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_material"
|
||||
bl_label = "Assign Material"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {"material": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
inverse_elements = self.file.get_inverse(self.settings["material"])
|
||||
self.file.remove(self.settings["material"])
|
||||
# TODO: this is probably not robust enough
|
||||
for inverse in inverse_elements:
|
||||
if inverse.is_a("IfcMaterialConstituent"):
|
||||
self.file.remove(inverse)
|
||||
elif inverse.is_a("IfcMaterialLayer"):
|
||||
self.file.remove(inverse)
|
||||
elif inverse.is_a("IfcMaterialProfile"):
|
||||
self.file.remove(inverse)
|
||||
elif inverse.is_a("IfcRelAssociatesMaterial"):
|
||||
self.file.remove(inverse)
|
||||
@@ -3,6 +3,21 @@ from blenderbim.bim.module.material.data import Data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_PT_material(Panel):
|
||||
bl_label = "IFC Material"
|
||||
bl_idname = "BIM_PT_material"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "material"
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout.row()
|
||||
if bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id):
|
||||
row.operator("bim.remove_material", icon="X", text="Remove IFC Material")
|
||||
else:
|
||||
row.operator("bim.add_material", icon="ADD", text="Create IFC Material")
|
||||
|
||||
|
||||
class BIM_PT_object_material(Panel):
|
||||
bl_label = "IFC Object Material"
|
||||
bl_idname = "BIM_PT_object_material"
|
||||
@@ -12,7 +27,12 @@ class BIM_PT_object_material(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not hasattr(IfcStore.get_file().by_id(props.ifc_definition_id), "HasAssociations"):
|
||||
return False
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
@@ -24,6 +44,11 @@ class BIM_PT_object_material(Panel):
|
||||
Data.load(self.oprops.ifc_definition_id)
|
||||
self.product_data = Data.products[self.oprops.ifc_definition_id]
|
||||
|
||||
if not Data.materials:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Materials Available")
|
||||
return
|
||||
|
||||
if self.product_data:
|
||||
if self.product_data["type"] == "IfcMaterialConstituentSet":
|
||||
self.material_set_data = Data.constituent_sets[self.product_data["id"]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import bpy
|
||||
from . import grid, wall, stair, door, window, slab, opening
|
||||
from . import grid, wall, stair, door, window, slab, opening, pie
|
||||
|
||||
classes = (
|
||||
grid.BIM_OT_add_object,
|
||||
@@ -9,8 +9,20 @@ classes = (
|
||||
window.BIM_OT_add_object,
|
||||
slab.BIM_OT_add_object,
|
||||
opening.BIM_OT_add_object,
|
||||
pie.OpenPieClass,
|
||||
pie.PieUpdateContainer,
|
||||
pie.PieAddOpening,
|
||||
pie.AssignIfcWall,
|
||||
pie.AssignIfcSlab,
|
||||
pie.AssignIfcStair,
|
||||
pie.AssignIfcDoor,
|
||||
pie.AssignIfcWindow,
|
||||
pie.VIEW3D_MT_PIE_bim,
|
||||
pie.VIEW3D_MT_PIE_bim_class,
|
||||
)
|
||||
|
||||
addon_keymaps = []
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.VIEW3D_MT_mesh_add.append(grid.add_object_button)
|
||||
@@ -20,6 +32,12 @@ def register():
|
||||
bpy.types.VIEW3D_MT_mesh_add.append(window.add_object_button)
|
||||
bpy.types.VIEW3D_MT_mesh_add.append(slab.add_object_button)
|
||||
bpy.types.VIEW3D_MT_mesh_add.append(opening.add_object_button)
|
||||
wm = bpy.context.window_manager
|
||||
if wm.keyconfigs.addon:
|
||||
km = wm.keyconfigs.addon.keymaps.new(name="3D View", space_type="VIEW_3D")
|
||||
kmi = km.keymap_items.new("wm.call_menu_pie", "E", "PRESS", shift=True)
|
||||
kmi.properties["name"] = "VIEW3D_MT_PIE_bim"
|
||||
addon_keymaps.append((km, kmi))
|
||||
|
||||
|
||||
def unregister():
|
||||
@@ -30,3 +48,9 @@ def unregister():
|
||||
bpy.types.VIEW3D_MT_mesh_add.remove(window.add_object_button)
|
||||
bpy.types.VIEW3D_MT_mesh_add.remove(slab.add_object_button)
|
||||
bpy.types.VIEW3D_MT_mesh_add.remove(opening.add_object_button)
|
||||
wm = bpy.context.window_manager
|
||||
kc = wm.keyconfigs.addon
|
||||
if kc:
|
||||
for km, kmi in addon_keymaps:
|
||||
km.keymap_items.remove(kmi)
|
||||
addon_keymaps.clear()
|
||||
|
||||
@@ -10,60 +10,61 @@ from mathutils import Vector
|
||||
def add_object(self, context):
|
||||
guid = ifcopenshell.guid.new()
|
||||
leaf_width = self.overall_width - 0.045 - 0.045
|
||||
verts = [
|
||||
# Left lining
|
||||
Vector((0, 0, 0)),
|
||||
Vector((0, self.depth, 0)),
|
||||
Vector((0.04, self.depth, 0)),
|
||||
Vector((0.04, self.depth - 0.04, 0)),
|
||||
Vector((0.065, self.depth - 0.04, 0)),
|
||||
Vector((0.065, 0, 0)),
|
||||
# Right lining
|
||||
Vector((self.overall_width, 0, 0)),
|
||||
Vector((self.overall_width, self.depth, 0)),
|
||||
Vector((self.overall_width - 0.04, self.depth, 0)),
|
||||
Vector((self.overall_width - 0.04, self.depth - 0.04, 0)),
|
||||
Vector((self.overall_width - 0.065, self.depth - 0.04, 0)),
|
||||
Vector((self.overall_width - 0.065, 0, 0)),
|
||||
# Door panel
|
||||
Vector((0.045, self.depth, 0)),
|
||||
Vector((0.045, self.depth + leaf_width, 0)),
|
||||
Vector((0.080, self.depth + leaf_width, 0)),
|
||||
Vector((0.080, self.depth, 0)),
|
||||
]
|
||||
edges = [
|
||||
[0, 1],
|
||||
[1, 2],
|
||||
[2, 3],
|
||||
[3, 4],
|
||||
[4, 5], # Left lining
|
||||
[6, 7],
|
||||
[7, 8],
|
||||
[8, 9],
|
||||
[9, 10],
|
||||
[10, 11], # Right lining
|
||||
[12, 13],
|
||||
[13, 14],
|
||||
[14, 15],
|
||||
[15, 12], # Door panel
|
||||
]
|
||||
# Door swing
|
||||
for i in range(0, 9):
|
||||
verts.append(
|
||||
Vector(
|
||||
(
|
||||
0.045 + (leaf_width * math.cos((math.pi / 2) / 8 * i)),
|
||||
self.depth + (leaf_width * math.sin((math.pi / 2) / 8 * i)),
|
||||
0,
|
||||
)
|
||||
)
|
||||
)
|
||||
edges.append([16 + i, 17 + i])
|
||||
edges.pop()
|
||||
faces = []
|
||||
mesh = bpy.data.meshes.new(name="Plan/Annotation/PLAN_VIEW/" + guid)
|
||||
mesh.use_fake_user = True
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
# TODO reimplement 2D. See #1222.
|
||||
#verts = [
|
||||
# # Left lining
|
||||
# Vector((0, 0, 0)),
|
||||
# Vector((0, self.depth, 0)),
|
||||
# Vector((0.04, self.depth, 0)),
|
||||
# Vector((0.04, self.depth - 0.04, 0)),
|
||||
# Vector((0.065, self.depth - 0.04, 0)),
|
||||
# Vector((0.065, 0, 0)),
|
||||
# # Right lining
|
||||
# Vector((self.overall_width, 0, 0)),
|
||||
# Vector((self.overall_width, self.depth, 0)),
|
||||
# Vector((self.overall_width - 0.04, self.depth, 0)),
|
||||
# Vector((self.overall_width - 0.04, self.depth - 0.04, 0)),
|
||||
# Vector((self.overall_width - 0.065, self.depth - 0.04, 0)),
|
||||
# Vector((self.overall_width - 0.065, 0, 0)),
|
||||
# # Door panel
|
||||
# Vector((0.045, self.depth, 0)),
|
||||
# Vector((0.045, self.depth + leaf_width, 0)),
|
||||
# Vector((0.080, self.depth + leaf_width, 0)),
|
||||
# Vector((0.080, self.depth, 0)),
|
||||
#]
|
||||
#edges = [
|
||||
# [0, 1],
|
||||
# [1, 2],
|
||||
# [2, 3],
|
||||
# [3, 4],
|
||||
# [4, 5], # Left lining
|
||||
# [6, 7],
|
||||
# [7, 8],
|
||||
# [8, 9],
|
||||
# [9, 10],
|
||||
# [10, 11], # Right lining
|
||||
# [12, 13],
|
||||
# [13, 14],
|
||||
# [14, 15],
|
||||
# [15, 12], # Door panel
|
||||
#]
|
||||
## Door swing
|
||||
#for i in range(0, 9):
|
||||
# verts.append(
|
||||
# Vector(
|
||||
# (
|
||||
# 0.045 + (leaf_width * math.cos((math.pi / 2) / 8 * i)),
|
||||
# self.depth + (leaf_width * math.sin((math.pi / 2) / 8 * i)),
|
||||
# 0,
|
||||
# )
|
||||
# )
|
||||
# )
|
||||
# edges.append([16 + i, 17 + i])
|
||||
#edges.pop()
|
||||
#faces = []
|
||||
#mesh = bpy.data.meshes.new(name="Plan/Annotation/PLAN_VIEW/" + guid)
|
||||
#mesh.use_fake_user = True
|
||||
#mesh.from_pydata(verts, edges, faces)
|
||||
|
||||
# Door lining profile
|
||||
verts = [
|
||||
@@ -96,6 +97,7 @@ def add_object(self, context):
|
||||
bpy.ops.object.convert(target="CURVE")
|
||||
|
||||
obj2.data.dimensions = "2D"
|
||||
obj2.data.bevel_mode = "OBJECT"
|
||||
obj2.data.bevel_object = obj
|
||||
|
||||
obj2.rotation_euler[0] = math.pi / 2
|
||||
@@ -145,27 +147,22 @@ def add_object(self, context):
|
||||
obj4.parent = obj2
|
||||
obj4.matrix_parent_inverse = obj2.matrix_world.inverted()
|
||||
obj4.hide_render = True
|
||||
obj4.name = "IfcOpeningElement/Dumb Door Opening"
|
||||
attribute = obj4.BIMObjectProperties.attributes.add()
|
||||
attribute.name = "PredefinedType"
|
||||
attribute.string_value = "OPENING"
|
||||
obj4.name = "Door Opening"
|
||||
|
||||
obj2.name = "IfcDoor/Dumb Door"
|
||||
attribute = obj2.BIMObjectProperties.attributes.add()
|
||||
attribute.name = "PredefinedType"
|
||||
attribute.string_value = "DOOR"
|
||||
obj2.data.name = "Model/Body/MODEL_VIEW/" + guid
|
||||
obj2.data.use_fake_user = True
|
||||
obj2.name = "Door"
|
||||
#obj2.data.name = "Model/Body/MODEL_VIEW/" + guid
|
||||
#obj2.data.use_fake_user = True
|
||||
|
||||
rep = obj2.BIMObjectProperties.representation_contexts.add()
|
||||
rep.context = "Model"
|
||||
rep.name = "Body"
|
||||
rep.target_view = "MODEL_VIEW"
|
||||
# TODO: reimplement. See #1222.
|
||||
#rep = obj2.BIMObjectProperties.representation_contexts.add()
|
||||
#rep.context = "Model"
|
||||
#rep.name = "Body"
|
||||
#rep.target_view = "MODEL_VIEW"
|
||||
|
||||
rep = obj2.BIMObjectProperties.representation_contexts.add()
|
||||
rep.context = "Plan"
|
||||
rep.name = "Annotation"
|
||||
rep.target_view = "PLAN_VIEW"
|
||||
#rep = obj2.BIMObjectProperties.representation_contexts.add()
|
||||
#rep.context = "Plan"
|
||||
#rep.name = "Annotation"
|
||||
#rep.target_view = "PLAN_VIEW"
|
||||
|
||||
|
||||
class BIM_OT_add_object(Operator, AddObjectHelper):
|
||||
|
||||
@@ -15,11 +15,8 @@ def add_object(self, context):
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
obj = object_data_add(context, mesh, operator=self)
|
||||
obj.name = "IfcOpening/Dumb Opening"
|
||||
obj.name = "Opening"
|
||||
obj.display_type = "WIRE"
|
||||
attribute = obj.BIMObjectProperties.attributes.add()
|
||||
attribute.name = "PredefinedType"
|
||||
attribute.string_value = "OPENING"
|
||||
|
||||
|
||||
class BIM_OT_add_object(Operator, AddObjectHelper):
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import bpy
|
||||
|
||||
|
||||
class OpenPieClass(bpy.types.Operator):
|
||||
bl_idname = "bim.open_pie_class"
|
||||
bl_label = "Open Pie Class"
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.wm.call_menu_pie(name="VIEW3D_MT_PIE_bim_class")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignIfcWall(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_ifc_wall"
|
||||
bl_label = "IfcWall"
|
||||
|
||||
def execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcWall")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignIfcSlab(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_ifc_slab"
|
||||
bl_label = "IfcSlab"
|
||||
|
||||
def execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcSlab")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignIfcStair(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_ifc_stair"
|
||||
bl_label = "IfcStair"
|
||||
|
||||
def execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcStair")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignIfcDoor(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_ifc_door"
|
||||
bl_label = "IfcDoor"
|
||||
|
||||
def execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcDoor")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignIfcWindow(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_ifc_window"
|
||||
bl_label = "IfcWindow"
|
||||
|
||||
def execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcWindow")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class PieAddOpening(bpy.types.Operator):
|
||||
bl_idname = "bim.pie_add_opening"
|
||||
bl_label = "Add Opening"
|
||||
|
||||
def execute(self, context):
|
||||
if len(context.selected_objects) == 2:
|
||||
opening_name = None
|
||||
obj_name = None
|
||||
for obj in context.selected_objects:
|
||||
if "IfcOpeningElement" in obj.name or not obj.BIMObjectProperties.ifc_definition_id:
|
||||
opening_name = obj.name
|
||||
else:
|
||||
opj_name = obj.name
|
||||
bpy.ops.bim.add_opening(obj=opj_name, opening=opening_name)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class PieUpdateContainer(bpy.types.Operator):
|
||||
bl_idname = "bim.pie_update_container"
|
||||
bl_label = "Update Spatial Container"
|
||||
|
||||
def execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
relating_structure = None
|
||||
for collection in obj.users_collection:
|
||||
relating_structure_obj = bpy.data.objects.get(collection.name)
|
||||
if not relating_structure_obj or not relating_structure_obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
relating_structure = relating_structure_obj
|
||||
bpy.ops.bim.assign_container(relating_structure=relating_structure.name, related_element=obj.name)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class VIEW3D_MT_PIE_bim(bpy.types.Menu):
|
||||
bl_label = "Geometry"
|
||||
|
||||
def draw(self, context):
|
||||
pie = self.layout.menu_pie()
|
||||
pie.operator("bim.edit_object_placement")
|
||||
pie.operator("bim.update_mesh_representation")
|
||||
pie.operator("bim.map_representation")
|
||||
pie.operator("bim.pie_add_opening")
|
||||
pie.operator("bim.pie_update_container")
|
||||
pie.operator("bim.open_pie_class", text="Assign IFC Class")
|
||||
|
||||
|
||||
class VIEW3D_MT_PIE_bim_class(bpy.types.Menu):
|
||||
bl_label = "IFC Class"
|
||||
|
||||
def draw(self, context):
|
||||
pie = self.layout.menu_pie()
|
||||
pie.operator("bim.assign_ifc_wall")
|
||||
pie.operator("bim.assign_ifc_slab")
|
||||
pie.operator("bim.assign_ifc_stair")
|
||||
pie.operator("bim.assign_ifc_door")
|
||||
pie.operator("bim.assign_ifc_window")
|
||||
@@ -22,10 +22,7 @@ def add_object(self, context):
|
||||
modifier.use_even_offset = True
|
||||
modifier.offset = 1
|
||||
modifier.thickness = self.depth
|
||||
obj.name = "IfcSlab/Dumb Slab"
|
||||
attribute = obj.BIMObjectProperties.attributes.add()
|
||||
attribute.name = "PredefinedType"
|
||||
attribute.string_value = "FLOOR"
|
||||
obj.name = "Slab"
|
||||
|
||||
|
||||
class BIM_OT_add_object(Operator, AddObjectHelper):
|
||||
|
||||
@@ -28,14 +28,13 @@ def add_object(self, context):
|
||||
modifier.relative_offset_displace[0] = 0
|
||||
modifier.relative_offset_displace[1] = 1
|
||||
modifier.use_constant_offset = True
|
||||
modifier.constant_offset_displace[0] = 0
|
||||
modifier.constant_offset_displace[1] = 0
|
||||
modifier.constant_offset_displace[2] = self.height / self.number_of_treads
|
||||
modifier.count = self.number_of_treads
|
||||
self.riser_height = self.height / self.number_of_treads
|
||||
self.length = self.number_of_treads * self.tread_length
|
||||
obj.name = "IfcStairFlight/Dumb Stair"
|
||||
attribute = obj.BIMObjectProperties.attributes.add()
|
||||
attribute.name = "PredefinedType"
|
||||
attribute.string_value = "STRAIGHT"
|
||||
obj.name = "Stair"
|
||||
|
||||
|
||||
class BIM_OT_add_object(Operator, AddObjectHelper):
|
||||
|
||||
@@ -38,10 +38,8 @@ def add_object(self, context):
|
||||
modifier = obj.modifiers.new("Wall Width", "SOLIDIFY")
|
||||
modifier.use_even_offset = True
|
||||
modifier.thickness = self.width
|
||||
obj.name = "IfcWall/Dumb Wall"
|
||||
attribute = obj.BIMObjectProperties.attributes.add()
|
||||
attribute.name = "PredefinedType"
|
||||
attribute.string_value = "STANDARD"
|
||||
obj.name = "Wall"
|
||||
return obj
|
||||
|
||||
|
||||
class BIM_OT_add_object(Operator, AddObjectHelper):
|
||||
|
||||
@@ -10,48 +10,48 @@ from mathutils import Vector
|
||||
def add_object(self, context):
|
||||
guid = ifcopenshell.guid.new()
|
||||
leaf_width = self.overall_width - 0.045 - 0.045
|
||||
verts = [
|
||||
# Left lining
|
||||
Vector((0, 0, 0)),
|
||||
Vector((0, self.depth, 0)),
|
||||
Vector((0.04, self.depth, 0)),
|
||||
Vector((0.04, 0, 0)),
|
||||
# Right lining
|
||||
Vector((self.overall_width, 0, 0)),
|
||||
Vector((self.overall_width, self.depth, 0)),
|
||||
Vector((self.overall_width - 0.04, self.depth, 0)),
|
||||
Vector((self.overall_width - 0.04, 0, 0)),
|
||||
# Bottom lining
|
||||
Vector((0, 0, 0)),
|
||||
Vector((self.overall_width, 0, 0)),
|
||||
Vector((0, self.depth, 0)),
|
||||
Vector((self.overall_width, self.depth, 0)),
|
||||
# Window panel
|
||||
Vector((0.04, (self.depth / 2) + 0.005, 0)),
|
||||
Vector((0.04, (self.depth / 2) - 0.005, 0)),
|
||||
Vector((self.overall_width - 0.04, (self.depth / 2) - 0.005, 0)),
|
||||
Vector((self.overall_width - 0.04, (self.depth / 2) + 0.005, 0)),
|
||||
]
|
||||
edges = [
|
||||
[0, 1],
|
||||
[1, 2],
|
||||
[2, 3],
|
||||
[3, 0], # Left lining
|
||||
[4, 5],
|
||||
[5, 6],
|
||||
[6, 7],
|
||||
[7, 4], # Right lining
|
||||
[8, 9],
|
||||
[10, 11], # Bottom lining
|
||||
[12, 13],
|
||||
[13, 14],
|
||||
[14, 15],
|
||||
[15, 12], # Window panel
|
||||
]
|
||||
faces = []
|
||||
mesh = bpy.data.meshes.new(name="Plan/Annotation/PLAN_VIEW/" + guid)
|
||||
mesh.use_fake_user = True
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
#verts = [
|
||||
# # Left lining
|
||||
# Vector((0, 0, 0)),
|
||||
# Vector((0, self.depth, 0)),
|
||||
# Vector((0.04, self.depth, 0)),
|
||||
# Vector((0.04, 0, 0)),
|
||||
# # Right lining
|
||||
# Vector((self.overall_width, 0, 0)),
|
||||
# Vector((self.overall_width, self.depth, 0)),
|
||||
# Vector((self.overall_width - 0.04, self.depth, 0)),
|
||||
# Vector((self.overall_width - 0.04, 0, 0)),
|
||||
# # Bottom lining
|
||||
# Vector((0, 0, 0)),
|
||||
# Vector((self.overall_width, 0, 0)),
|
||||
# Vector((0, self.depth, 0)),
|
||||
# Vector((self.overall_width, self.depth, 0)),
|
||||
# # Window panel
|
||||
# Vector((0.04, (self.depth / 2) + 0.005, 0)),
|
||||
# Vector((0.04, (self.depth / 2) - 0.005, 0)),
|
||||
# Vector((self.overall_width - 0.04, (self.depth / 2) - 0.005, 0)),
|
||||
# Vector((self.overall_width - 0.04, (self.depth / 2) + 0.005, 0)),
|
||||
#]
|
||||
#edges = [
|
||||
# [0, 1],
|
||||
# [1, 2],
|
||||
# [2, 3],
|
||||
# [3, 0], # Left lining
|
||||
# [4, 5],
|
||||
# [5, 6],
|
||||
# [6, 7],
|
||||
# [7, 4], # Right lining
|
||||
# [8, 9],
|
||||
# [10, 11], # Bottom lining
|
||||
# [12, 13],
|
||||
# [13, 14],
|
||||
# [14, 15],
|
||||
# [15, 12], # Window panel
|
||||
#]
|
||||
#faces = []
|
||||
#mesh = bpy.data.meshes.new(name="Plan/Annotation/PLAN_VIEW/" + guid)
|
||||
#mesh.use_fake_user = True
|
||||
#mesh.from_pydata(verts, edges, faces)
|
||||
|
||||
# Window lining profile
|
||||
verts = [
|
||||
@@ -83,6 +83,7 @@ def add_object(self, context):
|
||||
obj2.data.splines[0].use_cyclic_u = True
|
||||
|
||||
obj2.data.dimensions = "2D"
|
||||
obj2.data.bevel_mode = "OBJECT"
|
||||
obj2.data.bevel_object = obj
|
||||
|
||||
obj2.rotation_euler[0] = math.pi / 2
|
||||
@@ -132,27 +133,21 @@ def add_object(self, context):
|
||||
obj4.parent = obj2
|
||||
obj4.matrix_parent_inverse = obj2.matrix_world.inverted()
|
||||
obj4.hide_render = True
|
||||
obj4.name = "IfcOpeningElement/Dumb Window Opening"
|
||||
attribute = obj4.BIMObjectProperties.attributes.add()
|
||||
attribute.name = "PredefinedType"
|
||||
attribute.string_value = "OPENING"
|
||||
obj4.name = "Window Opening"
|
||||
|
||||
obj2.name = "IfcWindow/Dumb Window"
|
||||
attribute = obj2.BIMObjectProperties.attributes.add()
|
||||
attribute.name = "PredefinedType"
|
||||
attribute.string_value = "WINDOW"
|
||||
obj2.data.name = "Model/Body/MODEL_VIEW/" + guid
|
||||
obj2.data.use_fake_user = True
|
||||
obj2.name = "Window"
|
||||
#obj2.data.name = "Model/Body/MODEL_VIEW/" + guid
|
||||
#obj2.data.use_fake_user = True
|
||||
|
||||
rep = obj2.BIMObjectProperties.representation_contexts.add()
|
||||
rep.context = "Model"
|
||||
rep.name = "Body"
|
||||
rep.target_view = "MODEL_VIEW"
|
||||
#rep = obj2.BIMObjectProperties.representation_contexts.add()
|
||||
#rep.context = "Model"
|
||||
#rep.name = "Body"
|
||||
#rep.target_view = "MODEL_VIEW"
|
||||
|
||||
rep = obj2.BIMObjectProperties.representation_contexts.add()
|
||||
rep.context = "Plan"
|
||||
rep.name = "Annotation"
|
||||
rep.target_view = "PLAN_VIEW"
|
||||
#rep = obj2.BIMObjectProperties.representation_contexts.add()
|
||||
#rep.context = "Plan"
|
||||
#rep.name = "Annotation"
|
||||
#rep.target_view = "PLAN_VIEW"
|
||||
|
||||
|
||||
class BIM_OT_add_object(Operator, AddObjectHelper):
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import bpy
|
||||
import addon_utils
|
||||
import blenderbim.bim.module.owner.create_owner_history as create_owner_history_usecase
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
def create_owner_history(change_action=None):
|
||||
file = IfcStore.get_file()
|
||||
return create_owner_history_usecase.Usecase(
|
||||
file,
|
||||
{
|
||||
"person": file.by_id(int(bpy.context.scene.BIMOwnerProperties.user_person)),
|
||||
"organisation": file.by_id(int(bpy.context.scene.BIMOwnerProperties.user_organisation)),
|
||||
"ApplicationIdentifier": "BlenderBIM",
|
||||
"ApplicationFullName": "BlenderBIM Add-on",
|
||||
"Version": get_application_version(),
|
||||
"ChangeAction": change_action or "ADDED",
|
||||
},
|
||||
).execute()
|
||||
|
||||
|
||||
def get_application_version():
|
||||
return ".".join(
|
||||
[
|
||||
str(x)
|
||||
for x in [
|
||||
addon.bl_info.get("version", (-1, -1, -1))
|
||||
for addon in addon_utils.modules()
|
||||
if addon.bl_info["name"] == "BlenderBIM"
|
||||
][0]
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,101 @@
|
||||
import time
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"person": None,
|
||||
"organisation": None,
|
||||
"ApplicationIdentifier": "",
|
||||
"ApplicationFullName": "",
|
||||
"Version": "",
|
||||
"ChangeAction": "NOTDEFINED",
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
user = self.get_user()
|
||||
application = self.get_application()
|
||||
return self.file.create_entity(
|
||||
"IfcOwnerHistory",
|
||||
**{
|
||||
"OwningUser": user,
|
||||
"OwningApplication": application,
|
||||
"State": "READWRITE",
|
||||
"ChangeAction": self.settings["ChangeAction"],
|
||||
"LastModifiedDate": int(time.time()),
|
||||
"LastModifyingUser": user,
|
||||
"LastModifyingApplication": application,
|
||||
"CreationDate": int(time.time()),
|
||||
},
|
||||
)
|
||||
|
||||
def get_user(self):
|
||||
for element in self.file.by_type("IfcPersonAndOrganization"):
|
||||
if (
|
||||
element.ThePerson == self.settings["person"]
|
||||
and element.TheOrganization == self.settings["organisation"]
|
||||
):
|
||||
return element
|
||||
return self.file.create_entity(
|
||||
"IfcPersonAndOrganization",
|
||||
**{"ThePerson": self.settings["person"], "TheOrganization": self.settings["organisation"]},
|
||||
)
|
||||
|
||||
def get_application(self):
|
||||
for element in self.file.by_type("IfcApplication"):
|
||||
if element.ApplicationIdentifier == self.settings["ApplicationIdentifier"]:
|
||||
return element
|
||||
return self.file.create_entity(
|
||||
"IfcApplication",
|
||||
**{
|
||||
"ApplicationDeveloper": self.get_application_organisation(),
|
||||
"Version": self.settings["Version"],
|
||||
"ApplicationFullName": self.settings["ApplicationFullName"],
|
||||
"ApplicationIdentifier": self.settings["ApplicationIdentifier"],
|
||||
},
|
||||
)
|
||||
|
||||
def get_application_organisation(self):
|
||||
return self.file.create_entity(
|
||||
"IfcOrganization",
|
||||
**{
|
||||
"Name": "IfcOpenShell",
|
||||
"Description": "IfcOpenShell is an open source (LGPL) software library that helps users and software developers to work with the IFC file format.",
|
||||
"Roles": [
|
||||
self.file.create_entity("IfcActorRole", **{"Role": "USERDEFINED", "UserDefinedRole": "CONTRIBUTOR"})
|
||||
],
|
||||
"Addresses": [
|
||||
self.file.create_entity(
|
||||
"IfcTelecomAddress",
|
||||
**{
|
||||
"Purpose": "USERDEFINED",
|
||||
"UserDefinedPurpose": "WEBPAGE",
|
||||
"Description": "The main webpage of the software collection.",
|
||||
"WWWHomePageURL": "https://ifcopenshell.org",
|
||||
},
|
||||
),
|
||||
self.file.create_entity(
|
||||
"IfcTelecomAddress",
|
||||
**{
|
||||
"Purpose": "USERDEFINED",
|
||||
"UserDefinedPurpose": "WEBPAGE",
|
||||
"Description": "The BlenderBIM Add-on webpage of the software collection.",
|
||||
"WWWHomePageURL": "https://blenderbim.org",
|
||||
},
|
||||
),
|
||||
self.file.create_entity(
|
||||
"IfcTelecomAddress",
|
||||
**{
|
||||
"Purpose": "USERDEFINED",
|
||||
"UserDefinedPurpose": "REPOSITORY",
|
||||
"Description": "The source code repository of the software collection.",
|
||||
"WWWHomePageURL": "https://github.com/IfcOpenShell/IfcOpenShell.git",
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
)
|
||||
@@ -22,7 +22,7 @@ def getPersons(self, context):
|
||||
if "Id" in person:
|
||||
identifier = person["Id"] or ""
|
||||
else:
|
||||
identifier = person["Identifier"] or ""
|
||||
identifier = person["Identification"] or ""
|
||||
results.append((str(ifc_id), identifier, ""))
|
||||
return results
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ class BIM_PT_people(Panel):
|
||||
|
||||
self.file = IfcStore.get_file()
|
||||
self.layout.use_property_split = True
|
||||
self.layout.use_property_decorate = False
|
||||
props = context.scene.BIMOwnerProperties
|
||||
|
||||
row = self.layout.row()
|
||||
@@ -169,6 +170,7 @@ class BIM_PT_organisations(Panel):
|
||||
|
||||
self.file = IfcStore.get_file()
|
||||
self.layout.use_property_split = True
|
||||
self.layout.use_property_decorate = False
|
||||
props = context.scene.BIMOwnerProperties
|
||||
|
||||
row = self.layout.row()
|
||||
@@ -216,6 +218,7 @@ class BIM_PT_owner(Panel):
|
||||
Data.load()
|
||||
|
||||
self.layout.use_property_split = True
|
||||
self.layout.use_property_decorate = False
|
||||
props = context.scene.BIMOwnerProperties
|
||||
|
||||
if not Data.people:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user