mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-14 11:24:19 +00:00
Merge remote-tracking branch 'origin/v0.6.0' into v0.7.0
This commit is contained in:
@@ -69,7 +69,8 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||
print(f"Reading {bpy.path.basename(filename)}...")
|
||||
settings = ifcopenshell_geom.settings()
|
||||
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, blender_booleans)
|
||||
iterator = ifcopenshell_geom.iterator(settings, filename)
|
||||
file = ifcopenshell.open(filename)
|
||||
iterator = ifcopenshell_geom.iterator(settings, file)
|
||||
valid_file = iterator.initialize()
|
||||
if not valid_file:
|
||||
return False
|
||||
@@ -134,13 +135,15 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||
for mat in mats:
|
||||
props = {}
|
||||
if mat.has_diffuse:
|
||||
props['diffuse_color'] = mat.diffuse
|
||||
if mat.has_specular:
|
||||
props['specular_color'] = mat.specular
|
||||
if mat.has_transparency and mat.transparency > 0:
|
||||
props['transparency'] = mat.transparency
|
||||
if mat.has_specularity:
|
||||
props['specular_intensity'] = mat.specularity
|
||||
alpha = 1.
|
||||
if mat.has_transparency and mat.transparency > 0:
|
||||
alpha = 1. - mat.transparency
|
||||
props['diffuse_color'] = mat.diffuse + (alpha,)
|
||||
# @todo
|
||||
# if mat.has_specular:
|
||||
# props['specular_color'] = mat.specular
|
||||
# if mat.has_specularity:
|
||||
# props['specular_intensity'] = mat.specularity
|
||||
add_material(mat.name, props)
|
||||
|
||||
faces = me.polygons if hasattr(me, 'polygons') else me.faces
|
||||
@@ -263,8 +266,10 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||
mod.operation = "DIFFERENCE"
|
||||
mod.object = opening_ob
|
||||
|
||||
txt = bpy.data.texts.new(f"{bpy.path.basename(filename)}.log")
|
||||
txt.from_string(iterator.getLog())
|
||||
if hasattr(iterator, 'getLog'):
|
||||
# @todo
|
||||
txt = bpy.data.texts.new(f"{bpy.path.basename(filename)}.log")
|
||||
txt.from_string(iterator.getLog())
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -61,9 +61,9 @@
|
||||
#ifdef _MSC_VER
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
// C++11 header:
|
||||
#include <random>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && defined(_UNICODE)
|
||||
typedef std::wstring path_t;
|
||||
@@ -75,7 +75,7 @@ static std::ostream& cout_ = std::cout;
|
||||
static std::ostream& cerr_ = std::cerr;
|
||||
#endif
|
||||
|
||||
const std::string DEFAULT_EXTENSION = "obj";
|
||||
const std::string DEFAULT_EXTENSION = ".obj";
|
||||
const std::string TEMP_FILE_EXTENSION = ".tmp";
|
||||
|
||||
namespace po = boost::program_options;
|
||||
@@ -103,7 +103,7 @@ void print_usage(bool suggest_help = true)
|
||||
<< " .svg SVG Scalable Vector Graphics (2D floor plan)\n"
|
||||
<< " .ifc IFC-SPF Industry Foundation Classes\n"
|
||||
<< "\n"
|
||||
<< "If no output filename given, <input>." << IfcUtil::path::from_utf8(DEFAULT_EXTENSION) << " will be used as the output file.\n";
|
||||
<< "If no output filename given, <input>" << IfcUtil::path::from_utf8(DEFAULT_EXTENSION) << " will be used as the output file.\n";
|
||||
if (suggest_help) {
|
||||
cout_ << "\nRun 'IfcConvert --help' for more information.";
|
||||
}
|
||||
@@ -245,6 +245,11 @@ int main(int argc, char** argv) {
|
||||
"Specifies whether to convert back geometrical output back to the "
|
||||
"unit of measure in which it is defined in the IFC file. Default is "
|
||||
"to use meters.")
|
||||
("orient-shells",
|
||||
"Specifies whether to orient the faces of IfcConnectedFaceSets. "
|
||||
"This is a potentially time consuming operation, but guarantees a "
|
||||
"consistent orientation of surface normals, even if the faces are not "
|
||||
"properly oriented in the IFC file.")
|
||||
#if OCC_VERSION_HEX < 0x60900
|
||||
// In Open CASCADE version prior to 6.9.0 boolean operations with multiple
|
||||
// arguments where not introduced yet and a work-around was implemented to
|
||||
@@ -389,6 +394,7 @@ int main(int argc, char** argv) {
|
||||
const bool weld_vertices = vmap.count("weld-vertices") != 0;
|
||||
const bool use_world_coords = vmap.count("use-world-coords") != 0;
|
||||
const bool convert_back_units = vmap.count("convert-back-units") != 0;
|
||||
const bool orient_shells = vmap.count("orient-shells") != 0;
|
||||
#if OCC_VERSION_HEX < 0x60900
|
||||
const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0;
|
||||
#endif
|
||||
@@ -600,23 +606,32 @@ int main(int argc, char** argv) {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
if (output_extension == DAE || output_extension == STP || output_extension == IGS) {
|
||||
// These serializers do not support opening unicode paths on Windows. Therefore
|
||||
#else
|
||||
if (output_extension == DAE) {
|
||||
#endif
|
||||
// These serializers do not support opening unicode paths. Therefore
|
||||
// a random temp file is generated using only ASCII characters instead.
|
||||
std::random_device rng;
|
||||
std::uniform_int_distribution<int> index_dist(L'A', L'Z');
|
||||
output_temp_filename = L".ifcopenshell.";
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
output_temp_filename.push_back(static_cast<wchar_t>(index_dist(rng)));
|
||||
std::uniform_int_distribution<int> index_dist('A', 'Z');
|
||||
{
|
||||
std::string v = ".ifcopenshell.";
|
||||
output_temp_filename += path_t(v.begin(), v.end());
|
||||
}
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
output_temp_filename.push_back(static_cast<path_t::value_type>(index_dist(rng)));
|
||||
}
|
||||
{
|
||||
std::string v = ".tmp.";
|
||||
output_temp_filename += path_t(v.begin(), v.end());
|
||||
}
|
||||
output_temp_filename += L".tmp";
|
||||
}
|
||||
#endif
|
||||
|
||||
SerializerSettings settings;
|
||||
/// @todo Make APPLY_DEFAULT_MATERIALS configurable? Quickly tested setting this to false and using obj exporter caused the program to crash and burn.
|
||||
settings.set(IfcGeom::IteratorSettings::APPLY_DEFAULT_MATERIALS, true);
|
||||
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, use_world_coords);
|
||||
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, use_world_coords || output_extension == SVG || output_extension == OBJ);
|
||||
settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, weld_vertices);
|
||||
settings.set(IfcGeom::IteratorSettings::SEW_SHELLS, orient_shells);
|
||||
settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS, convert_back_units);
|
||||
#if OCC_VERSION_HEX < 0x60900
|
||||
settings.set(IfcGeom::IteratorSettings::FASTER_BOOLEANS, merge_boolean_operands);
|
||||
@@ -627,7 +642,7 @@ int main(int argc, char** argv) {
|
||||
settings.set(IfcGeom::IteratorSettings::APPLY_LAYERSETS, enable_layerset_slicing);
|
||||
settings.set(IfcGeom::IteratorSettings::NO_NORMALS, no_normals);
|
||||
settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, generate_uvs);
|
||||
settings.set(IfcGeom::IteratorSettings::SEARCH_FLOOR, use_element_hierarchy);
|
||||
settings.set(IfcGeom::IteratorSettings::SEARCH_FLOOR, use_element_hierarchy || output_extension == SVG);
|
||||
settings.set(IfcGeom::IteratorSettings::SITE_LOCAL_PLACEMENT, site_local_placement);
|
||||
settings.set(IfcGeom::IteratorSettings::BUILDING_LOCAL_PLACEMENT, building_local_placement);
|
||||
settings.set(IfcGeom::IteratorSettings::VALIDATE_QUANTITIES, validate);
|
||||
@@ -644,10 +659,6 @@ int main(int argc, char** argv) {
|
||||
if (output_extension == OBJ) {
|
||||
// Do not use temp file for MTL as it's such a small file.
|
||||
const path_t mtl_filename = change_extension(output_filename, MTL);
|
||||
if (!use_world_coords) {
|
||||
Logger::Notice("Using world coords when writing WaveFront OBJ files");
|
||||
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true);
|
||||
}
|
||||
serializer = boost::make_shared<WaveFrontOBJSerializer>(IfcUtil::path::to_utf8(output_temp_filename), IfcUtil::path::to_utf8(mtl_filename), settings);
|
||||
#ifdef WITH_OPENCOLLADA
|
||||
} else if (output_extension == DAE) {
|
||||
@@ -717,6 +728,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
if (!init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap)) {
|
||||
write_log(!quiet);
|
||||
serializer.reset();
|
||||
IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); /**< @todo Windows Unicode support */
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -726,6 +738,7 @@ int main(int argc, char** argv) {
|
||||
/// @todo It would be nice to know and print separate error prints for a case where we found no entities
|
||||
/// and for a case we found no entities that satisfy our filtering criteria.
|
||||
Logger::Notice("No geometrical elements found or none succesfully converted");
|
||||
serializer.reset();
|
||||
IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename));
|
||||
write_log(!quiet);
|
||||
return EXIT_FAILURE;
|
||||
@@ -847,7 +860,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
if (validate && Logger::MaxSeverity() >= Logger::LOG_ERROR) {
|
||||
Logger::Error("Errors encountered during proccessing.");
|
||||
Logger::Error("Errors encountered during processing.");
|
||||
successful = false;
|
||||
}
|
||||
|
||||
@@ -1115,13 +1128,13 @@ namespace latebound_access {
|
||||
auto i = decl->attribute_index(attr);
|
||||
|
||||
auto attr_type = decl->attribute_by_index(i)->type_of_attribute();
|
||||
if (attr_type->as_named_type() && attr_type->as_named_type()->declared_type()->as_enumeration_type()) {
|
||||
if (attr_type->as_named_type() && attr_type->as_named_type()->declared_type()->as_enumeration_type() && !std::is_same<T, IfcWrite::IfcWriteArgument::EnumerationReference>::value) {
|
||||
set_enumeration(inst, attr, attr_type->as_named_type()->declared_type()->as_enumeration_type(), t);
|
||||
} else {
|
||||
IfcWrite::IfcWriteArgument* a = new IfcWrite::IfcWriteArgument;
|
||||
a->set(t);
|
||||
inst->data().attributes()[i] = a;
|
||||
}
|
||||
|
||||
IfcWrite::IfcWriteArgument* a = new IfcWrite::IfcWriteArgument;
|
||||
a->set(t);
|
||||
inst->data().attributes()[i] = a;
|
||||
}
|
||||
|
||||
IfcUtil::IfcBaseClass* create(IfcParse::IfcFile& f, const std::string& entity) {
|
||||
@@ -1187,6 +1200,7 @@ void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool std
|
||||
IfcGeom::IteratorSettings settings;
|
||||
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, false);
|
||||
settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, false);
|
||||
settings.set(IfcGeom::IteratorSettings::SEW_SHELLS, true);
|
||||
settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS, true);
|
||||
settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
|
||||
|
||||
|
||||
@@ -199,14 +199,14 @@ else:
|
||||
pickle.dump(mapping, f, protocol=0)
|
||||
|
||||
import header
|
||||
import enum_header
|
||||
import implementation
|
||||
import schema_class
|
||||
import definitions
|
||||
|
||||
header.Header(mapping).emit()
|
||||
enum_header.EnumHeader(mapping).emit()
|
||||
implementation.Implementation(mapping).emit()
|
||||
schema_class.SchemaClass(mapping).emit()
|
||||
definitions.Definitions(mapping).emit()
|
||||
|
||||
sys.stdout.write(schema.name)
|
||||
"""%('\n '.join(statements)))
|
||||
|
||||
@@ -17,23 +17,46 @@
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import templates
|
||||
import operator
|
||||
|
||||
import nodes
|
||||
import codegen
|
||||
|
||||
class EnumHeader(codegen.Base):
|
||||
from collections import defaultdict
|
||||
|
||||
class Definitions(codegen.Base):
|
||||
def __init__(self, mapping):
|
||||
enumerable_types = sorted(set([name for name, type in mapping.schema.types.items()] + [name for name, type in mapping.schema.entities.items()]))
|
||||
|
||||
self.str = templates.enum_header % {
|
||||
'schema_name_upper' : mapping.schema.name.upper(),
|
||||
'schema_name' : mapping.schema.name.capitalize(),
|
||||
'types' : ', '.join(enumerable_types)
|
||||
}
|
||||
schema_name = mapping.schema.name
|
||||
self.schema_name = schema_name_title = schema_name.capitalize()
|
||||
|
||||
statements = ['']
|
||||
|
||||
self.schema_name = mapping.schema.name.capitalize()
|
||||
|
||||
self.file_name = '%senum.h'%self.schema_name
|
||||
def write_entity(schema_name, name, type):
|
||||
attribute_names = list(map(lambda t: (t.name, t.optional), type.attributes))
|
||||
for attr, is_optional in attribute_names:
|
||||
statements.append("#define SCHEMA_%(name)s_HAS_%(attr)s" % locals())
|
||||
if is_optional:
|
||||
statements.append("#define SCHEMA_%(name)s_%(attr)s_IS_OPTIONAL" % locals())
|
||||
|
||||
def write(name):
|
||||
statements.append("#define SCHEMA_HAS_%(name)s" % locals())
|
||||
fn = None
|
||||
if mapping.schema.is_entity(name):
|
||||
fn = write_entity
|
||||
|
||||
if fn is not None:
|
||||
decl = mapping.schema[name]
|
||||
if isinstance(decl, nodes.TypeDeclaration):
|
||||
decl = decl.type.type
|
||||
fn(schema_name, name, decl) is not False
|
||||
|
||||
for name in mapping.schema:
|
||||
write(name)
|
||||
|
||||
self.str = "\n".join(statements) + "\n"
|
||||
|
||||
self.file_name = '%s-definitions.h' % self.schema_name
|
||||
|
||||
def __repr__(self):
|
||||
return self.str
|
||||
@@ -47,7 +47,7 @@ regices = list(zip([re.compile(s,re.M) for s in [r'<[\w\n=" \-/\.;_\t:%#,\?\(\)]
|
||||
definition_files = ['DocEntity.csv', 'DocEnumeration.csv', 'DocDefined.csv', 'DocSelect.csv']
|
||||
definition_files = map(make_absolute, definition_files)
|
||||
for fn in definition_files:
|
||||
with open(fn) as f:
|
||||
with open(fn, encoding="utf8", errors='ignore') as f:
|
||||
for oid, name, desc in csv.reader(f, delimiter=';', quotechar='"'):
|
||||
name_to_oid[name] = oid
|
||||
oid_to_name[oid] = name
|
||||
@@ -74,4 +74,4 @@ def description(item):
|
||||
for r,s in regices: desc = r.sub(s,desc)
|
||||
desc = desc.strip()
|
||||
return desc.split("\n")
|
||||
else: return []
|
||||
else: return []
|
||||
|
||||
@@ -34,8 +34,6 @@ header = """
|
||||
#include "../ifcparse/IfcException.h"
|
||||
#include "../ifcparse/Argument.h"
|
||||
|
||||
#include "../ifcparse/%(schema_name)senum.h"
|
||||
|
||||
struct %(schema_name)s {
|
||||
|
||||
static const IfcParse::schema_definition& get_schema();
|
||||
|
||||
@@ -13,9 +13,6 @@ void IfcGeom::AbstractKernel::setValue(GeomValue var, double value) {
|
||||
case GV_DEFLECTION_TOLERANCE:
|
||||
deflection_tolerance = value;
|
||||
break;
|
||||
case GV_WIRE_CREATION_TOLERANCE:
|
||||
wire_creation_tolerance = value;
|
||||
break;
|
||||
case GV_POINT_EQUALITY_TOLERANCE:
|
||||
point_equality_tolerance = value;
|
||||
break;
|
||||
@@ -40,8 +37,6 @@ double IfcGeom::AbstractKernel::getValue(GeomValue var) const {
|
||||
switch (var) {
|
||||
case GV_DEFLECTION_TOLERANCE:
|
||||
return deflection_tolerance;
|
||||
case GV_WIRE_CREATION_TOLERANCE:
|
||||
return wire_creation_tolerance;
|
||||
case GV_MINIMAL_FACE_AREA:
|
||||
// Considering a right-angled triangle, this about the smallest
|
||||
// area you can obtain without the vertices being confused.
|
||||
@@ -178,7 +173,7 @@ namespace {
|
||||
|
||||
template <typename T>
|
||||
std::pair<IfcSchema::IfcSurfaceStyle*, T*> _get_surface_style(const IfcSchema::IfcStyledItem* si) {
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcStyleAssignmentSelect
|
||||
IfcEntityList::ptr style_assignments = si->Styles();
|
||||
for (IfcEntityList::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) {
|
||||
if (!(*kt)->declaration().is(IfcSchema::IfcPresentationStyleAssignment::Class())) {
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x.h)
|
||||
#include INCLUDE_SCHEMA(IfcSchema)
|
||||
#undef INCLUDE_SCHEMA
|
||||
#define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x-definitions.h)
|
||||
#include INCLUDE_SCHEMA(IfcSchema)
|
||||
#undef INCLUDE_SCHEMA
|
||||
|
||||
namespace IfcGeom {
|
||||
|
||||
|
||||
@@ -409,8 +409,8 @@ namespace IfcGeom {
|
||||
bool representation_processed_as_mapped_item = false;
|
||||
IfcSchema::IfcRepresentation* representation_mapped_to = kernel->representation_mapped_to(representation);
|
||||
if (representation_mapped_to) {
|
||||
representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ ||
|
||||
ok_mapped_representations->contains(representation_mapped_to);
|
||||
representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ && (
|
||||
ok_mapped_representations->contains(representation_mapped_to) || reuse_ok_(kernel->products_represented_by(representation_mapped_to)));
|
||||
}
|
||||
|
||||
if (representation_processed_as_mapped_item) {
|
||||
@@ -663,6 +663,7 @@ namespace IfcGeom {
|
||||
unit_name = "METER";
|
||||
unit_magnitude = 1.f;
|
||||
|
||||
kernel->setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_ORIENT, settings.get(IteratorSettings::SEW_SHELLS) ? std::numeric_limits<double>::infinity() : -1);
|
||||
kernel->setValue(IfcGeom::Kernel::GV_DIMENSIONALITY, (settings.get(IteratorSettings::INCLUDE_CURVES)
|
||||
? (settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES) ? -1. : 0.) : +1.));
|
||||
if (settings.get(IteratorSettings::BUILDING_LOCAL_PLACEMENT)) {
|
||||
|
||||
@@ -84,6 +84,9 @@ if ( it != cache.T.end() ) { e = it->second; return true; }
|
||||
#define INCLUDE_SCHEMA(x) STRINGIFY(../../../ifcparse/x.h)
|
||||
#include INCLUDE_SCHEMA(IfcSchema)
|
||||
#undef INCLUDE_SCHEMA
|
||||
#define INCLUDE_SCHEMA(x) STRINGIFY(../../../ifcparse/x-definitions.h)
|
||||
#include INCLUDE_SCHEMA(IfcSchema)
|
||||
#undef INCLUDE_SCHEMA
|
||||
|
||||
namespace IfcGeom {
|
||||
class IFC_GEOM_API geometry_exception : public std::exception {
|
||||
@@ -123,6 +126,7 @@ private:
|
||||
class faceset_helper {
|
||||
private:
|
||||
MAKE_TYPE_NAME(Kernel)* kernel_;
|
||||
std::set<const IfcSchema::IfcPolyLoop*> duplicates_;
|
||||
std::map<int, int> vertex_mapping_;
|
||||
std::map<std::pair<int, int>, TopoDS_Edge> edges_;
|
||||
double eps_;
|
||||
@@ -177,6 +181,9 @@ private:
|
||||
}
|
||||
|
||||
bool wire(const IfcSchema::IfcPolyLoop* loop, TopoDS_Wire& wire) {
|
||||
if (duplicates_.find(loop) != duplicates_.end()) {
|
||||
return false;
|
||||
}
|
||||
BRep_Builder builder;
|
||||
builder.MakeWire(wire);
|
||||
int count = 0;
|
||||
@@ -232,8 +239,7 @@ public:
|
||||
|
||||
MAKE_TYPE_NAME(Kernel)& operator=(const MAKE_TYPE_NAME(Kernel)& other) {
|
||||
setValue(GV_DEFLECTION_TOLERANCE, other.getValue(GV_DEFLECTION_TOLERANCE));
|
||||
setValue(GV_WIRE_CREATION_TOLERANCE, other.getValue(GV_WIRE_CREATION_TOLERANCE));
|
||||
setValue(GV_POINT_EQUALITY_TOLERANCE, other.getValue(GV_POINT_EQUALITY_TOLERANCE));
|
||||
setValue(GV_MAX_FACES_TO_ORIENT, other.getValue(GV_MAX_FACES_TO_ORIENT));
|
||||
setValue(GV_LENGTH_UNIT, other.getValue(GV_LENGTH_UNIT));
|
||||
setValue(GV_PLANEANGLE_UNIT, other.getValue(GV_PLANEANGLE_UNIT));
|
||||
setValue(GV_PRECISION, other.getValue(GV_PRECISION));
|
||||
@@ -302,7 +308,8 @@ public:
|
||||
void sequence_of_point_to_wire(const TColgp_SequenceOfPnt&, TopoDS_Wire&, bool closed);
|
||||
bool approximate_plane_through_wire(const TopoDS_Wire&, gp_Pln&, double eps=-1.);
|
||||
bool flatten_wire(TopoDS_Wire&);
|
||||
bool triangulate_wire(const TopoDS_Wire&, TopTools_ListOfShape&);
|
||||
/// Triangulate the set of wires. The firstmost wire is assumed to be the outer wire.
|
||||
bool triangulate_wire(const std::vector<TopoDS_Wire>&, TopTools_ListOfShape&);
|
||||
bool wire_intersections(const TopoDS_Wire & wire, TopTools_ListOfShape & wires);
|
||||
void select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest);
|
||||
|
||||
@@ -321,6 +328,55 @@ public:
|
||||
|
||||
std::pair<std::string, double> initializeUnits(IfcSchema::IfcUnitAssignment*);
|
||||
|
||||
template <typename T> std::pair<IfcSchema::IfcSurfaceStyle*, T*> _get_surface_style(const IfcSchema::IfcStyledItem* si) {
|
||||
#ifdef SCHEMA_HAS_IfcStyleAssignmentSelect
|
||||
IfcEntityList::ptr style_assignments = si->Styles();
|
||||
for (IfcEntityList::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) {
|
||||
if (!(*kt)->declaration().is(IfcSchema::IfcPresentationStyleAssignment::Class())) {
|
||||
continue;
|
||||
}
|
||||
IfcSchema::IfcPresentationStyleAssignment* style_assignment = (IfcSchema::IfcPresentationStyleAssignment*) *kt;
|
||||
#else
|
||||
IfcSchema::IfcPresentationStyleAssignment::list::ptr style_assignments = si->Styles();
|
||||
for (IfcSchema::IfcPresentationStyleAssignment::list::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) {
|
||||
IfcSchema::IfcPresentationStyleAssignment* style_assignment = *kt;
|
||||
#endif
|
||||
IfcEntityList::ptr styles = style_assignment->Styles();
|
||||
for (IfcEntityList::it lt = styles->begin(); lt != styles->end(); ++lt) {
|
||||
IfcUtil::IfcBaseClass* style = *lt;
|
||||
if (style->declaration().is(IfcSchema::IfcSurfaceStyle::Class())) {
|
||||
IfcSchema::IfcSurfaceStyle* surface_style = (IfcSchema::IfcSurfaceStyle*) style;
|
||||
if (surface_style->Side() != IfcSchema::IfcSurfaceSide::IfcSurfaceSide_NEGATIVE) {
|
||||
IfcEntityList::ptr styles_elements = surface_style->Styles();
|
||||
for (IfcEntityList::it mt = styles_elements->begin(); mt != styles_elements->end(); ++mt) {
|
||||
if ((*mt)->declaration().is(T::Class())) {
|
||||
return std::make_pair(surface_style, (T*) *mt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair<IfcSchema::IfcSurfaceStyle*, T*>(0,0);
|
||||
}
|
||||
|
||||
template <typename T> std::pair<IfcSchema::IfcSurfaceStyle*, T*> get_surface_style(const IfcSchema::IfcRepresentationItem* representation_item) {
|
||||
// For certain representation items, most notably boolean operands,
|
||||
// a style definition might reside on one of its operands.
|
||||
representation_item = find_item_carrying_style(representation_item);
|
||||
|
||||
if (representation_item->as<IfcSchema::IfcStyledItem>()) {
|
||||
return _get_surface_style<T>(representation_item->as<IfcSchema::IfcStyledItem>());
|
||||
}
|
||||
IfcSchema::IfcStyledItem::list::ptr styled_items = representation_item->StyledByItem();
|
||||
if (styled_items->size()) {
|
||||
// StyledByItem is a SET [0:1] OF IfcStyledItem, so we return after the first IfcStyledItem:
|
||||
return _get_surface_style<T>(*styled_items->begin());
|
||||
}
|
||||
return std::make_pair<IfcSchema::IfcSurfaceStyle*, T*>(0,0);
|
||||
}
|
||||
|
||||
void purge_cache() {
|
||||
// Rather hack-ish, but a stopgap solution to keep memory under control
|
||||
// for large files. SurfaceStyles need to be kept at all costs, as they
|
||||
|
||||
@@ -77,12 +77,12 @@
|
||||
|
||||
#include <TopLoc_Location.hxx>
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#include "../../../ifcgeom/kernels/opencascade/IfcGeom.h"
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcBSplineCurveWithKnots
|
||||
#include <Geom_BSplineCurve.hxx>
|
||||
#endif
|
||||
|
||||
#include "../../../ifcgeom/kernels/opencascade/IfcGeom.h"
|
||||
|
||||
#define Kernel MAKE_TYPE_NAME(Kernel)
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircle* l, Handle(Geom_Curve)& curve) {
|
||||
@@ -143,7 +143,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcLine* l, Handle(Geom_Curve)& c
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcBSplineCurveWithKnots
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcBSplineCurveWithKnots* l, Handle(Geom_Curve)& curve) {
|
||||
|
||||
const bool is_rational = l->declaration().is(IfcSchema::IfcRationalBSplineCurveWithKnots::Class());
|
||||
|
||||
@@ -97,39 +97,80 @@
|
||||
|
||||
#include <BRepLib_FindSurface.hxx>
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#include "../../../ifcgeom/kernels/opencascade/IfcGeom.h"
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcBSplineSurfaceWithKnots
|
||||
#include <Geom_BSplineSurface.hxx>
|
||||
#include <TColgp_Array2OfPnt.hxx>
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TColStd_Array1OfInteger.hxx>
|
||||
#endif
|
||||
|
||||
#include "../../../ifcgeom/kernels/opencascade/IfcGeom.h"
|
||||
|
||||
#define Kernel MAKE_TYPE_NAME(Kernel)
|
||||
|
||||
namespace {
|
||||
int count_occt(const TopoDS_Shape& s, TopAbs_ShapeEnum t) {
|
||||
IfcGeom::OpenCascadeShape Ss(s);
|
||||
return IfcGeom::Kernel::count(&Ss, (int) t);
|
||||
}
|
||||
/* Returns whether wire conforms to a polyhedron, i.e. only edges with linear curves*/
|
||||
bool is_polyhedron(const TopoDS_Wire& wire) {
|
||||
double a, b;
|
||||
TopLoc_Location l;
|
||||
|
||||
int is_manifold_occt(const TopoDS_Shape& s) {
|
||||
IfcGeom::OpenCascadeShape Ss(s);
|
||||
return IfcGeom::Kernel::is_manifold(&Ss);
|
||||
}
|
||||
}
|
||||
TopoDS_Iterator it(wire, false, false);
|
||||
for (; it.More(); it.Next()) {
|
||||
auto crv = BRep_Tool::Curve(TopoDS::Edge(it.Value()), l, a, b);
|
||||
if (!crv || crv->DynamicType() != STANDARD_TYPE(Geom_Line)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds = l->Bounds();
|
||||
|
||||
// Fail on this early as it can cause issues later on
|
||||
if (bounds->size() == 0) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Handle(Geom_Surface) face_surface;
|
||||
/* A temporary structure to store the intermediate data for the face conversion */
|
||||
class face_definition {
|
||||
private:
|
||||
Handle(Geom_Surface) surface_;
|
||||
std::vector<TopoDS_Wire> wires_;
|
||||
bool all_outer_;
|
||||
public:
|
||||
face_definition() : surface_(), all_outer_(false) {}
|
||||
|
||||
typedef std::vector<TopoDS_Wire>::const_iterator wire_it;
|
||||
|
||||
bool& all_outer() {
|
||||
return all_outer_;
|
||||
}
|
||||
|
||||
bool all_outer() const {
|
||||
return all_outer_;
|
||||
}
|
||||
|
||||
Handle(Geom_Surface)& surface() {
|
||||
return surface_;
|
||||
}
|
||||
|
||||
const Handle(Geom_Surface)& surface() const {
|
||||
return surface_;
|
||||
}
|
||||
|
||||
std::vector<TopoDS_Wire>& wires() {
|
||||
return wires_;
|
||||
}
|
||||
|
||||
const TopoDS_Wire& outer_wire() const {
|
||||
return wires_.front();
|
||||
}
|
||||
|
||||
std::pair<wire_it, wire_it> inner_wires() const {
|
||||
return { wires_.begin() + 1, wires_.end() };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& result) {
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds = l->Bounds();
|
||||
|
||||
face_definition fd;
|
||||
|
||||
const bool is_face_surface = l->declaration().is(IfcSchema::IfcFaceSurface::Class());
|
||||
|
||||
if (is_face_surface) {
|
||||
@@ -144,7 +185,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
|
||||
if (!exp.More()) return false;
|
||||
|
||||
TopoDS_Face surface = TopoDS::Face(exp.Current());
|
||||
face_surface = BRep_Tool::Surface(surface);
|
||||
fd.surface() = BRep_Tool::Surface(surface);
|
||||
}
|
||||
|
||||
const int num_bounds = bounds->size();
|
||||
@@ -164,247 +205,188 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TopoDS_Compound compound;
|
||||
BRep_Builder builder;
|
||||
if (num_outer_bounds > 1) {
|
||||
builder.MakeCompound(compound);
|
||||
Logger::Message(Logger::LOG_WARNING, "Multiple outer boundaries for:", l);
|
||||
fd.all_outer() = true;
|
||||
}
|
||||
|
||||
TopTools_DataMapOfShapeInteger wire_senses;
|
||||
|
||||
// The builder is initialized on the heap because of the various different moments
|
||||
// of initialization depending on the configuration of surfaces and boundaries.
|
||||
BRepBuilderAPI_MakeFace* mf = 0;
|
||||
|
||||
bool success = false;
|
||||
int processed = 0;
|
||||
|
||||
for (int process_interior = 0; process_interior <= 1; ++process_interior) {
|
||||
for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) {
|
||||
IfcSchema::IfcFaceBound* bound = *it;
|
||||
IfcSchema::IfcLoop* loop = bound->Bound();
|
||||
|
||||
|
||||
bool same_sense = bound->Orientation();
|
||||
const bool is_interior =
|
||||
const bool is_interior =
|
||||
!bound->declaration().is(IfcSchema::IfcFaceOuterBound::Class()) &&
|
||||
(num_bounds > 1) &&
|
||||
(num_outer_bounds < num_bounds);
|
||||
|
||||
// The exterior face boundary is processed first
|
||||
if (is_interior == !process_interior) continue;
|
||||
|
||||
|
||||
TopoDS_Wire wire;
|
||||
if (faceset_helper_ && loop->as<IfcSchema::IfcPolyLoop>()) {
|
||||
if (!faceset_helper_->wire(loop->as<IfcSchema::IfcPolyLoop>(), wire)) {
|
||||
Logger::Message(Logger::LOG_WARNING, "Face boundary loop not included", loop);
|
||||
delete mf;
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
} else if (!convert_wire(loop, wire)) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary loop", loop);
|
||||
delete mf;
|
||||
return false;
|
||||
}
|
||||
|
||||
// The approach below does not result in a significant speed-up
|
||||
if (loop->as<IfcSchema::IfcPolyLoop>() && processed == 0 && face_surface.IsNull()) {
|
||||
TopExp_Explorer exp(wire, TopAbs_EDGE);
|
||||
int count = 0;
|
||||
TopoDS_Edge edges[2];
|
||||
for (; exp.More(); exp.Next(), count++) {
|
||||
if (count < 2) {
|
||||
edges[count] = TopoDS::Edge(exp.Current());
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 3) {
|
||||
// Help Open Cascade by finding the plane more efficiently
|
||||
double _, __;
|
||||
Handle(Geom_Line) c1 = Handle(Geom_Line)::DownCast(BRep_Tool::Curve(edges[0], _, __));
|
||||
Handle(Geom_Line) c2 = Handle(Geom_Line)::DownCast(BRep_Tool::Curve(edges[1], _, __));
|
||||
|
||||
const gp_Vec ab = c1->Position().Direction();
|
||||
const gp_Vec ac = c2->Position().Direction();
|
||||
const gp_Vec cross = ab.Crossed(ac);
|
||||
|
||||
if (cross.SquareMagnitude() > ALMOST_ZERO) {
|
||||
const gp_Dir n = cross;
|
||||
face_surface = new Geom_Plane(c1->Position().Location(), n);
|
||||
}
|
||||
} else {
|
||||
gp_Pln pln;
|
||||
if (approximate_plane_through_wire(wire, pln)) {
|
||||
face_surface = new Geom_Plane(pln);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!same_sense) {
|
||||
wire.Reverse();
|
||||
}
|
||||
|
||||
wire_senses.Bind(wire.Oriented(TopAbs_FORWARD), same_sense ? TopAbs_FORWARD : TopAbs_REVERSED);
|
||||
|
||||
bool flattened_wire = false;
|
||||
|
||||
if (!mf) {
|
||||
process_wire:
|
||||
fd.wires().emplace_back(wire);
|
||||
}
|
||||
}
|
||||
|
||||
if (face_surface.IsNull()) {
|
||||
gp_Pln pln;
|
||||
if (count_occt(wire, TopAbs_EDGE) > 128 && approximate_plane_through_wire(wire, pln)) {
|
||||
// tfk: optimization find the underlying surface ourselves since it's going
|
||||
// to be planar in IFC if no explicit surface is given. Should we always do this?
|
||||
// @todo is this still relevant considering the code above
|
||||
mf = new BRepBuilderAPI_MakeFace(pln, wire, true);
|
||||
} else {
|
||||
BRepLib_FindSurface fs(wire, getValue(GV_PRECISION), true, true);
|
||||
if (fs.Found()) {
|
||||
mf = new BRepBuilderAPI_MakeFace(fs.Surface(), wire);
|
||||
ShapeFix_ShapeTolerance ftol;
|
||||
ftol.SetTolerance(wire, fs.ToleranceReached(), TopAbs_WIRE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/// @todo check necessity of false here
|
||||
mf = new BRepBuilderAPI_MakeFace(face_surface, wire, false);
|
||||
if (fd.wires().empty()) {
|
||||
Logger::Warning("Face with no boundaries", l);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fd.surface().IsNull()) {
|
||||
// Use the first wire to find a plane manually for polygonal wires
|
||||
const TopoDS_Wire& wire = fd.wires().front();
|
||||
if (is_polyhedron(wire)) {
|
||||
TopExp_Explorer exp(wire, TopAbs_EDGE);
|
||||
int count = 0;
|
||||
TopoDS_Edge edges[2];
|
||||
for (; exp.More(); exp.Next(), count++) {
|
||||
if (count < 2) {
|
||||
edges[count] = TopoDS::Edge(exp.Current());
|
||||
}
|
||||
}
|
||||
|
||||
if (mf && mf->IsDone()) {
|
||||
TopoDS_Face outer_face_bound = mf->Face();
|
||||
if (count == 3) {
|
||||
// Help Open Cascade by finding the plane more efficiently
|
||||
double _, __;
|
||||
Handle(Geom_Line) c1 = Handle(Geom_Line)::DownCast(BRep_Tool::Curve(edges[0], _, __));
|
||||
Handle(Geom_Line) c2 = Handle(Geom_Line)::DownCast(BRep_Tool::Curve(edges[1], _, __));
|
||||
|
||||
// In case of (non-planar) face surface, p-curves need to be computed.
|
||||
// For planar faces, Open Cascade generates p-curves on the fly.
|
||||
if (!face_surface.IsNull() && face_surface->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
||||
TopExp_Explorer exp(outer_face_bound, TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(exp.Current());
|
||||
ShapeFix_Edge fix_edge;
|
||||
fix_edge.FixAddPCurve(edge, outer_face_bound, false, getValue(GV_PRECISION));
|
||||
}
|
||||
}
|
||||
|
||||
if (BRepCheck_Face(outer_face_bound).OrientationOfWires() == BRepCheck_BadOrientationOfSubshape) {
|
||||
wire.Reverse();
|
||||
same_sense = !same_sense;
|
||||
delete mf;
|
||||
if (face_surface.IsNull()) {
|
||||
mf = new BRepBuilderAPI_MakeFace(wire);
|
||||
} else {
|
||||
mf = new BRepBuilderAPI_MakeFace(face_surface, wire);
|
||||
}
|
||||
ShapeFix_Face fix(mf->Face());
|
||||
fix.FixOrientation();
|
||||
outer_face_bound = fix.Face();
|
||||
}
|
||||
const gp_Vec ab = c1->Position().Direction();
|
||||
const gp_Vec ac = c2->Position().Direction();
|
||||
const gp_Vec cross = ab.Crossed(ac);
|
||||
|
||||
if (num_outer_bounds > 1) {
|
||||
builder.Add(compound, outer_face_bound);
|
||||
delete mf; mf = 0;
|
||||
} else if (num_bounds > 1) {
|
||||
// Reinitialize the builder to the outer face
|
||||
// bound in order to add holes more robustly.
|
||||
delete mf;
|
||||
// TODO: What about the face_surface?
|
||||
mf = new BRepBuilderAPI_MakeFace(outer_face_bound);
|
||||
} else {
|
||||
face = outer_face_bound;
|
||||
success = true;
|
||||
}
|
||||
} else {
|
||||
// if mf == nullptr, it means we failed to find a surface earlier using BRepLib_FindSurface
|
||||
const bool non_planar = mf == nullptr || mf->Error() == BRepBuilderAPI_NotPlanar;
|
||||
delete mf;
|
||||
|
||||
if (non_planar && bounds->size() == 1 && face_surface.IsNull()) {
|
||||
Logger::Message(Logger::LOG_WARNING, "Triangulating face boundary", bound);
|
||||
|
||||
// When creating a solid, flatting the boundary only postpones the issue to
|
||||
// creating a topological manifold out of the individual faces.
|
||||
TopTools_ListOfShape face_list;
|
||||
triangulate_wire(wire, face_list);
|
||||
|
||||
TopoDS_Compound triangulation_compound;
|
||||
BRep_Builder triangulation_builder;
|
||||
triangulation_builder.MakeCompound(triangulation_compound);
|
||||
|
||||
TopTools_ListIteratorOfListOfShape face_iterator;
|
||||
for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) {
|
||||
triangulation_builder.Add(triangulation_compound, face_iterator.Value());
|
||||
}
|
||||
|
||||
face = triangulation_compound;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!non_planar || flattened_wire || !flatten_wire(wire)) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary", bound);
|
||||
return false;
|
||||
} else {
|
||||
Logger::Message(Logger::LOG_ERROR, "Flattening face boundary", bound);
|
||||
flattened_wire = true;
|
||||
goto process_wire;
|
||||
}
|
||||
if (cross.SquareMagnitude() > ALMOST_ZERO) {
|
||||
const gp_Dir n = cross;
|
||||
fd.surface() = new Geom_Plane(c1->Position().Location(), n);
|
||||
}
|
||||
|
||||
} else {
|
||||
mf->Add(wire);
|
||||
gp_Pln pln;
|
||||
if (approximate_plane_through_wire(wire, pln)) {
|
||||
fd.surface() = new Geom_Plane(pln);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same as above:
|
||||
// In case of (non-planar) face surface, p-curves need to be computed.
|
||||
if (BRep_Tool::Surface(mf->Face())->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
||||
TopExp_Explorer exp(wire, TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(exp.Current());
|
||||
if (fd.surface().IsNull()) {
|
||||
// BRepLib_FindSurface is used in case no surface is found or provided
|
||||
|
||||
const TopoDS_Wire& wire = fd.wires().front();
|
||||
|
||||
BRepLib_FindSurface fs(wire, getValue(GV_PRECISION), true, true);
|
||||
if (fs.Found()) {
|
||||
fd.surface() = fs.Surface();
|
||||
ShapeFix_ShapeTolerance ftol;
|
||||
ftol.SetTolerance(wire, fs.ToleranceReached(), TopAbs_WIRE);
|
||||
}
|
||||
}
|
||||
|
||||
TopTools_ListOfShape face_list;
|
||||
|
||||
if (fd.surface().IsNull()) {
|
||||
// The set of wires is triangulated in case no surface can be found
|
||||
Logger::Message(Logger::LOG_WARNING, "Triangulating face boundaries for face", l);
|
||||
|
||||
if (fd.all_outer()) {
|
||||
for (const auto& w : fd.wires()) {
|
||||
TopTools_ListOfShape fl;
|
||||
triangulate_wire({ w }, fl);
|
||||
face_list.Append(fl);
|
||||
}
|
||||
} else {
|
||||
triangulate_wire(fd.wires(), face_list);
|
||||
}
|
||||
} else if (!fd.all_outer()) {
|
||||
BRepBuilderAPI_MakeFace mf(fd.surface(), fd.outer_wire());
|
||||
|
||||
if (mf.IsDone()) {
|
||||
// Is this necessary
|
||||
TopoDS_Face f = mf.Face();
|
||||
mf.Init(f);
|
||||
|
||||
for (auto it = fd.inner_wires().first; it != fd.inner_wires().second; ++it) {
|
||||
mf.Add(*it);
|
||||
}
|
||||
|
||||
face_list.Append(mf.Face());
|
||||
}
|
||||
} else {
|
||||
for (const auto& w : fd.wires()) {
|
||||
BRepBuilderAPI_MakeFace mf(fd.surface(), w);
|
||||
if (mf.IsDone()) {
|
||||
face_list.Append(mf.Face());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fd.surface().IsNull()) {
|
||||
// Some fixes for orientation and p-curves. If we have no surface, it
|
||||
// means the face has been triangulated in which case none of these
|
||||
// fixes are necessary.
|
||||
|
||||
if (fd.surface()->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
||||
// In case of (non-planar) face surface, p-curves need to be computed.
|
||||
// For planar faces, Open Cascade generates p-curves on the fly.
|
||||
|
||||
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
|
||||
// Small chance there are multiple faces
|
||||
const TopoDS_Face& face = TopoDS::Face(it.Value());
|
||||
for (TopExp_Explorer exp2(face, TopAbs_EDGE); exp2.More(); exp2.Next()) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(exp2.Current());
|
||||
ShapeFix_Edge fix_edge;
|
||||
fix_edge.FixAddPCurve(edge, mf->Face(), false, getValue(GV_PRECISION));
|
||||
fix_edge.FixAddPCurve(edge, face, false, getValue(GV_PRECISION));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
|
||||
const TopoDS_Face& face = TopoDS::Face(it.Value());
|
||||
|
||||
ShapeFix_Face sfs(TopoDS::Face(face));
|
||||
TopTools_DataMapOfShapeListOfShape wire_map;
|
||||
sfs.FixOrientation(wire_map);
|
||||
|
||||
TopoDS_Iterator jt(face, false);
|
||||
for (; jt.More(); jt.Next()) {
|
||||
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
|
||||
// tfk: @todo if wire_map contains w, I would assume wire_senses also contains w,
|
||||
// this is not the case in github issue #405.
|
||||
if (wire_map.IsBound(w) && wire_senses.IsBound(w)) {
|
||||
const TopTools_ListOfShape& shapes = wire_map.Find(w);
|
||||
TopTools_ListIteratorOfListOfShape kt(shapes);
|
||||
for (; kt.More(); kt.Next()) {
|
||||
// Apparently the wire got reversed, so register it with opposite orientation in the map
|
||||
wire_senses.Bind(kt.Value(), wire_senses.Find(w) == TopAbs_FORWARD ? TopAbs_REVERSED : TopAbs_FORWARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
processed ++;
|
||||
|
||||
it.Value() = sfs.Face();
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
success = processed == num_bounds;
|
||||
if (success) {
|
||||
if (num_outer_bounds > 1) {
|
||||
face = compound;
|
||||
} else {
|
||||
success = success && mf->IsDone();
|
||||
if (success) {
|
||||
face = mf->Face();
|
||||
}
|
||||
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
|
||||
TopoDS_Face& face = TopoDS::Face(it.Value());
|
||||
|
||||
ShapeFix_Face sfs(TopoDS::Face(face));
|
||||
TopTools_DataMapOfShapeListOfShape wire_map;
|
||||
sfs.FixOrientation(wire_map);
|
||||
|
||||
TopoDS_Iterator jt(face, false);
|
||||
for (; jt.More(); jt.Next()) {
|
||||
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
|
||||
// tfk: @todo if wire_map contains w, I would assume wire_senses also contains w,
|
||||
// this is not the case in github issue #405.
|
||||
if (wire_map.IsBound(w) && wire_senses.IsBound(w)) {
|
||||
const TopTools_ListOfShape& shapes = wire_map.Find(w);
|
||||
TopTools_ListIteratorOfListOfShape it(shapes);
|
||||
for (; it.More(); it.Next()) {
|
||||
// Apparently the wire got reversed, so register it with opposite orientation in the map
|
||||
wire_senses.Bind(it.Value(), wire_senses.Find(w) == TopAbs_FORWARD ? TopAbs_REVERSED : TopAbs_FORWARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
face = TopoDS::Face(sfs.Face());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
// If the wires are reversed the face needs to be reversed as well in order
|
||||
// to maintain the counter-clock-wise ordering of the bounding wire's vertices.
|
||||
if (num_bounds == 1 || true) {
|
||||
bool all_reversed = true;
|
||||
TopoDS_Iterator jt(face, false);
|
||||
for (; jt.More(); jt.Next()) {
|
||||
@@ -420,8 +402,20 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
|
||||
}
|
||||
}
|
||||
|
||||
delete mf;
|
||||
return success;
|
||||
if (face_list.Extent() > 1) {
|
||||
TopoDS_Compound compound;
|
||||
BRep_Builder builder;
|
||||
builder.MakeCompound(compound);
|
||||
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
|
||||
TopoDS_Face& face = TopoDS::Face(it.Value());
|
||||
builder.Add(compound, face);
|
||||
}
|
||||
result = compound;
|
||||
} else {
|
||||
result = face_list.First();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryClosedProfileDef* l, TopoDS_Shape& face) {
|
||||
@@ -478,7 +472,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleProfileDef* l, TopoDS
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -501,7 +495,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRoundedRectangleProfileDef* l,
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -535,7 +529,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleHollowProfileDef* l,
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -581,7 +575,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrapeziumProfileDef* l, TopoDS
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -626,7 +620,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcIShapeProfileDef* l, TopoDS_Sh
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -665,7 +659,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcZShapeProfileDef* l, TopoDS_Sh
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -698,7 +692,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCShapeProfileDef* l, TopoDS_Sh
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -773,7 +767,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcLShapeProfileDef* l, TopoDS_Sh
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -821,7 +815,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcUShapeProfileDef* l, TopoDS_Sh
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -910,7 +904,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTShapeProfileDef* l, TopoDS_Sh
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -932,7 +926,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircleProfileDef* l, TopoDS_Sh
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -964,7 +958,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircleHollowProfileDef* l, Top
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -1002,7 +996,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcEllipseProfileDef* l, TopoDS_S
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -1130,7 +1124,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPlane* l, TopoDS_Shape& face)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcBSplineSurfaceWithKnots
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcBSplineSurfaceWithKnots* l, TopoDS_Shape& face) {
|
||||
boost::shared_ptr< IfcTemplatedEntityListList<IfcSchema::IfcCartesianPoint> > cps = l->ControlPointsList();
|
||||
|
||||
@@ -374,7 +374,7 @@ namespace {
|
||||
|
||||
IfcGeom::impl::tree<int> tree;
|
||||
|
||||
// Add edges to tree
|
||||
// Add faces to tree
|
||||
for (int i = 1; i <= faces.Extent(); ++i) {
|
||||
if (BRep_Tool::Surface(TopoDS::Face(faces(i)))->DynamicType() == STANDARD_TYPE(Geom_Plane)) {
|
||||
tree.add(i, faces(i));
|
||||
@@ -395,9 +395,9 @@ namespace {
|
||||
BRepBndLib::AddClose(f, b);
|
||||
b.Enlarge(max_search);
|
||||
|
||||
std::vector<int> edge_idxs = tree.select_box(b, false);
|
||||
std::vector<int>::const_iterator it = edge_idxs.begin();
|
||||
for (; it != edge_idxs.end(); ++it) {
|
||||
std::vector<int> face_idxs = tree.select_box(b, false);
|
||||
std::vector<int>::const_iterator it = face_idxs.begin();
|
||||
for (; it != face_idxs.end(); ++it) {
|
||||
if (*it == j) {
|
||||
continue;
|
||||
}
|
||||
@@ -419,6 +419,8 @@ namespace {
|
||||
double u = d.Dot(p1->Position().XDirection());
|
||||
double v = d.Dot(p1->Position().YDirection());
|
||||
|
||||
// nb: TopAbs_ON is explicitly not considered to prevent matching adjacent faces
|
||||
// with similar orientations.
|
||||
if (cls.Perform(gp_Pnt2d(u, v)) == TopAbs_IN) {
|
||||
gp_Pnt test2;
|
||||
p1->D0(u, v, test2);
|
||||
@@ -458,16 +460,15 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef UNIFY_OPERANDS
|
||||
TopoDS_Shape unify(const TopoDS_Shape& s) {
|
||||
TopoDS_Shape unify(const TopoDS_Shape& s, double tolerance) {
|
||||
tolerance = (std::min)(min_edge_length(s) / 2., tolerance);
|
||||
ShapeUpgrade_UnifySameDomain usd(s);
|
||||
usd.SetLinearTolerance(Precision::Confusion() * 10.);
|
||||
usd.SetAngularTolerance(Precision::Angular() * 10.);
|
||||
usd.SetSafeInputMode(true);
|
||||
usd.SetLinearTolerance(tolerance);
|
||||
usd.SetAngularTolerance(1.e-3);
|
||||
usd.Build();
|
||||
return usd.Shape();
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -1438,7 +1439,7 @@ bool IfcGeom::Kernel::convert_layerset(const IfcSchema::IfcProduct* product, std
|
||||
gp_Trsf extrusion_position;
|
||||
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL
|
||||
has_position = extrusion->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -1486,6 +1487,8 @@ bool IfcGeom::Kernel::convert_layerset(const IfcSchema::IfcProduct* product, std
|
||||
}
|
||||
|
||||
if (positive) {
|
||||
std::reverse(thicknesses.begin(), thicknesses.end());
|
||||
std::reverse(styles.begin(), styles.end());
|
||||
std::reverse(surfaces.begin(), surfaces.end());
|
||||
}
|
||||
|
||||
@@ -2532,7 +2535,7 @@ bool IfcGeom::Kernel::flatten_wire(TopoDS_Wire& wire) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::triangulate_wire(const TopoDS_Wire& wire, TopTools_ListOfShape& faces) {
|
||||
bool IfcGeom::Kernel::triangulate_wire(const std::vector<TopoDS_Wire>& wires, TopTools_ListOfShape& faces) {
|
||||
// This is a bit of a precarious approach, but seems to work for the
|
||||
// versions of OCCT tested for. OCCT has a Delaunay triangulation function
|
||||
// BRepMesh_Delaun, but it is notoriously hard to interpret the results
|
||||
@@ -2549,52 +2552,70 @@ bool IfcGeom::Kernel::triangulate_wire(const TopoDS_Wire& wire, TopTools_ListOfS
|
||||
typedef std::pair<double, double> uv_node;
|
||||
|
||||
gp_Pln pln;
|
||||
if (!approximate_plane_through_wire(wire, pln, std::numeric_limits<double>::infinity())) {
|
||||
if (!approximate_plane_through_wire(wires.front(), pln, std::numeric_limits<double>::infinity())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const gp_XYZ& udir = pln.Position().XDirection().XYZ();
|
||||
const gp_XYZ& vdir = pln.Position().YDirection().XYZ();
|
||||
const gp_XYZ& pnt = pln.Position().Location().XYZ();
|
||||
|
||||
BRepTools_WireExplorer exp(wire);
|
||||
BRepBuilderAPI_MakePolygon mp;
|
||||
|
||||
std::map<uv_node, TopoDS_Vertex> mapping;
|
||||
std::map<std::pair<uv_node, uv_node>, TopoDS_Edge> existing_edges, new_edges;
|
||||
|
||||
// Add UV coordinates to a newly created polygon
|
||||
for (; exp.More(); exp.Next()) {
|
||||
// Project onto plane
|
||||
const TopoDS_Vertex& V = exp.CurrentVertex();
|
||||
gp_Pnt p = BRep_Tool::Pnt(V);
|
||||
double u = (p.XYZ() - pnt).Dot(udir);
|
||||
double v = (p.XYZ() - pnt).Dot(vdir);
|
||||
mp.Add(gp_Pnt(u, v, 0.));
|
||||
std::unique_ptr<BRepBuilderAPI_MakeFace> mf;
|
||||
|
||||
mapping.insert(std::make_pair(std::make_pair(u, v), V));
|
||||
for (auto it = wires.begin(); it != wires.end(); ++it) {
|
||||
const TopoDS_Wire& wire = *it;
|
||||
BRepTools_WireExplorer exp(wire);
|
||||
BRepBuilderAPI_MakePolygon mp;
|
||||
|
||||
// Store existing edges in a map so that triangles can
|
||||
// actually reference the preexisting edges.
|
||||
const TopoDS_Edge& e = exp.Current();
|
||||
TopoDS_Vertex V0, V1;
|
||||
TopExp::Vertices(e, V0, V1, true);
|
||||
gp_Pnt p0 = BRep_Tool::Pnt(V0);
|
||||
gp_Pnt p1 = BRep_Tool::Pnt(V1);
|
||||
double u0 = (p0.XYZ() - pnt).Dot(udir);
|
||||
double v0 = (p0.XYZ() - pnt).Dot(vdir);
|
||||
double u1 = (p1.XYZ() - pnt).Dot(udir);
|
||||
double v1 = (p1.XYZ() - pnt).Dot(vdir);
|
||||
uv_node uv0 = std::make_pair(u0, v0);
|
||||
uv_node uv1 = std::make_pair(u1, v1);
|
||||
existing_edges.insert(std::make_pair(std::make_pair(uv0, uv1), e));
|
||||
existing_edges.insert(std::make_pair(std::make_pair(uv1, uv0), TopoDS::Edge(e.Reversed())));
|
||||
// Add UV coordinates to a newly created polygon
|
||||
for (; exp.More(); exp.Next()) {
|
||||
// Project onto plane
|
||||
const TopoDS_Vertex& V = exp.CurrentVertex();
|
||||
gp_Pnt p = BRep_Tool::Pnt(V);
|
||||
double u = (p.XYZ() - pnt).Dot(udir);
|
||||
double v = (p.XYZ() - pnt).Dot(vdir);
|
||||
mp.Add(gp_Pnt(u, v, 0.));
|
||||
|
||||
mapping.insert(std::make_pair(std::make_pair(u, v), V));
|
||||
|
||||
// Store existing edges in a map so that triangles can
|
||||
// actually reference the preexisting edges.
|
||||
const TopoDS_Edge& e = exp.Current();
|
||||
TopoDS_Vertex V0, V1;
|
||||
TopExp::Vertices(e, V0, V1, true);
|
||||
gp_Pnt p0 = BRep_Tool::Pnt(V0);
|
||||
gp_Pnt p1 = BRep_Tool::Pnt(V1);
|
||||
double u0 = (p0.XYZ() - pnt).Dot(udir);
|
||||
double v0 = (p0.XYZ() - pnt).Dot(vdir);
|
||||
double u1 = (p1.XYZ() - pnt).Dot(udir);
|
||||
double v1 = (p1.XYZ() - pnt).Dot(vdir);
|
||||
uv_node uv0 = std::make_pair(u0, v0);
|
||||
uv_node uv1 = std::make_pair(u1, v1);
|
||||
existing_edges.insert(std::make_pair(std::make_pair(uv0, uv1), e));
|
||||
existing_edges.insert(std::make_pair(std::make_pair(uv1, uv0), TopoDS::Edge(e.Reversed())));
|
||||
}
|
||||
|
||||
// Not closed by default
|
||||
mp.Close();
|
||||
|
||||
if (mf) {
|
||||
if (it - 1 == wires.begin()) {
|
||||
// @todo is this necessary?
|
||||
TopoDS_Face f = mf->Face();
|
||||
mf->Init(f);
|
||||
}
|
||||
mf->Add(mp.Wire());
|
||||
} else {
|
||||
mf.reset(new BRepBuilderAPI_MakeFace(mp.Wire()));
|
||||
}
|
||||
}
|
||||
|
||||
// Not closed by default
|
||||
mp.Close();
|
||||
const TopoDS_Face& face = mf->Face();
|
||||
|
||||
// Create a new face from the {u,v,0} wire and mesh the face
|
||||
TopoDS_Face face = BRepBuilderAPI_MakeFace(mp.Wire());
|
||||
// Create a triangular mesh from the face
|
||||
BRepMesh_IncrementalMesh(face, Precision::Confusion());
|
||||
|
||||
int n123[3];
|
||||
@@ -2655,13 +2676,13 @@ bool IfcGeom::Kernel::triangulate_wire(const TopoDS_Wire& wire, TopTools_ListOfS
|
||||
}
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeFace mf(mp2.Wire());
|
||||
if (mf.IsDone()) {
|
||||
TopoDS_Face triangle_face = mf.Face();
|
||||
BRepBuilderAPI_MakeFace mft(mp2.Wire());
|
||||
if (mft.IsDone()) {
|
||||
TopoDS_Face triangle_face = mft.Face();
|
||||
TopoDS_Iterator jt(triangle_face, false);
|
||||
for (; jt.More(); jt.Next()) {
|
||||
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
|
||||
if (w.Orientation() != wire.Orientation()) {
|
||||
if (w.Orientation() != wires.front().Orientation()) {
|
||||
triangle_face.Reverse();
|
||||
}
|
||||
}
|
||||
@@ -2674,7 +2695,9 @@ bool IfcGeom::Kernel::triangulate_wire(const TopoDS_Wire& wire, TopTools_ListOfS
|
||||
}
|
||||
|
||||
TopTools_IndexedDataMapOfShapeListOfShape mape, mapn;
|
||||
TopExp::MapShapesAndAncestors(wire, TopAbs_EDGE, TopAbs_WIRE, mape);
|
||||
for (auto& wire : wires) {
|
||||
TopExp::MapShapesAndAncestors(wire, TopAbs_EDGE, TopAbs_WIRE, mape);
|
||||
}
|
||||
TopTools_ListIteratorOfListOfShape it(faces);
|
||||
for (; it.More(); it.Next()) {
|
||||
TopExp::MapShapesAndAncestors(it.Value(), TopAbs_EDGE, TopAbs_WIRE, mapn);
|
||||
@@ -3162,20 +3185,21 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopoDS_Shap
|
||||
|
||||
bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_, const TopTools_ListOfShape& b__, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) {
|
||||
|
||||
#ifdef UNIFY_OPERANDS
|
||||
TopoDS_Shape a = unify(a_);
|
||||
if (fuzziness < 0.) {
|
||||
fuzziness = getValue(GV_PRECISION) / 10.;
|
||||
}
|
||||
|
||||
// @todo, it does seem a bit odd, we first triangulate non-planar faces
|
||||
// to later unify them again. Can we make this a bit more intelligent?
|
||||
TopoDS_Shape a = unify(a_, fuzziness);
|
||||
TopTools_ListOfShape b_;
|
||||
{
|
||||
TopTools_ListIteratorOfListOfShape it(b__);
|
||||
for (; it.More(); it.Next()) {
|
||||
b_.Append(unify(it.Value()));
|
||||
b_.Append(unify(it.Value(), fuzziness));
|
||||
}
|
||||
}
|
||||
#else
|
||||
const TopoDS_Shape& a = a_;
|
||||
const TopTools_ListOfShape& b_ = b__;
|
||||
#endif
|
||||
|
||||
|
||||
bool success = false;
|
||||
BRepAlgoAPI_BooleanOperation* builder;
|
||||
TopTools_ListOfShape B, b;
|
||||
@@ -3197,15 +3221,11 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_, const TopTools_L
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fuzziness < 0.) {
|
||||
fuzziness = getValue(GV_PRECISION);
|
||||
}
|
||||
|
||||
// Find a sensible value for the fuzziness, based on precision
|
||||
// and limited by edge lengths and vertex-edge distances.
|
||||
const double len_a = min_edge_length(a);
|
||||
double min_length_orig = (std::min)(len_a, min_vertex_edge_distance(a, getValue(GV_PRECISION), len_a));
|
||||
TopTools_ListIteratorOfListOfShape it(b);
|
||||
const double len_a = min_edge_length(a_);
|
||||
double min_length_orig = (std::min)(len_a, min_vertex_edge_distance(a_, getValue(GV_PRECISION), len_a));
|
||||
TopTools_ListIteratorOfListOfShape it(b__);
|
||||
for (; it.More(); it.Next()) {
|
||||
double d = min_edge_length(it.Value());
|
||||
if (d < min_length_orig) {
|
||||
@@ -3217,7 +3237,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_, const TopTools_L
|
||||
}
|
||||
}
|
||||
|
||||
const double fuzz = (std::min)(min_length_orig / 10., fuzziness);
|
||||
const double fuzz = (std::min)(min_length_orig / 3., fuzziness);
|
||||
|
||||
TopTools_ListOfShape s1s;
|
||||
s1s.Append(copy_operand(a));
|
||||
@@ -3255,13 +3275,13 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_, const TopTools_L
|
||||
// output is not trusted and the operation is attempted with a higher fuzziness.
|
||||
int reason = 0;
|
||||
double v;
|
||||
if ((v = min_edge_length(r)) < fuzziness * 10.) {
|
||||
if ((v = min_edge_length(r)) < fuzziness * 3.) {
|
||||
reason = 0;
|
||||
success = false;
|
||||
} else if ((v = min_vertex_edge_distance(r, getValue(GV_PRECISION), fuzziness * 10.)) < fuzziness * 10.) {
|
||||
} else if ((v = min_vertex_edge_distance(r, getValue(GV_PRECISION), fuzziness * 3.)) < fuzziness * 3.) {
|
||||
reason = 1;
|
||||
success = false;
|
||||
} else if ((v = min_face_face_distance(r, fuzziness * 10.)) < fuzziness * 10.) {
|
||||
} else if ((v = min_face_face_distance(r, fuzziness * 3.)) < fuzziness * 3.) {
|
||||
reason = 2;
|
||||
success = false;
|
||||
}
|
||||
@@ -3401,19 +3421,36 @@ IfcGeom::Kernel::faceset_helper::faceset_helper(Kernel* kernel, const IfcSchema:
|
||||
}
|
||||
}
|
||||
|
||||
// @todo, there a tiny possibility that the duplicate faces are triggered
|
||||
// for an internal boundary, that is also present as an external boundary.
|
||||
// This will result in non-manifold configuration then, but this is deemed
|
||||
// such as corner-case that it is not considered.
|
||||
IfcSchema::IfcPolyLoop::list::ptr loops = IfcParse::traverse((IfcUtil::IfcBaseClass*)l)->as<IfcSchema::IfcPolyLoop>();
|
||||
|
||||
size_t loops_removed = 0, non_manifold = 0;
|
||||
size_t loops_removed = 0, non_manifold = 0, duplicate_faces = 0;
|
||||
|
||||
typedef std::array<int, 2> edge_t;
|
||||
typedef std::set<edge_t> edge_set_t;
|
||||
std::set<edge_set_t> edge_sets;
|
||||
|
||||
for (auto& loop : *loops) {
|
||||
auto ps = loop->Polygon();
|
||||
|
||||
std::vector<std::pair<int, int> > segments;
|
||||
edge_set_t segment_set;
|
||||
|
||||
loop_(ps, [&segments](int C, int D, bool) {
|
||||
loop_(ps, [&segments, &segment_set](int C, int D, bool) {
|
||||
segment_set.insert({{ C, D }});
|
||||
segments.push_back({ C, D });
|
||||
});
|
||||
|
||||
if (edge_sets.find(segment_set) != edge_sets.end()) {
|
||||
duplicate_faces++;
|
||||
duplicates_.insert(loop);
|
||||
continue;
|
||||
}
|
||||
edge_sets.insert(segment_set);
|
||||
|
||||
if (segments.size() >= 3) {
|
||||
for (auto& p : segments) {
|
||||
edge_use[p] ++;
|
||||
@@ -3434,7 +3471,7 @@ IfcGeom::Kernel::faceset_helper::faceset_helper(Kernel* kernel, const IfcSchema:
|
||||
}
|
||||
|
||||
if (loops_removed || (non_manifold && l->declaration().is(IfcSchema::IfcClosedShell::Class()))) {
|
||||
Logger::Warning(boost::lexical_cast<std::string>(loops_removed) + " loops removed and " + boost::lexical_cast<std::string>(non_manifold) + " non-manifold edges for:", l);
|
||||
Logger::Warning(boost::lexical_cast<std::string>(duplicate_faces) + " duplicate faces removed, " + boost::lexical_cast<std::string>(loops_removed) + " loops removed and " + boost::lexical_cast<std::string>(non_manifold) + " non-manifold edges for:", l);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3544,4 +3581,6 @@ bool IfcGeom::Kernel::validate_quantities(const IfcSchema::IfcProduct* product,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolid* l, TopoDS_S
|
||||
|
||||
gp_Trsf trsf;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -166,7 +166,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolid* l, TopoDS_S
|
||||
return !shape.IsNull();
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcExtrudedAreaSolidTapered
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolidTapered* l, TopoDS_Shape& shape) {
|
||||
const double height = l->Depth() * getValue(GV_LENGTH_UNIT);
|
||||
if (height < getValue(GV_PRECISION)) {
|
||||
@@ -180,7 +180,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolidTapered* l, T
|
||||
|
||||
gp_Trsf trsf;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -280,7 +280,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceOfLinearExtrusion* l, T
|
||||
|
||||
gp_Trsf trsf;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -315,7 +315,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceOfRevolution* l, TopoDS
|
||||
|
||||
gp_Trsf trsf;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -344,7 +344,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRevolvedAreaSolid* l, TopoDS_S
|
||||
|
||||
gp_Trsf trsf;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -376,7 +376,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcManifoldSolidBrep* l, Conversi
|
||||
if (l->declaration().is(IfcSchema::IfcFacetedBrepWithVoids::Class())) {
|
||||
voids = l->as<IfcSchema::IfcFacetedBrepWithVoids>()->Voids();
|
||||
}
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcAdvancedBrepWithVoids
|
||||
if (l->declaration().is(IfcSchema::IfcAdvancedBrepWithVoids::Class())) {
|
||||
voids = l->as<IfcSchema::IfcAdvancedBrepWithVoids>()->Voids();
|
||||
}
|
||||
@@ -478,7 +478,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcShellBasedSurfaceModel* l, Con
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape& shape) {
|
||||
|
||||
TopoDS_Shape s1, s2;
|
||||
ConversionResults items1, items2;
|
||||
ConversionResults items1;
|
||||
TopoDS_Wire boundary_wire;
|
||||
IfcSchema::IfcBooleanOperand* operand1 = l->FirstOperand();
|
||||
IfcSchema::IfcBooleanOperand* operand2 = l->SecondOperand();
|
||||
@@ -545,24 +545,27 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape
|
||||
|
||||
TopTools_ListOfShape second_operand_shapes;
|
||||
|
||||
for (auto& operand2 : second_operands) {
|
||||
for (auto& op2 : second_operands) {
|
||||
TopoDS_Shape s2;
|
||||
|
||||
bool shape2_processed = false;
|
||||
|
||||
bool is_halfspace = operand2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class());
|
||||
bool is_unbounded_halfspace = is_halfspace && !operand2->declaration().is(IfcSchema::IfcPolygonalBoundedHalfSpace::Class());
|
||||
bool is_halfspace = op2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class());
|
||||
bool is_unbounded_halfspace = is_halfspace && !op2->declaration().is(IfcSchema::IfcPolygonalBoundedHalfSpace::Class());
|
||||
has_halfspace_operand |= is_halfspace;
|
||||
|
||||
{
|
||||
if (shape_type(operand2) == ST_SHAPELIST) {
|
||||
shape2_processed = convert_shapes(operand2, items2) && flatten_shape_list(items2, s2, true);
|
||||
} else if (shape_type(operand2) == ST_SHAPE) {
|
||||
shape2_processed = convert_shape(operand2, s2);
|
||||
if (shape_type(op2) == ST_SHAPELIST) {
|
||||
ConversionResults items2;
|
||||
shape2_processed = convert_shapes(op2, items2) && flatten_shape_list(items2, s2, true);
|
||||
} else if (shape_type(op2) == ST_SHAPE) {
|
||||
shape2_processed = convert_shape(op2, s2);
|
||||
if (shape2_processed) {
|
||||
TopoDS_Solid temp_solid;
|
||||
s2 = ensure_fit_for_subtraction(s2, temp_solid);
|
||||
}
|
||||
} else {
|
||||
Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", operand2);
|
||||
Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", op2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,14 +583,14 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape
|
||||
}
|
||||
|
||||
if (!shape2_processed) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to convert SecondOperand:", operand2);
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to convert SecondOperand:", op2);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (operand2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class())) {
|
||||
if (op2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class())) {
|
||||
const double second_operand_volume = shape_volume(s2);
|
||||
if (second_operand_volume <= ALMOST_ZERO) {
|
||||
Logger::Message(Logger::LOG_WARNING, "Empty solid for:", operand2);
|
||||
Logger::Message(Logger::LOG_WARNING, "Empty solid for:", op2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,16 +604,18 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape
|
||||
TopoDS_Compound compound;
|
||||
builder.MakeCompound(compound);
|
||||
builder.Add(compound, s1);
|
||||
builder.Add(compound, s2);
|
||||
for (const auto& s2 : second_operand_shapes) {
|
||||
builder.Add(compound, s2);
|
||||
}
|
||||
shape = compound;
|
||||
return true;
|
||||
*/
|
||||
|
||||
#if OCC_VERSION_HEX < 0x60900
|
||||
// @todo: this currently does not compile anymore, do we still need this?
|
||||
bool valid_result = boolean_operation(s1, s2, occ_op, shape);
|
||||
#else
|
||||
const double fuzz = has_halfspace_operand ? getValue(GV_PRECISION) * 10. : -1.;
|
||||
bool valid_result = boolean_operation(s1, second_operand_shapes, occ_op, shape, fuzz);
|
||||
bool valid_result = boolean_operation(s1, second_operand_shapes, occ_op, shape);
|
||||
#endif
|
||||
|
||||
if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) {
|
||||
@@ -637,6 +642,10 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Sh
|
||||
|
||||
IfcSchema::IfcFace::list::ptr faces = l->CfsFaces();
|
||||
|
||||
double min_face_area = faceset_helper_
|
||||
? (faceset_helper_->epsilon() * faceset_helper_->epsilon() / 20.)
|
||||
: getValue(GV_MINIMAL_FACE_AREA);
|
||||
|
||||
TopTools_ListOfShape face_list;
|
||||
for (IfcSchema::IfcFace::list::it it = faces->begin(); it != faces->end(); ++it) {
|
||||
bool success = false;
|
||||
@@ -667,18 +676,18 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Sh
|
||||
if (face_it.Value().ShapeType() == TopAbs_FACE) {
|
||||
// This should really be the case. This is not asserted.
|
||||
const TopoDS_Face& triangle = TopoDS::Face(face_it.Value());
|
||||
if (face_area(triangle) > getValue(GV_MINIMAL_FACE_AREA)) {
|
||||
if (face_area(triangle) > min_face_area) {
|
||||
face_list.Append(triangle);
|
||||
} else {
|
||||
Logger::Message(Logger::LOG_WARNING, "Invalid face:", (*it));
|
||||
Logger::Message(Logger::LOG_WARNING, "Degenerate face:", (*it));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (face_area(face) > getValue(GV_MINIMAL_FACE_AREA)) {
|
||||
if (face_area(face) > min_face_area) {
|
||||
face_list.Append(face);
|
||||
} else {
|
||||
Logger::Message(Logger::LOG_WARNING, "Invalid face:", (*it));
|
||||
Logger::Message(Logger::LOG_WARNING, "Degenerate face:", (*it));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -687,7 +696,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Sh
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!create_solid_from_faces(face_list, shape)) {
|
||||
if (face_list.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !create_solid_from_faces(face_list, shape)) {
|
||||
TopoDS_Compound compound;
|
||||
BRep_Builder builder;
|
||||
builder.MakeCompound(compound);
|
||||
@@ -942,7 +951,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceCurveSweptAreaSolid* l,
|
||||
|
||||
gp_Trsf trsf;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
@@ -1100,20 +1109,15 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shap
|
||||
}
|
||||
}
|
||||
|
||||
const bool is_continuous = wire_is_c1_continuous(wire, 1.e-3);
|
||||
// This is not used anymore, BRepBuilderAPI_RightCorner is always used now.
|
||||
// const bool is_continuous = wire_is_c1_continuous(wire, 1.e-3);
|
||||
|
||||
// NB: Note that StartParam and EndParam param are ignored and the assumption is
|
||||
// made that the parametric range over which to be swept matches the IfcCurve in
|
||||
// its entirety.
|
||||
// NB2: Contrary to IfcSurfaceCurveSweptAreaSolid the transition mode has been
|
||||
// set to create round corners as this has proven to work better with the types
|
||||
// of directrices encountered, which do not necessarily conform to a surface.
|
||||
{ BRepOffsetAPI_MakePipeShell builder(wire);
|
||||
builder.Add(section1);
|
||||
if (!is_continuous) {
|
||||
// Only perform round corners on wires that are not c1 continuous
|
||||
builder.SetTransitionMode(BRepBuilderAPI_RoundCorner);
|
||||
}
|
||||
builder.SetTransitionMode(BRepBuilderAPI_RightCorner);
|
||||
builder.Build();
|
||||
builder.MakeSolid();
|
||||
shape = builder.Shape(); }
|
||||
@@ -1121,9 +1125,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shap
|
||||
if (hasInnerRadius) {
|
||||
BRepOffsetAPI_MakePipeShell builder(wire);
|
||||
builder.Add(section2);
|
||||
if (!is_continuous) {
|
||||
builder.SetTransitionMode(BRepBuilderAPI_RoundCorner);
|
||||
}
|
||||
builder.SetTransitionMode(BRepBuilderAPI_RightCorner);
|
||||
builder.Build();
|
||||
builder.MakeSolid();
|
||||
TopoDS_Shape inner = builder.Shape();
|
||||
@@ -1151,7 +1153,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shap
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcCylindricalSurface
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCylindricalSurface* l, TopoDS_Shape& face) {
|
||||
gp_Trsf trsf;
|
||||
@@ -1166,10 +1168,18 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCylindricalSurface* l, TopoDS_
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcAdvancedBrep
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcAdvancedBrep* l, TopoDS_Shape& shape) {
|
||||
return convert(l->Outer(), shape);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcTriangulatedFaceSet
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcTriangulatedFaceSet* l, TopoDS_Shape& shape) {
|
||||
IfcSchema::IfcCartesianPointList3D* point_list = l->Coordinates();
|
||||
const std::vector< std::vector<double> > coordinates = point_list->CoordList();
|
||||
@@ -1230,18 +1240,18 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTriangulatedFaceSet* l, TopoDS
|
||||
bool valid_shell = false;
|
||||
|
||||
// @todo Do this more efficiently by creating proper half-edge pairs.
|
||||
BRepOffsetAPI_Sewing builder;
|
||||
builder.SetTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE));
|
||||
builder.SetMaxTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE));
|
||||
builder.SetMinTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE));
|
||||
BRepOffsetAPI_Sewing sewing_builder;
|
||||
sewing_builder.SetTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE));
|
||||
sewing_builder.SetMaxTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE));
|
||||
sewing_builder.SetMinTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE));
|
||||
|
||||
for (std::vector<TopoDS_Face>::const_iterator it = faces.begin(); it != faces.end(); ++it) {
|
||||
builder.Add(*it);
|
||||
sewing_builder.Add(*it);
|
||||
}
|
||||
|
||||
try {
|
||||
builder.Perform();
|
||||
shape = builder.SewedShape();
|
||||
sewing_builder.Perform();
|
||||
shape = sewing_builder.SewedShape();
|
||||
valid_shell = BRepCheck_Analyzer(shape).IsValid();
|
||||
} catch(...) {}
|
||||
|
||||
|
||||
@@ -269,6 +269,7 @@ namespace IfcGeom {
|
||||
IfcGeom::IteratorSettings settings_ = settings;
|
||||
settings_.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
|
||||
settings_.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true);
|
||||
settings_.set(IfcGeom::IteratorSettings::SEW_SHELLS, true);
|
||||
|
||||
IfcGeom::Iterator<double> it(settings_, &f);
|
||||
|
||||
|
||||
@@ -849,7 +849,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSubedge* l, TopoDS_Wire& resul
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcIndexedPolyCurve
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcIndexedPolyCurve* l, TopoDS_Wire& result) {
|
||||
|
||||
|
||||
@@ -52,12 +52,20 @@ SHAPES(IfcMappedItem);
|
||||
SHAPES(IfcManifoldSolidBrep);
|
||||
SHAPES(IfcGeometricSet);
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcCylindricalSurface
|
||||
SHAPE(IfcCylindricalSurface);
|
||||
#endif
|
||||
#ifdef SCHEMA_HAS_IfcAdvancedBrep
|
||||
SHAPE(IfcAdvancedBrep);
|
||||
#endif
|
||||
// FIXME: Surfaces should have a shape type of their own
|
||||
#ifdef SCHEMA_HAS_IfcBSplineSurfaceWithKnots
|
||||
SHAPE(IfcBSplineSurfaceWithKnots);
|
||||
#endif
|
||||
#ifdef SCHEMA_HAS_IfcTriangulatedFaceSet
|
||||
SHAPE(IfcTriangulatedFaceSet);
|
||||
#endif
|
||||
#ifdef SCHEMA_HAS_IfcExtrudedAreaSolidTapered
|
||||
SHAPE(IfcExtrudedAreaSolidTapered);
|
||||
#endif
|
||||
SHAPE(IfcPlane);
|
||||
@@ -114,14 +122,14 @@ WIRE(IfcPolyLoop);
|
||||
WIRE(IfcCompositeCurve);
|
||||
WIRE(IfcTrimmedCurve);
|
||||
WIRE(IfcArbitraryOpenProfileDef);
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcIndexedPolyCurve
|
||||
WIRE(IfcIndexedPolyCurve)
|
||||
#endif
|
||||
|
||||
CURVE(IfcCircle);
|
||||
CURVE(IfcEllipse);
|
||||
CURVE(IfcLine);
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcBSplineCurveWithKnots
|
||||
// IfcRationalBSplineCurveWithKnots included
|
||||
CURVE(IfcBSplineCurveWithKnots);
|
||||
#endif
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "../../ifcparse/IfcGlobalId.h"
|
||||
#include "../../ifcparse/Argument.h"
|
||||
|
||||
#include "../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h"
|
||||
#include "../../ifcgeom/schema_agnostic/IfcGeomIteratorSettings.h"
|
||||
|
||||
@@ -167,6 +167,8 @@ IfcUtil::IfcBaseEntity* IfcGeom::Kernel::get_decomposing_entity(IfcUtil::IfcBase
|
||||
return get_decomposing_entity_impl(inst->as<Ifc2x3::IfcProduct>(), include_openings);
|
||||
} else if (inst->as<Ifc4::IfcProduct>()) {
|
||||
return get_decomposing_entity_impl(inst->as<Ifc4::IfcProduct>(), include_openings);
|
||||
} else if (inst->declaration().name() == "IfcProject") {
|
||||
return nullptr;
|
||||
} else {
|
||||
throw IfcParse::IfcException("Unexpected entity " + inst->declaration().name());
|
||||
}
|
||||
|
||||
@@ -22,15 +22,15 @@ namespace IfcGeom {
|
||||
// Specifies the deflection of the mesher
|
||||
// Default: 0.001m / 1mm
|
||||
GV_DEFLECTION_TOLERANCE,
|
||||
// Specifies the tolerance of the wire builder, most notably for trimmed curves
|
||||
// Default: 0.0001m / 0.1mm
|
||||
GV_WIRE_CREATION_TOLERANCE,
|
||||
// Specifies the minimal area of a face to be included in an IfcConnectedFaceset
|
||||
// Read-only
|
||||
GV_MINIMAL_FACE_AREA,
|
||||
// Specifies the threshold distance under which cartesian points are deemed equal
|
||||
// Default: 0.00001m / 0.01mm
|
||||
// Read-only
|
||||
GV_POINT_EQUALITY_TOLERANCE,
|
||||
// Specifies maximum number of faces for a shell to be reoriented.
|
||||
// Default: -1
|
||||
GV_MAX_FACES_TO_ORIENT,
|
||||
// The length unit used the creation of TopoDS_Shapes, primarily affects the
|
||||
// interpretation of IfcCartesianPoints and IfcVector magnitudes
|
||||
// DefaultL 1.0
|
||||
|
||||
@@ -28,12 +28,6 @@
|
||||
|
||||
#include "ifc_parse_api.h"
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#include "../ifcparse/Ifc4enum.h"
|
||||
#else
|
||||
#include "../ifcparse/Ifc2x3enum.h"
|
||||
#endif
|
||||
|
||||
#include "../ifcparse/IfcEntityList.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+298
-339
File diff suppressed because it is too large
Load Diff
@@ -1,35 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file has been generated from IFC2X3_TC1.exp. Do not make modifications *
|
||||
* but instead modify the python script that has been used to generate this. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef IFC2X3ENUM_H
|
||||
#define IFC2X3ENUM_H
|
||||
|
||||
#include "../ifcparse/ifc_parse_api.h"
|
||||
|
||||
#include <string>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
+409
-466
File diff suppressed because it is too large
Load Diff
@@ -1,35 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file has been generated from IFC4.exp. Do not make modifications *
|
||||
* but instead modify the python script that has been used to generate this. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef IFC4ENUM_H
|
||||
#define IFC4ENUM_H
|
||||
|
||||
#include "../ifcparse/ifc_parse_api.h"
|
||||
|
||||
#include <string>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+16117
File diff suppressed because one or more lines are too long
+50693
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+16455
File diff suppressed because one or more lines are too long
+50906
File diff suppressed because one or more lines are too long
@@ -22,12 +22,6 @@
|
||||
|
||||
#include "ifc_parse_api.h"
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#include "../ifcparse/Ifc4enum.h"
|
||||
#else
|
||||
#include "../ifcparse/Ifc2x3enum.h"
|
||||
#endif
|
||||
|
||||
#include "../ifcparse/IfcEntityInstanceData.h"
|
||||
#include "../ifcparse/IfcSchema.h"
|
||||
|
||||
|
||||
@@ -27,6 +27,13 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5
|
||||
#include <boost/locale/encoding_utf.hpp>
|
||||
using boost::locale::conv::utf_to_utf;
|
||||
#define CODECVT_BOOST_FALLBACK
|
||||
#else
|
||||
#include <codecvt>
|
||||
#endif
|
||||
|
||||
#include "../ifcparse/IfcCharacterDecoder.h"
|
||||
#include "../ifcparse/IfcException.h"
|
||||
@@ -334,10 +341,21 @@ std::wstring::value_type IfcUtil::convert_codepage(int codepage, int c) {
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef CODECVT_BOOST_FALLBACK
|
||||
std::string IfcUtil::convert_utf8(const std::wstring& s) {
|
||||
return utf_to_utf<char>(s.c_str(), s.c_str() + s.size());
|
||||
}
|
||||
|
||||
std::wstring IfcUtil::convert_utf8(const std::string& s)
|
||||
{
|
||||
return utf_to_utf<wchar_t>(s.c_str(), s.c_str() + s.size());
|
||||
}
|
||||
#else
|
||||
std::string IfcUtil::convert_utf8(const std::wstring& s) {
|
||||
return std::wstring_convert<std::codecvt_utf8<std::wstring::value_type>>().to_bytes(s);
|
||||
}
|
||||
|
||||
std::wstring IfcUtil::convert_utf8(const std::string& s) {
|
||||
return std::wstring_convert<std::codecvt_utf8<std::wstring::value_type>>().from_bytes(s);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -29,7 +29,6 @@
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <codecvt>
|
||||
|
||||
#include "../ifcparse/IfcSpfStream.h"
|
||||
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef IFCENTITYDESCRIPTOR_H
|
||||
#define IFCENTITYDESCRIPTOR_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
#include "../ifcparse/IfcException.h"
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#include "../ifcparse/Ifc4enum.h"
|
||||
#else
|
||||
#include "../ifcparse/Ifc2x3enum.h"
|
||||
#endif
|
||||
|
||||
namespace IfcUtil {
|
||||
|
||||
class IFC_PARSE_API IfcEnumerationDescriptor {
|
||||
private:
|
||||
IfcSchema::Type::Enum type;
|
||||
std::vector<std::string> values;
|
||||
public:
|
||||
IfcEnumerationDescriptor(IfcSchema::Type::Enum type, const std::vector<std::string>& values)
|
||||
: type(type), values(values) {}
|
||||
std::pair<const char*, int> getIndex(const std::string& value) const {
|
||||
std::vector<std::string>::const_iterator it = std::find(values.begin(), values.end(), value);
|
||||
if (it != values.end()) {
|
||||
return std::make_pair(it->c_str(), (int)std::distance(it, values.begin()));
|
||||
} else {
|
||||
throw IfcParse::IfcException("Invalid enumeration value");
|
||||
}
|
||||
}
|
||||
const std::vector<std::string>& getValues() {
|
||||
return values;
|
||||
}
|
||||
IfcSchema::Type::Enum getType() {
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
class IFC_PARSE_API IfcEntityDescriptor {
|
||||
public:
|
||||
class IfcArgumentDescriptor
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
bool optional;
|
||||
ArgumentType argument_type;
|
||||
IfcSchema::Type::Enum data_type;
|
||||
IfcArgumentDescriptor(const std::string& name, bool optional, ArgumentType argument_type, IfcSchema::Type::Enum data_type)
|
||||
: name(name), optional(optional), argument_type(argument_type), data_type(data_type) {}
|
||||
};
|
||||
private:
|
||||
IfcSchema::Type::Enum type;
|
||||
IfcEntityDescriptor* parent;
|
||||
std::vector<IfcArgumentDescriptor> arguments;
|
||||
unsigned argument_start() const {
|
||||
return parent ? parent->getArgumentCount() : 0;
|
||||
}
|
||||
const IfcArgumentDescriptor& get_argument(unsigned i) const {
|
||||
if (i < arguments.size()) return arguments[i];
|
||||
else throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range");
|
||||
}
|
||||
public:
|
||||
IfcEntityDescriptor(IfcSchema::Type::Enum type, IfcEntityDescriptor* parent)
|
||||
: type(type), parent(parent) {}
|
||||
void add(const std::string& name, bool optional, ArgumentType argument_type, IfcSchema::Type::Enum data_type = IfcSchema::Type::UNDEFINED) {
|
||||
arguments.push_back(IfcArgumentDescriptor(name, optional, argument_type, data_type));
|
||||
}
|
||||
unsigned getArgumentCount() const {
|
||||
return (parent ? parent->getArgumentCount() : 0) + (unsigned)arguments.size();
|
||||
}
|
||||
const std::string& getArgumentName(unsigned i) const {
|
||||
const unsigned a = argument_start();
|
||||
return i < a
|
||||
? parent->getArgumentName(i)
|
||||
: get_argument(i-a).name;
|
||||
}
|
||||
ArgumentType getArgumentType(unsigned i) const {
|
||||
const unsigned a = argument_start();
|
||||
return i < a
|
||||
? parent->getArgumentType(i)
|
||||
: get_argument(i-a).argument_type;
|
||||
}
|
||||
bool getArgumentOptional(unsigned i) const {
|
||||
const unsigned a = argument_start();
|
||||
return i < a
|
||||
? parent->getArgumentOptional(i)
|
||||
: get_argument(i-a).optional;
|
||||
}
|
||||
IfcSchema::Type::Enum getArgumentEntity(unsigned i) const {
|
||||
const unsigned a = argument_start();
|
||||
return i < a
|
||||
? parent->getArgumentEntity(i)
|
||||
: get_argument(i-a).data_type;
|
||||
}
|
||||
unsigned getArgumentIndex(const std::string& s) const {
|
||||
unsigned a = argument_start();
|
||||
for(std::vector<IfcArgumentDescriptor>::const_iterator i = arguments.begin(); i != arguments.end(); ++i) {
|
||||
if (i->name == s) return a;
|
||||
a++;
|
||||
}
|
||||
if (parent) return parent->getArgumentIndex(s);
|
||||
throw IfcParse::IfcException(std::string("Argument ") + s + " not found on " + IfcSchema::Type::ToString(type));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "../ifcparse/ArgumentType.h"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
+1
-15
@@ -174,7 +174,7 @@ public:
|
||||
IfcUtil::IfcBaseClass* instance_by_id(int id);
|
||||
|
||||
/// Returns the entity with the specified GlobalId
|
||||
virtual IfcUtil::IfcBaseClass* instance_by_guid(const std::string& guid);
|
||||
IfcUtil::IfcBaseClass* instance_by_guid(const std::string& guid);
|
||||
|
||||
/// Performs a depth-first traversal, returning all entity instance
|
||||
/// attributes as a flat list. NB: includes the root instance specified
|
||||
@@ -216,20 +216,6 @@ public:
|
||||
void build_inverses();
|
||||
};
|
||||
|
||||
template <typename Schema>
|
||||
class IFC_PARSE_API IfcFileWithSchema : public IfcFile {
|
||||
public:
|
||||
std::pair<typename Schema::IfcNamedUnit*, double> getUnit(typename Schema::IfcUnitEnum::Value unit_type) {
|
||||
std::pair<IfcUtil::IfcBaseClass*, double> unit_info = IfcFile::getUnit(Schema::IfcUnitEnum::ToString(unit_type));
|
||||
return std::make_pair(unit_info.first->template as<typename Schema::IfcNamedUnit>(), unit_info.second);
|
||||
}
|
||||
|
||||
/// Returns the entity with the specified GlobalId
|
||||
virtual typename Schema::IfcRoot* instance_by_guid(const std::string& guid) {
|
||||
return IfcFile::instance_by_guid(guid)->template as<typename Schema::IfcRoot>();
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WITH_IFCXML
|
||||
IFC_PARSE_API IfcFile* parse_ifcxml(const std::string& filename);
|
||||
#endif
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
|
||||
#include "../ifcparse/Ifc2x3.h"
|
||||
#include "../ifcparse/Ifc4.h"
|
||||
#include "../ifcparse/Ifc4x1.h"
|
||||
#include "../ifcparse/Ifc4x2.h"
|
||||
|
||||
|
||||
#include "../ifcparse/IfcFile.h"
|
||||
#include "../ifcparse/IfcWrite.h"
|
||||
@@ -54,6 +57,14 @@ namespace {
|
||||
return t->RelatingStructure();
|
||||
}
|
||||
|
||||
Ifc4x1::IfcObjectDefinition* get_parent_of_relation(Ifc4x1::IfcRelContainedInSpatialStructure* t) {
|
||||
return t->RelatingStructure();
|
||||
}
|
||||
|
||||
Ifc4x2::IfcObjectDefinition* get_parent_of_relation(Ifc4x2::IfcRelContainedInSpatialStructure* t) {
|
||||
return t->RelatingStructure();
|
||||
}
|
||||
|
||||
IfcEntityList::ptr get_children_of_relation(IfcUtil::IfcBaseClass* t) {
|
||||
return *t->data().getArgument(
|
||||
t->declaration().as_entity()->attribute_index("RelatedElements")
|
||||
@@ -299,4 +310,4 @@ inline void IfcHierarchyHelper::addRelatedObject <typename Schema::IfcRelDefines
|
||||
}
|
||||
*/
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -114,6 +114,9 @@ void Logger::SetOutput(std::wostream* l1, std::wostream* l2) {
|
||||
}
|
||||
|
||||
void Logger::Message(Logger::Severity type, const std::string& message, const IfcUtil::IfcBaseClass* instance) {
|
||||
if (type > max_severity) {
|
||||
max_severity = type;
|
||||
}
|
||||
if ((log2 || wlog2) && type >= verbosity) {
|
||||
if (format == FMT_PLAIN) {
|
||||
if (log2) {
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include "../ifcparse/Ifc2x3.h"
|
||||
#include "../ifcparse/Ifc4.h"
|
||||
#include "../ifcparse/Ifc4x1.h"
|
||||
#include "../ifcparse/Ifc4x2.h"
|
||||
|
||||
double IfcParse::IfcSIPrefixToValue(const std::string& v) {
|
||||
if ( v == "EXA" ) return 1.e18;
|
||||
@@ -72,3 +74,5 @@ double IfcParse::get_SI_equivalent(typename Schema::IfcNamedUnit* named_unit) {
|
||||
|
||||
template double IfcParse::get_SI_equivalent<Ifc2x3>(typename Ifc2x3::IfcNamedUnit* named_unit);
|
||||
template double IfcParse::get_SI_equivalent<Ifc4>(typename Ifc4::IfcNamedUnit* named_unit);
|
||||
template double IfcParse::get_SI_equivalent<Ifc4x1>(typename Ifc4x1::IfcNamedUnit* named_unit);
|
||||
template double IfcParse::get_SI_equivalent<Ifc4x2>(typename Ifc4x2::IfcNamedUnit* named_unit);
|
||||
|
||||
@@ -64,15 +64,19 @@ IfcParse::schema_definition::~schema_definition() {
|
||||
|
||||
#include "../ifcparse/Ifc2x3.h"
|
||||
#include "../ifcparse/Ifc4.h"
|
||||
#include "../ifcparse/Ifc4x1.h"
|
||||
#include "../ifcparse/Ifc4x2.h"
|
||||
|
||||
const IfcParse::schema_definition* IfcParse::schema_by_name(const std::string& name) {
|
||||
// TODO: initialize automatically somehow
|
||||
Ifc2x3::get_schema();
|
||||
Ifc4::get_schema();
|
||||
Ifc4x1::get_schema();
|
||||
Ifc4x2::get_schema();
|
||||
|
||||
std::map<std::string, const IfcParse::schema_definition*>::const_iterator it = schemas.find(name);
|
||||
if (it == schemas.end()) {
|
||||
throw IfcParse::IfcException("No schema named " + name);
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,6 @@
|
||||
|
||||
#include "../ifcparse/IfcException.h"
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#include "../ifcparse/Ifc4enum.h"
|
||||
#else
|
||||
#include "../ifcparse/Ifc2x3enum.h"
|
||||
#endif
|
||||
|
||||
// Forward declarations
|
||||
class IfcEntityInstanceData;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include <libxml/parser.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include <boost/range/algorithm/copy.hpp>
|
||||
|
||||
@@ -328,9 +328,24 @@ static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs)
|
||||
attributes.push_back(std::make_pair(attrname, value));
|
||||
|
||||
if ((tagname == "ifc:ifcXML" || tagname == "ifcXML") && attrname == "xsi:schemaLocation" && boost::starts_with(value, "http://www.buildingsmart-tech.org/ifcXML/IFC4")) {
|
||||
state->file = new IfcParse::IfcFile(IfcParse::schema_by_name("IFC4"));
|
||||
state->file->parsing_complete() = false;
|
||||
state->dialect = ifcxml_dialect_ifc4;
|
||||
// We're expecting a schemaLocation like "http://www.buildingsmart-tech.org/ifcXML/IFC4/Add2 IFC4_ADD2_TC1.xsd"
|
||||
// With token compression this is split into:
|
||||
// [0] http:
|
||||
// [1] www.buildingsmart-tech.org
|
||||
// [2] ifcXML
|
||||
// [3] IFC4
|
||||
// [4] Add2
|
||||
// [5] IFC4_ADD2_TC1.xsd
|
||||
// The hosstname will likely change though.
|
||||
auto it = boost::algorithm::make_split_iterator(value, boost::algorithm::token_finder(boost::algorithm::is_any_of("/ "), boost::algorithm::token_compress_on));
|
||||
decltype(it) end;
|
||||
for (int tok = 0; it != end && tok < 3; ++it, ++tok) {}
|
||||
if (it != end) {
|
||||
const std::string schema_name(&it->front(), it->size());
|
||||
state->file = new IfcParse::IfcFile(IfcParse::schema_by_name(schema_name));
|
||||
state->file->parsing_complete() = false;
|
||||
state->dialect = ifcxml_dialect_ifc4;
|
||||
}
|
||||
goto end;
|
||||
} else if (tagname == "ex:iso_10303_28" && attrname == "xsi:schemaLocation" && boost::starts_with(value, "http://www.iai-tech.org/ifcXML/IFC2x3")) {
|
||||
state->file = new IfcParse::IfcFile(IfcParse::schema_by_name("IFC2X3"));
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
%extend IfcGeom::tree {
|
||||
|
||||
static IfcEntityList::ptr vector_to_list(const std::vector<IfcUtil::IfcBaseEntity*>& ps) const {
|
||||
static IfcEntityList::ptr vector_to_list(const std::vector<IfcUtil::IfcBaseEntity*>& ps) {
|
||||
IfcEntityList::ptr r(new IfcEntityList);
|
||||
for (std::vector<IfcUtil::IfcBaseEntity*>::const_iterator it = ps.begin(); it != ps.end(); ++it) {
|
||||
r->push(*it);
|
||||
@@ -174,13 +174,12 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
|
||||
|
||||
%extend IfcGeom::Representation::Triangulation {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
id = property(id)
|
||||
faces = property(faces)
|
||||
edges = property(edges)
|
||||
material_ids = property(material_ids)
|
||||
materials = property(materials)
|
||||
# Hide the getters with read-only property implementations
|
||||
id = property(id)
|
||||
faces = property(faces)
|
||||
edges = property(edges)
|
||||
material_ids = property(material_ids)
|
||||
materials = property(materials)
|
||||
%}
|
||||
};
|
||||
|
||||
@@ -188,28 +187,25 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
|
||||
// would appear before templated getter functions are defined.
|
||||
%extend IfcGeom::Representation::Triangulation<float> {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
verts = property(verts)
|
||||
normals = property(normals)
|
||||
# Hide the getters with read-only property implementations
|
||||
verts = property(verts)
|
||||
normals = property(normals)
|
||||
%}
|
||||
};
|
||||
%extend IfcGeom::Representation::Triangulation<double> {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
verts = property(verts)
|
||||
normals = property(normals)
|
||||
# Hide the getters with read-only property implementations
|
||||
verts = property(verts)
|
||||
normals = property(normals)
|
||||
%}
|
||||
};
|
||||
|
||||
%extend IfcGeom::Representation::Serialization {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
id = property(id)
|
||||
brep_data = property(brep_data)
|
||||
surface_styles = property(surface_styles)
|
||||
# Hide the getters with read-only property implementations
|
||||
id = property(id)
|
||||
brep_data = property(brep_data)
|
||||
surface_styles = property(surface_styles)
|
||||
%}
|
||||
};
|
||||
|
||||
@@ -220,66 +216,60 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
|
||||
}
|
||||
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
id = property(id)
|
||||
parent_id = property(parent_id)
|
||||
name = property(name)
|
||||
type = property(type)
|
||||
guid = property(guid)
|
||||
context = property(context)
|
||||
unique_id = property(unique_id)
|
||||
transformation = property(transformation)
|
||||
product = property(product_)
|
||||
# Hide the getters with read-only property implementations
|
||||
id = property(id)
|
||||
parent_id = property(parent_id)
|
||||
name = property(name)
|
||||
type = property(type)
|
||||
guid = property(guid)
|
||||
context = property(context)
|
||||
unique_id = property(unique_id)
|
||||
transformation = property(transformation)
|
||||
product = property(product_)
|
||||
%}
|
||||
|
||||
};
|
||||
|
||||
%extend IfcGeom::TriangulationElement {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
geometry = property(geometry)
|
||||
# Hide the getters with read-only property implementations
|
||||
geometry = property(geometry)
|
||||
%}
|
||||
};
|
||||
|
||||
%extend IfcGeom::SerializedElement {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
geometry = property(geometry)
|
||||
# Hide the getters with read-only property implementations
|
||||
geometry = property(geometry)
|
||||
%}
|
||||
};
|
||||
|
||||
%extend IfcGeom::Material {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
has_diffuse = property(hasDiffuse)
|
||||
has_specular = property(hasSpecular)
|
||||
has_transparency = property(hasTransparency)
|
||||
has_specularity = property(hasSpecularity)
|
||||
diffuse = property(diffuse)
|
||||
specular = property(specular)
|
||||
transparency = property(transparency)
|
||||
specularity = property(specularity)
|
||||
name = property(name)
|
||||
# Hide the getters with read-only property implementations
|
||||
has_diffuse = property(hasDiffuse)
|
||||
has_specular = property(hasSpecular)
|
||||
has_transparency = property(hasTransparency)
|
||||
has_specularity = property(hasSpecularity)
|
||||
diffuse = property(diffuse)
|
||||
specular = property(specular)
|
||||
transparency = property(transparency)
|
||||
specularity = property(specularity)
|
||||
name = property(name)
|
||||
%}
|
||||
};
|
||||
|
||||
%extend IfcGeom::Transformation {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
matrix = property(matrix)
|
||||
# Hide the getters with read-only property implementations
|
||||
matrix = property(matrix)
|
||||
%}
|
||||
};
|
||||
|
||||
%extend IfcGeom::Matrix {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
data = property(data)
|
||||
# Hide the getters with read-only property implementations
|
||||
data = property(data)
|
||||
%}
|
||||
};
|
||||
|
||||
@@ -289,6 +279,7 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
|
||||
IfcParse::IfcFile* file = instance->data().file;
|
||||
|
||||
IfcGeom::Kernel kernel(file);
|
||||
kernel.setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_ORIENT, settings.get(IfcGeom::IteratorSettings::SEW_SHELLS) ? std::numeric_limits<double>::infinity() : -1);
|
||||
kernel.setValue(IfcGeom::Kernel::GV_DIMENSIONALITY, (settings.get(IfcGeom::IteratorSettings::INCLUDE_CURVES) ? (settings.get(IfcGeom::IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES) ? -1. : 0.) : +1.));
|
||||
|
||||
if (instance->declaration().is(Schema::IfcProduct::Class())) {
|
||||
|
||||
@@ -90,6 +90,12 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas
|
||||
f << (*$self);
|
||||
}
|
||||
|
||||
std::string to_string() {
|
||||
std::stringstream s;
|
||||
s << (*$self);
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::vector<unsigned> entity_names() const {
|
||||
std::vector<unsigned> keys;
|
||||
keys.reserve(std::distance($self->begin(), $self->end()));
|
||||
@@ -121,10 +127,9 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas
|
||||
}
|
||||
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
header = property(header)
|
||||
schema = property(schema_name)
|
||||
# Hide the getters with read-only property implementations
|
||||
header = property(header)
|
||||
schema = property(schema_name)
|
||||
%}
|
||||
}
|
||||
|
||||
@@ -473,62 +478,58 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas
|
||||
|
||||
%extend IfcParse::IfcSpfHeader {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-only property implementations
|
||||
file_description = property(file_description)
|
||||
file_name = property(file_name)
|
||||
file_schema = property(file_schema)
|
||||
# Hide the getters with read-only property implementations
|
||||
file_description = property(file_description)
|
||||
file_name = property(file_name)
|
||||
file_schema = property(file_schema)
|
||||
%}
|
||||
};
|
||||
|
||||
%extend IfcParse::FileDescription {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-write property implementations
|
||||
__swig_getmethods__["description"] = description
|
||||
__swig_setmethods__["description"] = description
|
||||
description = property(description, description)
|
||||
__swig_getmethods__["implementation_level"] = implementation_level
|
||||
__swig_setmethods__["implementation_level"] = implementation_level
|
||||
implementation_level = property(implementation_level, implementation_level)
|
||||
# Hide the getters with read-write property implementations
|
||||
__swig_getmethods__["description"] = description
|
||||
__swig_setmethods__["description"] = description
|
||||
description = property(description, description)
|
||||
__swig_getmethods__["implementation_level"] = implementation_level
|
||||
__swig_setmethods__["implementation_level"] = implementation_level
|
||||
implementation_level = property(implementation_level, implementation_level)
|
||||
%}
|
||||
};
|
||||
|
||||
%extend IfcParse::FileName {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-write property implementations
|
||||
__swig_getmethods__["name"] = name
|
||||
__swig_setmethods__["name"] = name
|
||||
name = property(name, name)
|
||||
__swig_getmethods__["time_stamp"] = time_stamp
|
||||
__swig_setmethods__["time_stamp"] = time_stamp
|
||||
time_stamp = property(time_stamp, time_stamp)
|
||||
__swig_getmethods__["author"] = author
|
||||
__swig_setmethods__["author"] = author
|
||||
author = property(author, author)
|
||||
__swig_getmethods__["organization"] = organization
|
||||
__swig_setmethods__["organization"] = organization
|
||||
organization = property(organization, organization)
|
||||
__swig_getmethods__["preprocessor_version"] = preprocessor_version
|
||||
__swig_setmethods__["preprocessor_version"] = preprocessor_version
|
||||
preprocessor_version = property(preprocessor_version, preprocessor_version)
|
||||
__swig_getmethods__["originating_system"] = originating_system
|
||||
__swig_setmethods__["originating_system"] = originating_system
|
||||
originating_system = property(originating_system, originating_system)
|
||||
__swig_getmethods__["authorization"] = authorization
|
||||
__swig_setmethods__["authorization"] = authorization
|
||||
authorization = property(authorization, authorization)
|
||||
# Hide the getters with read-write property implementations
|
||||
__swig_getmethods__["name"] = name
|
||||
__swig_setmethods__["name"] = name
|
||||
name = property(name, name)
|
||||
__swig_getmethods__["time_stamp"] = time_stamp
|
||||
__swig_setmethods__["time_stamp"] = time_stamp
|
||||
time_stamp = property(time_stamp, time_stamp)
|
||||
__swig_getmethods__["author"] = author
|
||||
__swig_setmethods__["author"] = author
|
||||
author = property(author, author)
|
||||
__swig_getmethods__["organization"] = organization
|
||||
__swig_setmethods__["organization"] = organization
|
||||
organization = property(organization, organization)
|
||||
__swig_getmethods__["preprocessor_version"] = preprocessor_version
|
||||
__swig_setmethods__["preprocessor_version"] = preprocessor_version
|
||||
preprocessor_version = property(preprocessor_version, preprocessor_version)
|
||||
__swig_getmethods__["originating_system"] = originating_system
|
||||
__swig_setmethods__["originating_system"] = originating_system
|
||||
originating_system = property(originating_system, originating_system)
|
||||
__swig_getmethods__["authorization"] = authorization
|
||||
__swig_setmethods__["authorization"] = authorization
|
||||
authorization = property(authorization, authorization)
|
||||
%}
|
||||
};
|
||||
|
||||
%extend IfcParse::FileSchema {
|
||||
%pythoncode %{
|
||||
if _newclass:
|
||||
# Hide the getters with read-write property implementations
|
||||
__swig_getmethods__["schema_identifiers"] = schema_identifiers
|
||||
__swig_setmethods__["schema_identifiers"] = schema_identifiers
|
||||
schema_identifiers = property(schema_identifiers, schema_identifiers)
|
||||
# Hide the getters with read-write property implementations
|
||||
__swig_getmethods__["schema_identifiers"] = schema_identifiers
|
||||
__swig_setmethods__["schema_identifiers"] = schema_identifiers
|
||||
schema_identifiers = property(schema_identifiers, schema_identifiers)
|
||||
%}
|
||||
};
|
||||
|
||||
|
||||
@@ -54,6 +54,13 @@ GltfSerializer::GltfSerializer(const std::string& filename, const SerializerSett
|
||||
, tmp_fstream2_(IfcUtil::path::from_utf8(tmp_filename2_).c_str(), std::ios_base::binary)
|
||||
{}
|
||||
|
||||
GltfSerializer::~GltfSerializer() {
|
||||
tmp_fstream1_.close();
|
||||
tmp_fstream2_.close();
|
||||
IfcUtil::path::delete_file(tmp_filename1_);
|
||||
IfcUtil::path::delete_file(tmp_filename2_);
|
||||
}
|
||||
|
||||
bool GltfSerializer::ready() {
|
||||
return fstream_.is_open() && tmp_fstream1_.is_open() && tmp_fstream2_.is_open();
|
||||
}
|
||||
@@ -127,7 +134,7 @@ size_t write_accessor(json& j, std::ofstream& ofs, It begin, It end) {
|
||||
|
||||
std::array<typename It::value_type, N> min, max;
|
||||
min.fill(std::numeric_limits<typename It::value_type>::max());
|
||||
max.fill(std::numeric_limits<typename It::value_type>::min());
|
||||
max.fill(std::numeric_limits<typename It::value_type>::lowest());
|
||||
for (auto it = begin; it != end; it += N) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
const float& v = *(it + i);
|
||||
@@ -151,15 +158,19 @@ size_t write_accessor(json& j, std::ofstream& ofs, It begin, It end) {
|
||||
}
|
||||
|
||||
void GltfSerializer::write(const IfcGeom::TriangulationElement<real_t>* o) {
|
||||
if (o->geometry().material_ids().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
node_array_.push_back(json_["nodes"].size());
|
||||
|
||||
const std::vector<double>& m = o->transformation().matrix().data();
|
||||
// nb: note that this contains the Y-UP transform as well.
|
||||
const std::array<double, 16> matrix_flat = {
|
||||
m[0], m[ 2], m[ 1], 0,
|
||||
m[3], m[ 5], m[ 4], 0,
|
||||
m[6], m[ 8], m[ 7], 0,
|
||||
m[9], m[11], m[10], 1
|
||||
m[0], m[ 2], -m[ 1], 0,
|
||||
m[3], m[ 5], -m[ 4], 0,
|
||||
m[6], m[ 8], -m[ 7], 0,
|
||||
m[9], m[11], -m[10], 1
|
||||
};
|
||||
static const std::array<double, 16> identity_matrix = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
|
||||
|
||||
@@ -168,6 +179,7 @@ void GltfSerializer::write(const IfcGeom::TriangulationElement<real_t>* o) {
|
||||
// glTF validator complains about identity matrices
|
||||
node["matrix"] = matrix_flat;
|
||||
}
|
||||
node["name"] = object_id(o);
|
||||
|
||||
int current_mesh_index;
|
||||
|
||||
@@ -319,9 +331,6 @@ void GltfSerializer::finalize() {
|
||||
fstream_ << ifs.rdbuf();
|
||||
}
|
||||
write_padding<BIN>(fstream_, binary_length);
|
||||
|
||||
IfcUtil::path::delete_file(tmp_filename1_);
|
||||
IfcUtil::path::delete_file(tmp_filename2_);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -39,7 +39,7 @@ private:
|
||||
int writeMaterial(const IfcGeom::Material& style);
|
||||
public:
|
||||
GltfSerializer(const std::string& filename, const SerializerSettings& settings);
|
||||
virtual ~GltfSerializer() {}
|
||||
virtual ~GltfSerializer();
|
||||
bool ready();
|
||||
void writeHeader();
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* o);
|
||||
|
||||
@@ -290,57 +290,25 @@ void SvgSerializer::write(const IfcGeom::NativeElement<real_t>* o)
|
||||
IfcUtil::IfcBaseEntity* storey = storey_;
|
||||
boost::optional<double> storey_elevation = boost::none;
|
||||
|
||||
/*
|
||||
|
||||
TODO: based on NativeElement::parent()
|
||||
|
||||
IfcSchema::IfcObjectDefinition* obdef = static_cast<IfcSchema::IfcObjectDefinition*>(file->entityById(o->id()));
|
||||
|
||||
#ifndef USE_IFC4
|
||||
typedef IfcSchema::IfcRelDecomposes decomposition_element;
|
||||
#else
|
||||
typedef IfcSchema::IfcRelAggregates decomposition_element;
|
||||
#endif
|
||||
|
||||
for (; storey == 0;) {
|
||||
// Iterate over the decomposing element to find the parent IfcBuildingStorey
|
||||
decomposition_element::list::ptr decomposes = obdef->Decomposes();
|
||||
if (!decomposes->size()) {
|
||||
if (obdef->declaration().is(IfcSchema::Type::IfcElement)) {
|
||||
IfcSchema::IfcRelContainedInSpatialStructure::list::ptr containment = ((IfcSchema::IfcElement*)obdef)->ContainedInStructure();
|
||||
if (!containment->size()) {
|
||||
break;
|
||||
}
|
||||
for (IfcSchema::IfcRelContainedInSpatialStructure::list::it it = containment->begin(); it != containment->end(); ++it) {
|
||||
IfcSchema::IfcRelContainedInSpatialStructure* container = *it;
|
||||
if (container->RelatingStructure() != obdef) {
|
||||
obdef = container->RelatingStructure();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (decomposition_element::list::it it = decomposes->begin(); it != decomposes->end(); ++it) {
|
||||
decomposition_element* decompose = *it;
|
||||
if (decompose->RelatingObject() != obdef) {
|
||||
obdef = decompose->RelatingObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (obdef->declaration().is(IfcSchema::Type::IfcBuildingStorey)) {
|
||||
storey = static_cast<IfcSchema::IfcBuildingStorey*>(obdef);
|
||||
if (storey->hasElevation()) {
|
||||
for (const auto& p : o->parents()) {
|
||||
if (p->type() == "IfcBuildingStorey") {
|
||||
try {
|
||||
const IfcGeom::ElementSettings& settings = o->geometry().settings();
|
||||
storey_elevation = storey->Elevation() * settings.unit_magnitude();
|
||||
double e = *p->product()->get("Elevation");
|
||||
storey_elevation = e * settings.unit_magnitude();
|
||||
} catch (...) {
|
||||
continue;
|
||||
}
|
||||
storey = p->product();
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// With a global section height, building storeys are not a requirement.
|
||||
if (!storey && !section_height) return;
|
||||
if (!storey && !section_height) {
|
||||
Logger::Warning("No global section height and unable to determine building storey for:", o->product());
|
||||
return;
|
||||
}
|
||||
|
||||
path_object& p = start_path(storey, nameElement(o));
|
||||
|
||||
|
||||
@@ -175,6 +175,8 @@ ptree& format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& child, pt
|
||||
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) {
|
||||
@@ -255,7 +257,7 @@ ptree& descend(IfcSchema::IfcObjectDefinition* product, ptree& tree) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef USE_IFC4
|
||||
#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);
|
||||
|
||||
Reference in New Issue
Block a user