Merge branch 'master' into v0.6.0

# Conflicts:
#	src/ifcconvert/IfcConvert.cpp
#	src/ifcgeom/IfcGeomFilter.h
#	src/ifcgeom/IfcGeomIteratorImplementation.h
#	src/ifcgeom/IfcGeomWires.cpp
#	src/ifcparse/IfcFile.h
#	src/ifcparse/IfcLogger.cpp
#	src/ifcparse/IfcLogger.h
#	src/ifcparse/IfcParse.cpp
#	src/serializers/SvgSerializer.cpp
#	src/serializers/schema_dependent/XmlSerializer.cpp
This commit is contained in:
Thomas Krijnen
2018-07-07 17:15:54 +02:00
33 changed files with 3564 additions and 335 deletions
@@ -92,7 +92,8 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
faces = [[f[i], f[i + 1], f[i + 2]] \
for i in range(0, len(f), 3)]
me = bpy.data.meshes.new('mesh%d' % ob.geometry.id)
# Depending on version, geometry.id will be either int or str
me = bpy.data.meshes.new('mesh-%r' % ob.geometry.id)
me.from_pydata(verts, [], faces)
me.validate()
+108 -62
View File
@@ -113,7 +113,7 @@ bool rename_file(const std::string& old_filename, const std::string& new_filenam
}
static std::stringstream log_stream;
void write_log();
void write_log(bool);
/// @todo make the filters non-global
/*
@@ -154,13 +154,16 @@ bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file,
int main(int argc, char** argv)
{
std::string log_format;
po::options_description generic_options("Command line options");
generic_options.add_options()
("help,h", "display usage information")
("version", "display version information")
("verbose,v", "more verbose output")
("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)")
("no-progress", "Suppress possible progress bar type of prints that use carriage return.");
("verbose,v", "more verbose log messages")
("quiet,q", "less status and progress output")
("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)")
("no-progress", "suppress possible progress bar type of prints that use carriage return")
("log-format", po::value<std::string>(&log_format), "log format: plain or json");
po::options_description fileio_options;
fileio_options.add_options()
@@ -305,6 +308,8 @@ int main(int argc, char** argv)
("site-local-placement",
"Place elements locally in the IfcSite coordinate system, instead of placing "
"them in the IFC global coords. Applicable for OBJ and DAE output.")
("building-local-placement",
"Similar to --site-local-placement, but placing elements in locally in the parent IfcBuilding coord system")
("precision", po::value<short>(&precision)->default_value(SerializerSettings::DEFAULT_PRECISION),
"Sets the precision to be used to format floating-point values, 15 by default. "
"Use a negative value to use the system's default precision (should be 6 typically). "
@@ -341,7 +346,36 @@ int main(int argc, char** argv)
po::notify(vmap);
print_version();
const bool mmap = vmap.count("mmap") != 0;
const bool verbose = vmap.count("verbose") != 0;
const bool no_progress = vmap.count("no-progress") != 0;
const bool quiet = vmap.count("quiet") != 0;
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 sew_shells = vmap.count("sew-shells") != 0;
#if OCC_VERSION_HEX < 0x60900
const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0;
#endif
const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0;
const bool include_plan = vmap.count("plan") != 0;
const bool include_model = vmap.count("model") != 0 || (!include_plan);
const bool enable_layerset_slicing = vmap.count("enable-layerset-slicing") != 0;
const bool use_element_names = vmap.count("use-element-names") != 0;
const bool use_element_guids = vmap.count("use-element-guids") != 0;
const bool use_material_names = vmap.count("use-material-names") != 0;
const bool use_element_types = vmap.count("use-element-types") != 0;
const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 0;
const bool no_normals = vmap.count("no-normals") != 0;
const bool center_model = vmap.count("center-model") != 0;
const bool model_offset = vmap.count("model-offset") != 0;
const bool site_local_placement = vmap.count("site-local-placement") != 0;
const bool building_local_placement = vmap.count("building-local-placement") != 0;
const bool generate_uvs = vmap.count("generate-uvs") != 0;
if (!quiet || vmap.count("version")) {
print_version();
}
if (vmap.count("version")) {
return EXIT_SUCCESS;
@@ -354,30 +388,6 @@ int main(int argc, char** argv)
print_usage();
return EXIT_FAILURE;
}
const bool mmap = vmap.count("mmap") != 0;
const bool verbose = vmap.count("verbose") != 0;
const bool no_progress = vmap.count("no-progress") != 0;
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 sew_shells = vmap.count("sew-shells") != 0;
#if OCC_VERSION_HEX < 0x60900
const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0;
#endif
const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0;
const bool include_plan = vmap.count("plan") != 0;
const bool include_model = vmap.count("model") != 0 || (!include_plan);
const bool enable_layerset_slicing = vmap.count("enable-layerset-slicing") != 0;
const bool use_element_names = vmap.count("use-element-names") != 0;
const bool use_element_guids = vmap.count("use-element-guids") != 0 ;
const bool use_material_names = vmap.count("use-material-names") != 0;
const bool use_element_types = vmap.count("use-element-types") != 0;
const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 0;
const bool no_normals = vmap.count("no-normals") != 0 ;
const bool center_model = vmap.count("center-model") != 0 ;
const bool model_offset = vmap.count("model-offset") != 0 ;
const bool site_local_placement = vmap.count("site-local-placement") != 0 ;
const bool generate_uvs = vmap.count("generate-uvs") != 0 ;
#ifdef HAVE_ICU
if (!unicode_mode.empty()) {
@@ -442,12 +452,25 @@ int main(int argc, char** argv)
Logger::SetOutput(&std::cout, &log_stream);
Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR);
if (vmap.count("log-format") == 1) {
boost::to_lower(log_format);
if (log_format == "plain") {
Logger::OutputFormat(Logger::FMT_PLAIN);
} else if (log_format == "json") {
Logger::OutputFormat(Logger::FMT_JSON);
} else {
std::cerr << "[Error] --log-format should be either plain or json" << std::endl;
print_usage();
return EXIT_FAILURE;
}
}
IfcParse::IfcFile* ifc_file = 0;
if (output_extension == ".xml") {
int exit_code = EXIT_FAILURE;
try {
if (init_input_file(input_filename, ifc_file, no_progress, mmap)) {
if (init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) {
XmlSerializer s(ifc_file, output_temp_filename);
Logger::Status("Writing XML output...");
s.finalize();
@@ -458,7 +481,7 @@ int main(int argc, char** argv)
} catch (const std::exception& e) {
Logger::Error(e);
}
write_log();
write_log(!quiet);
return exit_code;
}
@@ -512,6 +535,8 @@ int main(int argc, char** argv)
settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, generate_uvs);
settings.set(IfcGeom::IteratorSettings::SEARCH_FLOOR, use_element_hierarchy);
settings.set(IfcGeom::IteratorSettings::SITE_LOCAL_PLACEMENT, site_local_placement);
settings.set(IfcGeom::IteratorSettings::BUILDING_LOCAL_PLACEMENT, building_local_placement);
settings.set(SerializerSettings::USE_ELEMENT_NAMES, use_element_names);
settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids);
@@ -551,7 +576,7 @@ int main(int argc, char** argv)
}
} else {
std::cerr << "[Error] Unknown output filename extension '" + output_extension + "'\n";
write_log();
write_log(!quiet);
print_usage();
return EXIT_FAILURE;
}
@@ -560,7 +585,7 @@ int main(int argc, char** argv)
if (use_element_hierarchy && output_extension != ".dae") {
std::cerr << "[Error] --use-element-hierarchy can be used only with .dae output.\n";
write_log();
write_log(!quiet);
print_usage();
delete serializer;
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
@@ -585,14 +610,14 @@ int main(int argc, char** argv)
if (!serializer->ready()) {
delete serializer;
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
write_log();
write_log(!quiet);
return EXIT_FAILURE;
}
time_t start,end;
time(&start);
if (!init_input_file(input_filename, ifc_file, no_progress, mmap)) {
if (!init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) {
return EXIT_FAILURE;
}
@@ -603,7 +628,7 @@ int main(int argc, char** argv)
Logger::Error("No geometrical entities found");
delete serializer;
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
write_log();
write_log(!quiet);
return EXIT_FAILURE;
}
@@ -617,13 +642,13 @@ int main(int argc, char** argv)
serializer->writeHeader();
int old_progress = -1;
int old_progress = quiet ? 0 : -1;
if (center_model || model_offset) {
double* offset = serializer->settings().offset;
if (center_model) {
if (site_local_placement) {
Logger::Error("Cannot use --center-model together with --site-local-placement");
if (site_local_placement || building_local_placement) {
Logger::Error("Cannot use --center-model together with --{site,building}-local-placement");
delete serializer;
return EXIT_FAILURE;
}
@@ -649,7 +674,9 @@ int main(int argc, char** argv)
Logger::Notice(msg.str());
}
Logger::Status("Creating geometry...");
if (!quiet) {
Logger::Status("Creating geometry...");
}
// The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next()
// wrap an iterator of all geometrical products in the Ifc file.
@@ -676,14 +703,28 @@ int main(int argc, char** argv)
}
if (!no_progress) {
const int progress = context_iterator.progress() / 2;
if (old_progress != progress) Logger::ProgressBar(progress);
old_progress = progress;
if (quiet) {
const int progress = context_iterator.progress();
for (; old_progress < progress; ++old_progress) {
std::cout << ".";
}
std::cout << std::flush;
} else {
const int progress = context_iterator.progress() / 2;
if (old_progress != progress) Logger::ProgressBar(progress);
old_progress = progress;
}
}
} while (++num_created, context_iterator.next());
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(num_created) +
" objects) ");
if (!no_progress && quiet) {
for (; old_progress < 100; ++old_progress) {
std::cout << ".";
}
} else {
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(num_created) +
" objects) ");
}
serializer->finalize();
delete serializer;
@@ -696,34 +737,39 @@ int main(int argc, char** argv)
output_temp_filename + "' for the conversion result.");
}
write_log();
write_log(!quiet);
time(&end);
int seconds = (int)difftime(end, start);
std::stringstream msg;
int minutes = seconds / 60;
seconds = seconds % 60;
msg << "\nConversion took";
if (minutes > 0) {
msg << " " << minutes << " minute";
if (minutes > 1) {
if (!quiet) {
int seconds = (int)difftime(end, start);
std::stringstream msg;
int minutes = seconds / 60;
seconds = seconds % 60;
msg << "\nConversion took";
if (minutes > 0) {
msg << " " << minutes << " minute";
if (minutes > 1) {
msg << "s";
}
}
msg << " " << seconds << " second";
if (seconds > 1) {
msg << "s";
}
Logger::Status(msg.str());
}
msg << " " << seconds << " second";
if (seconds > 1) {
msg << "s";
}
Logger::Status(msg.str());
return successful ? EXIT_SUCCESS : EXIT_FAILURE;
}
void write_log() {
void write_log(bool header) {
std::string log = log_stream.str();
if (!log.empty()) {
std::cout << "\nLog:\n" << log << std::endl;
if (header) {
std::cout << "\nLog:\n";
}
std::cout << log << std::endl;
}
}
@@ -881,7 +927,7 @@ void validate(boost::any& v, const std::vector<std::string>& values, exclusion_t
std::vector<IfcGeom::filter_t> setup_filters(const std::vector<geom_filter>& filters, const std::string& output_extension)
{
std::vector<IfcGeom::filter_t> filter_funcs;
foreach(const geom_filter& f, filters) {
BOOST_FOREACH(const geom_filter& f, filters) {
if (f.type == geom_filter::ENTITY_TYPE) {
entity_filter.include = f.include;
entity_filter.traverse = f.traverse;
+2
View File
@@ -202,6 +202,8 @@ public:
bool approximate_plane_through_wire(const TopoDS_Wire&, gp_Pln&);
bool flatten_wire(TopoDS_Wire&);
bool triangulate_wire(const TopoDS_Wire&, TopTools_ListOfShape&);
bool wire_intersections(const TopoDS_Wire & wire, TopTools_ListOfShape & wires);
void select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest);
static double shape_volume(const TopoDS_Shape& s);
static double face_area(const TopoDS_Face& f);
+9 -1
View File
@@ -215,7 +215,15 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
process_wire:
if (face_surface.IsNull()) {
mf = new BRepBuilderAPI_MakeFace(wire);
if (count(wire, TopAbs_EDGE) > 128) {
// 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?
gp_Pln pln;
approximate_plane_through_wire(wire, pln);
mf = new BRepBuilderAPI_MakeFace(pln, wire, true);
} else {
mf = new BRepBuilderAPI_MakeFace(wire);
}
} else {
/// @todo check necessity of false here
mf = new BRepBuilderAPI_MakeFace(face_surface, wire, false);
+9 -8
View File
@@ -25,6 +25,7 @@
#include "IfcGeom.h"
#include <boost/foreach.hpp>
#include <boost/function.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string/replace.hpp>
@@ -90,7 +91,7 @@ namespace IfcGeom
void populate(const std::set<std::string>& patterns)
{
values.clear();
foreach(const std::string &pattern, patterns) {
BOOST_FOREACH(const std::string &pattern, patterns) {
values.insert(wildcard_string_to_regex(pattern));
}
}
@@ -99,7 +100,7 @@ namespace IfcGeom
static bool match_values(const std::set<boost::regex>& values, const std::string &str)
{
foreach(const boost::regex& r, values) {
BOOST_FOREACH(const boost::regex& r, values) {
if (boost::regex_match(str, r)) {
return true;
}
@@ -111,7 +112,7 @@ namespace IfcGeom
{
// Escape all non-"*?" regex special chars
static const std::string special_chars = "\\^.$|()[]+/";
foreach(char c, special_chars) {
BOOST_FOREACH(char c, special_chars) {
std::string char_str(1, c);
boost::replace_all(str, char_str, "\\" + char_str);
}
@@ -184,7 +185,7 @@ namespace IfcGeom
ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude");
std::vector<std::string> patterns;
foreach(const boost::regex& r, values) {
BOOST_FOREACH(const boost::regex& r, values) {
patterns.push_back("\"" + r.str() + "\"");
}
@@ -247,7 +248,7 @@ namespace IfcGeom
std::stringstream ss;
ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude") << " layers";
std::vector<std::string> str_values;
foreach(const boost::regex& r, values) {
BOOST_FOREACH(const boost::regex& r, values) {
str_values.push_back(" \"" + r.str() + "\"");
}
ss << boost::algorithm::join(str_values, " ");
@@ -271,7 +272,7 @@ namespace IfcGeom
// TODO
#if 0
values.clear();
foreach(const std::string& type, types) {
BOOST_FOREACH(const std::string& type, types) {
const IfcParse::declaration* ty;
try {
ty = IfcSchema::FromString::Class()(boost::to_upper_copy(type));
@@ -287,7 +288,7 @@ namespace IfcGeom
bool match(IfcSchema::IfcProduct* prod) const
{
// The set is iterated over to able to filter on subtypes.
foreach(const IfcParse::declaration* type, values) {
BOOST_FOREACH(const IfcParse::declaration* type, values) {
if (prod->declaration().is(*type)) {
return true;
}
@@ -306,7 +307,7 @@ namespace IfcGeom
#if 0
std::stringstream ss;
ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude") << " entities";
foreach(IfcSchema::Enum::Class() type, values) {
BOOST_FOREACH(IfcSchema::Enum::Class() type, values) {
ss << " " << IfcSchema::ToString::Class()(type);
}
description = ss.str();
+315 -7
View File
@@ -95,6 +95,7 @@
#include <ShapeFix_Solid.hxx>
#include <ShapeAnalysis_Curve.hxx>
#include <ShapeAnalysis_Wire.hxx>
#include <ShapeAnalysis_Surface.hxx>
#include <ShapeAnalysis_ShapeTolerance.hxx>
@@ -105,6 +106,7 @@
#include <GProp_GProps.hxx>
#include <BRepGProp.hxx>
#include <BRepBuilderAPI_Copy.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <BRepBuilderAPI_GTransform.hxx>
@@ -131,6 +133,8 @@
#include <BRepClass3d_SolidClassifier.hxx>
#include <GeomAPI_ExtremaCurveCurve.hxx>
#include <Standard_Version.hxx>
#include "../ifcparse/macros.h"
@@ -249,8 +253,10 @@ bool IfcGeom::Kernel::create_solid_from_faces(const TopTools_ListOfShape& face_l
}
if (valid_shell) {
TopoDS_Shape complete_shape;
TopExp_Explorer exp(shape, TopAbs_SHELL);
for (; exp.More(); exp.Next()) {
TopoDS_Shape result_shape = exp.Current();
@@ -295,12 +301,28 @@ bool IfcGeom::Kernel::create_solid_from_faces(const TopTools_ListOfShape& face_l
B.MakeCompound(C);
B.Add(C, complete_shape);
complete_shape = C;
Logger::Message(Logger::LOG_WARNING, "Multiple components in IfcConnectedFaceSet");
Logger::Message(Logger::LOG_ERROR, "Multiple components in IfcConnectedFaceSet");
}
B.Add(complete_shape, result_shape);
}
}
TopExp_Explorer loose_faces(shape, TopAbs_FACE, TopAbs_SHELL);
for (; loose_faces.More(); loose_faces.Next()) {
BRep_Builder B;
if (complete_shape.ShapeType() != TopAbs_COMPOUND) {
TopoDS_Compound C;
B.MakeCompound(C);
B.Add(C, complete_shape);
complete_shape = C;
Logger::Message(Logger::LOG_ERROR, "Loose faces in IfcConnectedFaceSet");
}
B.Add(complete_shape, loose_faces.Current());
}
shape = complete_shape;
} else {
Logger::Message(Logger::LOG_WARNING, "Failed to sew faceset");
}
@@ -626,9 +648,12 @@ bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity,
}
for ( unsigned int i = 0; i < opening_shapes.size(); ++ i ) {
TopoDS_Shape opening_shape_solid;
const TopoDS_Shape& opening_shape_unlocated = ensure_fit_for_subtraction(opening_shapes[i].Shape(), opening_shape_solid);
gp_GTrsf gtrsf = opening_shapes[i].Placement();
gtrsf.PreMultiply(opening_trsf);
TopoDS_Shape opening_shape = apply_transformation(opening_shapes[i].Shape(), gtrsf);
TopoDS_Shape opening_shape = apply_transformation(opening_shape_unlocated, gtrsf);
opening_shapelist.Append(opening_shape);
}
@@ -657,7 +682,15 @@ bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity,
}
#endif
bool IfcGeom::Kernel::convert_wire_to_face(const TopoDS_Wire& wire, TopoDS_Face& face) {
bool IfcGeom::Kernel::convert_wire_to_face(const TopoDS_Wire& w, TopoDS_Face& face) {
TopoDS_Wire wire = w;
TopTools_ListOfShape results;
if (wire_intersections(wire, results)) {
Logger::Error("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected");
select_largest(results, wire);
}
ShapeFix_ShapeTolerance FTol;
FTol.SetTolerance(wire, getValue(GV_PRECISION), TopAbs_WIRE);
@@ -2429,7 +2462,11 @@ bool IfcGeom::Kernel::split_solid_by_shell(const TopoDS_Shape& input, const Topo
}
apply_tolerance(solid, getValue(GV_PRECISION));
#if OCC_VERSION_HEX >= 0x70300
TopTools_ListOfShape shapes;
#else
BOPCol_ListOfShape shapes;
#endif
shapes.Append(input);
shapes.Append(solid);
BOPAlgo_PaveFiller filler(new NCollection_IncAllocator); // TODO: Does this need to be freed?
@@ -2774,6 +2811,226 @@ TopoDS_Shape IfcGeom::Kernel::apply_transformation(const TopoDS_Shape& s, const
}
}
namespace {
/*
* A small helper utility to wrap around a numeric range
*/
class bounded_int {
private:
int i;
size_t n;
public:
bounded_int(int i, size_t n) : i(i), n(n) {}
bounded_int& operator--() {
--i;
if (i == -1) {
i = n - 1;
}
return *this;
}
bounded_int& operator++() {
++i;
if (i == (int) n) {
i = 0;
}
return *this;
}
operator int() { return i; }
};
std::string format_pnt(const gp_Pnt& p) {
std::stringstream ss;
ss << std::fixed << std::setprecision(4) << p.X() << " " << p.Y() << " " << p.Z();
return ss.str();
}
std::string format_edge(const TopoDS_Edge& e) {
std::stringstream ss;
TopoDS_Vertex v1, v2;
TopExp::Vertices(e, v1, v2);
gp_Pnt p1 = BRep_Tool::Pnt(v1);
gp_Pnt p2 = BRep_Tool::Pnt(v2);
ss << "edge " << format_pnt(p1) << " -> " << format_pnt(p2);
return ss.str();
}
}
bool IfcGeom::Kernel::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires) {
if (!wire.Closed()) {
wires.Append(wire);
return false;
}
int n = count(wire, TopAbs_EDGE);
if (n < 3 || n > 128) {
if (n > 128) {
Logger::Notice("Too many segments for detection of self-intersections");
}
wires.Append(wire);
return false;
}
// Note: initialize empty
Handle(ShapeExtend_WireData) wd = new ShapeExtend_WireData();
// ... to be sure to get consecutive edges
BRepTools_WireExplorer exp(wire);
for (; exp.More(); exp.Next()) {
wd->Add(exp.Current());
}
bool intersected = false;
// tfk: Extrema on infinite curves proved to be more robust.
// TopoDS_Face face = BRepBuilderAPI_MakeFace(wire, true).Face();
// ShapeAnalysis_Wire saw(wd, face, getValue(GV_PRECISION));
for (int i = 2; i < n; ++i) {
for (int j = 0; j < i - 1; ++j) {
if (i == n - 1 && j == 0) continue;
bool unbounded_intersects;
const double eps = getValue(GV_PRECISION) * 2.;
double u11, u12, u21, u22, U1, U2;
GeomAPI_ExtremaCurveCurve ecc(
BRep_Tool::Curve(wd->Edge(i + 1), u11, u12),
BRep_Tool::Curve(wd->Edge(j + 1), u21, u22)
);
if ((unbounded_intersects = (ecc.NbExtrema() == 1 && ecc.Distance(1) < eps))) {
ecc.Parameters(1, U1, U2);
}
if (u11 > u12) {
std::swap(u11, u12);
}
if (u21 > u22) {
std::swap(u21, u22);
}
/// @todo: tfk: probably need different thresholds on non-linear curves
u11 -= eps;
u12 += eps;
u21 -= eps;
u22 += eps;
// tfk: code below is for ShapeAnalysis_Wire::CheckIntersectingEdges()
// IntRes2d_SequenceOfIntersectionPoint points2d;
// TColgp_SequenceOfPnt points3d;
// TColStd_SequenceOfReal errors;
// if (saw.CheckIntersectingEdges(i + 1, j + 1, points2d, points3d, errors)) {
if (unbounded_intersects && u11 < U1 && U1 < u12 && u21 < U2 && U2 < u22) {
intersected = true;
// Explore a forward and backward cycle from the intersection point
for (int fb = 0; fb <= 1; ++fb) {
const bool forward = fb == 0;
BRepBuilderAPI_MakeWire mw;
bool first = true;
for (bounded_int k(j, n);;) {
bool intersecting = k == j || k == i;
if (intersecting) {
TopoDS_Edge e = wd->Edge(k + 1);
TopoDS_Vertex v1, v2;
TopExp::Vertices(e, v1, v2);
const TopoDS_Vertex* v = first == forward ? &v2 : &v1;
// gp_Pnt p2 = points3d.Value(1);
gp_Pnt p1 = BRep_Tool::Pnt(*v);
gp_Pnt pp1, pp2;
ecc.Points(1, pp1, pp2);
const gp_Pnt& p2 = k == i ? pp1 : pp2;
// Substitute with a new edge from/to the intersection point
if (p1.Distance(p2) > getValue(GV_PRECISION) * 2) {
double _, __;
Handle_Geom_Curve crv = BRep_Tool::Curve(e, _, __);
BRepBuilderAPI_MakeEdge me(crv, p1, p2);
TopoDS_Edge ed = me.Edge();
mw.Add(ed);
}
first = false;
} else {
// Re-use original edge
mw.Add(wd->Edge(k+1));
}
if (k == i) {
break;
}
if (forward) {
++k;
} else {
--k;
}
}
// Recursively process both cuts
wire_intersections(mw.Wire(), wires);
}
return true;
}
}
}
// No intersections found, append original wire
if (!intersected) {
wires.Append(wire);
}
return intersected;
}
void IfcGeom::Kernel::select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest) {
double mass = 0.;
TopTools_ListIteratorOfListOfShape it(shapes);
for (; it.More(); it.Next()) {
/*
// tfk: bounding box is more efficient probably
const TopoDS_Wire& w = TopoDS::Wire(it.Value());
TopoDS_Face face = BRepBuilderAPI_MakeFace(w).Face();
const double m = face_area(face);
*/
Bnd_Box bb;
BRepBndLib::AddClose(it.Value(), bb);
double xyz_min[3], xyz_max[3];
bb.Get(xyz_min[0], xyz_min[1], xyz_min[2], xyz_max[0], xyz_max[1], xyz_max[2]);
const double eps = getValue(GV_PRECISION);
double m = 1.;
for (int i = 0; i < 3; ++i) {
if (Precision::IsNegativeInfinite(xyz_min[i])) {
xyz_min[i] = 0.;
}
if (Precision::IsInfinite(xyz_max[i])) {
xyz_max[i] = 0.;
}
m *= (xyz_max[i] + eps) - (xyz_min[i] - eps);
}
if (m > mass) {
mass = m;
largest = it.Value();
}
}
}
#if OCC_VERSION_HEX < 0x60900
bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_ListOfShape& b, BOPAlgo_Operation op, TopoDS_Shape& result) {
result = a;
@@ -2832,6 +3089,47 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopoDS_Shap
return succesful;
}
#else
namespace {
TopTools_ListOfShape copy_operand(const TopTools_ListOfShape& l) {
#if OCC_VERSION_HEX < 0x70000
TopTools_ListOfShape r;
TopTools_ListIteratorOfListOfShape it(l);
for (; it.More(); it.Next()) {
r.Append(BRepBuilderAPI_Copy(it.Value()));
}
return r;
#else
// On OCCT 7.0 and higher BRepAlgoAPI_BuilderAlgo::SetNonDestructive(true) is
// called. Not entirely sure on the behaviour before 7.0, so overcautiously
// create copies.
return l;
#endif
}
TopoDS_Shape copy_operand(const TopoDS_Shape& s) {
#if OCC_VERSION_HEX < 0x70000
return BRepBuilderAPI_Copy(s);
#else
return s;
#endif
}
double min_edge_length(const TopoDS_Shape& a) {
double min_edge_len = std::numeric_limits<double>::infinity();
TopExp_Explorer exp(a, TopAbs_EDGE);
for (; exp.More(); exp.Next()) {
GProp_GProps prop;
BRepGProp::LinearProperties(exp.Current(), prop);
double l = prop.Mass();
if (l < min_edge_len) {
min_edge_len = l;
}
}
return min_edge_len;
}
}
bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_ListOfShape& b, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) {
bool success = false;
BRepAlgoAPI_BooleanOperation* builder;
@@ -2847,17 +3145,27 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_Li
if (fuzziness < 0.) {
fuzziness = getValue(GV_PRECISION);
}
const double min_edge_len = min_edge_length(a);
const double fuzz = (std::min)(min_edge_len / 3., fuzziness);
TopTools_ListOfShape s1s;
s1s.Append(a);
builder->SetFuzzyValue(fuzziness);
s1s.Append(copy_operand(a));
#if OCC_VERSION_HEX >= 0x70000
builder->SetNonDestructive(true);
#endif
builder->SetFuzzyValue(fuzz);
builder->SetArguments(s1s);
builder->SetTools(b);
builder->SetTools(copy_operand(b));
builder->Build();
if (builder->IsDone()) {
TopoDS_Shape r = *builder;
ShapeFix_Shape fix(r);
try {
fix.SetMinTolerance(fuzz);
fix.SetMaxTolerance(fuzz);
fix.SetPrecision(fuzz);
fix.Perform();
r = fix.Shape();
} catch (...) {
@@ -2873,7 +3181,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_Li
delete builder;
if (!success) {
const double new_fuzziness = fuzziness * 10.;
if (new_fuzziness + 1e-15 <= getValue(GV_PRECISION) * 1000.) {
if (new_fuzziness + 1e-15 <= getValue(GV_PRECISION) * 1000. && new_fuzziness < min_edge_len) {
return boolean_operation(a, b, op, result, new_fuzziness);
}
}
+25 -5
View File
@@ -119,6 +119,9 @@ namespace IfcGeom {
IfcSchema::IfcProduct::list::ptr ifcproducts;
IfcSchema::IfcProduct::list::it ifcproduct_iterator;
IfcSchema::IfcRepresentation::list::ptr ok_mapped_representations;
int done;
int total;
@@ -192,6 +195,7 @@ namespace IfcGeom {
bool any_precision_encountered = false;
representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list);
ok_mapped_representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list);
IfcSchema::IfcGeometricRepresentationContext::list::it it;
IfcSchema::IfcGeometricRepresentationSubContext::list::it jt;
@@ -389,6 +393,7 @@ namespace IfcGeom {
// Note that this can be a nullptr (!), but the fact that set size should be one still holds
associated_single_materials.insert(kernel.get_single_material_association(product));
if (associated_single_materials.size() > 1) return false;
}
return associated_single_materials.size() == 1;
@@ -412,12 +417,20 @@ namespace IfcGeom {
geometry_reuse_ok_for_current_representation_ = reuse_ok_(unfiltered_products);
if (!geometry_reuse_ok_for_current_representation_ && representation->RepresentationMap()->size() == 1) {
IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap();
if (!geometry_reuse_ok_for_current_representation_ && maps->size() == 1) {
// unfiltered_products contains products represented by this representation by means of mapped items.
// For example because of openings applied to products, reuse might not be acceptable and then the
// products will be processed by means of their immediate representation and not the mapped representation.
_nextShape();
continue;
// IfcRepresentationMaps are also used for IfcTypeProducts, so an additional check is performed whether the map
// is indeed used by IfcMappedItems.
IfcSchema::IfcRepresentationMap* map = *maps->begin();
if (map->MapUsage()->size() > 0) {
_nextShape();
continue;
}
}
bool representation_processed_as_mapped_item = false;
@@ -425,10 +438,12 @@ namespace IfcGeom {
IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation);
if (representation_mapped_to) {
// Check if this represenation has (or will be) processed as part its mapped representation
representation_processed_as_mapped_item = reuse_ok_(kernel.products_represented_by(representation_mapped_to));
representation_processed_as_mapped_item = ok_mapped_representations->contains(representation_mapped_to) ||
reuse_ok_(kernel.products_represented_by(representation_mapped_to));
}
if (representation_processed_as_mapped_item) {
ok_mapped_representations->push(representation_mapped_to);
_nextShape();
continue;
}
@@ -688,7 +703,12 @@ namespace IfcGeom {
kernel.setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_SEW, settings.get(IteratorSettings::SEW_SHELLS) ? 1000 : -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::SITE_LOCAL_PLACEMENT)) {
if (settings.get(IteratorSettings::BUILDING_LOCAL_PLACEMENT)) {
if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) {
Logger::Message(Logger::LOG_WARNING, "building-local-placement takes precedence over site-local-placement");
}
kernel.set_conversion_placement_rel_to(&IfcSchema::IfcBuilding::Class());
} else if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) {
kernel.set_conversion_placement_rel_to(&IfcSchema::IfcSite::Class());
}
}
+4 -2
View File
@@ -82,8 +82,10 @@ namespace IfcGeom
SEARCH_FLOOR = 1 << 14,
///
SITE_LOCAL_PLACEMENT = 1 << 15,
/// Number of different setting flags.
NUM_SETTINGS = 15
///
BUILDING_LOCAL_PLACEMENT = 1 << 16,
/// Number of different setting flags.
NUM_SETTINGS = 16
};
/// Used to store logical OR combination of setting flags.
typedef unsigned SettingField;
+5
View File
@@ -551,7 +551,12 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape
return false;
}
#if OCC_VERSION_HEX < 0x60900
bool valid_result = boolean_operation(s1, s2, occ_op, shape);
#else
const double fuzz = is_halfspace ? getValue(GV_PRECISION) * 10. : -1.;
bool valid_result = boolean_operation(s1, s2, occ_op, shape, fuzz);
#endif
if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) {
// In case of a subtraction, a check on volume is performed.
+198 -81
View File
@@ -85,11 +85,169 @@
#include <Geom_BSplineCurve.hxx>
#include <BRepTools_WireExplorer.hxx>
#include <ShapeBuild_ReShape.hxx>
#include <TopTools_ListOfShape.hxx>
#include <TopTools_ListIteratorOfListOfShape.hxx>
#include "../ifcgeom/IfcGeom.h"
#define Kernel MAKE_TYPE_NAME(Kernel)
namespace {
// Returns the other vertex of an edge
TopoDS_Vertex other(const TopoDS_Edge& e, const TopoDS_Vertex& v) {
TopoDS_Vertex a, b;
TopExp::Vertices(e, a, b);
return v.IsSame(b) ? a : b;
}
TopoDS_Edge first_edge(const TopoDS_Wire& w) {
TopoDS_Vertex v1, v2;
TopExp::Vertices(w, v1, v2);
TopTools_IndexedDataMapOfShapeListOfShape wm;
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, wm);
return TopoDS::Edge(wm.FindFromKey(v1).First());
}
// Returns new wire with the edge replaced by a linear edge with the vertex v moved to p
TopoDS_Wire adjust(const TopoDS_Wire& w, const TopoDS_Vertex& v, const gp_Pnt& p) {
BRep_Builder b;
TopoDS_Vertex v2;
b.MakeVertex(v2, p, BRep_Tool::Tolerance(v));
ShapeBuild_ReShape reshape;
reshape.Replace(v.Oriented(TopAbs_FORWARD), v2);
return TopoDS::Wire(reshape.Apply(w));
}
// A wrapper around BRepBuilderAPI_MakeWire that makes sure segments are connected either by moving end points or by adding intermediate segments
class wire_builder {
private:
BRepBuilderAPI_MakeWire mw_;
double p_;
bool override_next_;
gp_Pnt next_override_;
const IfcUtil::IfcBaseClass* inst_;
public:
wire_builder(double p, const IfcUtil::IfcBaseClass* inst = 0) : p_(p), override_next_(false), inst_(inst) {}
void operator()(const TopoDS_Shape& a) {
const TopoDS_Wire& w = TopoDS::Wire(a);
if (override_next_) {
override_next_ = false;
TopoDS_Edge e = first_edge(w);
mw_.Add(adjust(w, TopExp::FirstVertex(e, true), next_override_));
} else {
mw_.Add(w);
}
}
void operator()(const TopoDS_Shape& a, const TopoDS_Shape& b, bool last) {
TopoDS_Wire w1 = TopoDS::Wire(a);
const TopoDS_Wire& w2 = TopoDS::Wire(b);
if (override_next_) {
override_next_ = false;
TopoDS_Edge e = first_edge(w1);
w1 = adjust(w1, TopExp::FirstVertex(e, true), next_override_);
}
TopoDS_Vertex w11, w12, w21, w22;
TopExp::Vertices(w1, w11, w12);
TopExp::Vertices(w2, w21, w22);
gp_Pnt p1 = BRep_Tool::Pnt(w12);
gp_Pnt p2 = BRep_Tool::Pnt(w21);
double dist = p1.Distance(p2);
// Distance is within 2p, this is fine
if (dist < p_) {
mw_.Add(w1);
goto check;
}
// Distance is too large for attempting to move end points, add intermediate edge
if (dist > 1000. * p_) {
mw_.Add(w1);
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
Logger::Message(Logger::LOG_ERROR, "Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_);
goto check;
}
{
TopTools_IndexedDataMapOfShapeListOfShape wmap1, wmap2;
// Find edges connected to end- and begin vertex
TopExp::MapShapesAndAncestors(w1, TopAbs_VERTEX, TopAbs_EDGE, wmap1);
TopExp::MapShapesAndAncestors(w2, TopAbs_VERTEX, TopAbs_EDGE, wmap2);
const TopTools_ListOfShape& last_edges = wmap1.FindFromKey(w12);
const TopTools_ListOfShape& first_edges = wmap2.FindFromKey(w21);
double _, __;
if (last_edges.Extent() == 1 && first_edges.Extent() == 1) {
Handle(Geom_Curve) c1 = BRep_Tool::Curve(TopoDS::Edge(last_edges.First()), _, __);
Handle(Geom_Curve) c2 = BRep_Tool::Curve(TopoDS::Edge(first_edges.First()), _, __);
const bool is_line1 = c1->DynamicType() == STANDARD_TYPE(Geom_Line);
const bool is_line2 = c2->DynamicType() == STANDARD_TYPE(Geom_Line);
// Adjust the segment that is linear
if (is_line1) {
mw_.Add(adjust(w1, w12, p2));
Logger::Message(Logger::LOG_ERROR, "Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
} else if (is_line2 && !last) {
mw_.Add(w1);
override_next_ = true;
next_override_ = p1;
Logger::Message(Logger::LOG_ERROR, "Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
} else {
// If both aren't linear an edge is added
mw_.Add(w1);
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
Logger::Message(Logger::LOG_ERROR, "Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_);
}
} else {
Logger::Error("Internal error, inconsistent wire segments", inst_);
mw_.Add(w1);
}
}
check:
if (mw_.Error() == BRepBuilderAPI_NonManifoldWire) {
Logger::Error("Non-manifold curve segments:", inst_);
} else if (mw_.Error() == BRepBuilderAPI_DisconnectedWire) {
Logger::Error("Failed to join curve segments:", inst_);
}
}
const TopoDS_Wire& wire() { return mw_.Wire(); }
};
template <typename Fn>
void shape_pair_enumerate(TopTools_ListIteratorOfListOfShape& it, Fn& fn, bool closed) {
bool is_first = true;
TopoDS_Shape first, previous, current;
for (; it.More(); it.Next(), is_first = false) {
current = it.Value();
if (is_first) {
first = current;
} else {
fn(previous, current, false);
}
previous = current;
}
if (closed) {
fn(current, first, true);
} else {
fn(current);
}
}
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire& wire) {
if ( getValue(GV_PLANEANGLE_UNIT)<0 ) {
Logger::Message(Logger::LOG_WARNING,"Creating a composite curve without unit information:",l);
@@ -167,105 +325,45 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire
return use_radians || use_degrees;
}
BRepBuilderAPI_MakeWire w;
TopoDS_Vertex wire_first_vertex, wire_last_vertex, edge_first_vertex, edge_last_vertex;
IfcSchema::IfcCompositeCurveSegment::list::ptr segments = l->Segments();
const double precision_sq_2 = 2 * getValue(GV_PRECISION) * getValue(GV_PRECISION);
TopTools_ListOfShape converted_segments;
for(IfcSchema::IfcCompositeCurveSegment::list::it it = segments->begin(); it != segments->end(); ++it) {
for (IfcSchema::IfcCompositeCurveSegment::list::it it = segments->begin(); it != segments->end(); ++it) {
IfcSchema::IfcCurve* curve = (*it)->ParentCurve();
TopoDS_Wire segment;
if (!convert_wire(curve, segment)) {
Logger::Message(Logger::LOG_ERROR, "Failed to convert curve:", curve);
continue;
}
if (!(*it)->SameSense()) {
segment.Reverse();
}
ShapeFix_ShapeTolerance FTol;
FTol.SetTolerance(segment, getValue(GV_PRECISION), TopAbs_WIRE);
TopExp::Vertices(segment, edge_first_vertex, edge_last_vertex);
if (it == segments->begin()) {
wire_first_vertex = edge_first_vertex;
} else {
gp_Pnt first = BRep_Tool::Pnt(edge_first_vertex);
gp_Pnt last = BRep_Tool::Pnt(wire_last_vertex);
converted_segments.Append(segment);
Standard_Real distance = first.SquareDistance(last);
if (distance > precision_sq_2) {
w.Add(BRepBuilderAPI_MakeEdge(wire_last_vertex, edge_first_vertex));
Logger::Message(Logger::LOG_ERROR, "Closed gap on:", l);
}
}
w.Add(segment);
if ( w.Error() != BRepBuilderAPI_WireDone ) {
if (w.Error() == BRepBuilderAPI_NonManifoldWire) {
Logger::Message(Logger::LOG_ERROR, "Non-manifold curve segments:", l);
} else if (w.Error() == BRepBuilderAPI_DisconnectedWire) {
Logger::Message(Logger::LOG_ERROR, "Failed to join curve segments:", l);
gp_Pnt p1, p2;
int precision = 4;
double d = 0.;
if (!wire_last_vertex.IsNull()) {
p1 = BRep_Tool::Pnt(wire_last_vertex);
}
if (!edge_first_vertex.IsNull()) {
p2 = BRep_Tool::Pnt(edge_first_vertex);
}
if (!wire_last_vertex.IsNull() && !edge_first_vertex.IsNull()) {
d = p1.Distance(p2);
precision = ceil(-log10(d)) + 3;
}
if (!wire_last_vertex.IsNull()) {
std::stringstream ss;
ss << std::setprecision(precision) << "Last vertex at (" << p1.X() << " " << p1.Y() << " " << p1.Z() << ")";
Logger::Message(Logger::LOG_NOTICE, ss.str());
}
if (!edge_first_vertex.IsNull()) {
std::stringstream ss;
ss << std::setprecision(precision) << "Segment starts at (" << p2.X() << " " << p2.Y() << " " << p2.Z() << ")";
if (d > 0.) {
ss << ", distance " << d << " > precision " << std::fixed << getValue(GV_PRECISION) / 10.;
}
ss << " for:";
Logger::Message(Logger::LOG_NOTICE, ss.str(), (*it));
}
}
return false;
}
wire_last_vertex = edge_last_vertex;
}
gp_Pnt first = BRep_Tool::Pnt(edge_last_vertex);
gp_Pnt last = BRep_Tool::Pnt(wire_first_vertex);
BRepBuilderAPI_MakeWire w;
TopoDS_Vertex wire_first_vertex, wire_last_vertex, edge_first_vertex, edge_last_vertex;
Standard_Real distance = first.SquareDistance(last);
if (distance > precision_sq_2) {
w.Add(BRepBuilderAPI_MakeEdge(edge_last_vertex, wire_first_vertex));
const double precision_sq_2 = 2 * getValue(GV_PRECISION) * getValue(GV_PRECISION);
Logger::Message(Logger::LOG_ERROR, "Closed gap on:", l);
}
TopTools_ListIteratorOfListOfShape it(converted_segments);
wire = w.Wire();
IfcEntityList::ptr profile = l->data().getInverse(&IfcSchema::IfcProfileDef::Class(), -1);
const bool force_close = profile && profile->size() > 0;
wire_builder bld(getValue(GV_PRECISION), l);
shape_pair_enumerate(it, bld, force_close);
wire = bld.wire();
return true;
}
@@ -395,14 +493,25 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyline* l, TopoDS_Wire& resu
polygon.Append(pnt);
}
const double eps = getValue(GV_PRECISION) * 10;
const bool closed_by_proximity = polygon.Length() >= 2 && polygon.First().Distance(polygon.Last()) < eps;
if (closed_by_proximity) {
// tfk: note 1-based
polygon.Remove(polygon.Length());
}
// Remove points that are too close to one another
remove_duplicate_points_from_loop(polygon, false);
remove_duplicate_points_from_loop(polygon, closed_by_proximity, eps);
BRepBuilderAPI_MakePolygon w;
for (int i = 1; i <= polygon.Length(); ++i) {
w.Add(polygon.Value(i));
}
if (closed_by_proximity) {
w.Close();
}
result = w.Wire();
return true;
}
@@ -426,7 +535,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyLoop* l, TopoDS_Wire& resu
}
// Remove points that are too close to one another
remove_duplicate_points_from_loop(polygon, true);
const double eps = getValue(GV_PRECISION) * 10;
remove_duplicate_points_from_loop(polygon, true, eps);
int count = polygon.Length();
if (original_count - count != 0) {
@@ -445,7 +555,14 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyLoop* l, TopoDS_Wire& resu
}
w.Close();
result = w.Wire();
result = w.Wire();
TopTools_ListOfShape results;
if (wire_intersections(result, results)) {
Logger::Error("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected", l);
select_largest(results, result);
}
return true;
}
@@ -12,18 +12,24 @@ import OCC.AIS
from collections import defaultdict, Iterable, OrderedDict
os.environ['QT_API'] = 'pyqt4'
try:
QString = unicode
except NameError:
# Python 3
QString = str
os.environ['QT_API'] = 'pyqt5'
try:
from pyqode.qt import QtCore
except BaseException:
pass
from PyQt4 import QtGui, QtCore
from PyQt5 import QtCore, QtGui, QtWidgets
from .code_editor_pane import code_edit
try:
from OCC.Display.pyqt4Display import qtViewer3d
from OCC.Display.pyqt5Display import qtViewer3d
except BaseException:
import OCC.Display
@@ -33,9 +39,9 @@ except BaseException:
pass
try:
OCC.Display.backend.get_backend("qt-pyqt4")
OCC.Display.backend.get_backend("qt-pyqt5")
except BaseException:
OCC.Display.backend.load_backend("qt-pyqt4")
OCC.Display.backend.load_backend("qt-pyqt5")
from OCC.Display.qtDisplay import qtViewer3d
@@ -43,14 +49,11 @@ from .main import settings, iterator
from .occ_utils import display_shape
from .. import open as open_ifc_file
from .. import get_supertype
# Depending on Python version and what not there may or may not be a QString
try:
from PyQt4.QtCore import QString
except ImportError:
QString = str
from .. import version as ifcopenshell_version
if ifcopenshell_version < "0.6":
# not yet ported
from .. import get_supertype
class configuration(object):
def __init__(self):
@@ -113,11 +116,11 @@ if selection:
return OrderedDict([(k, self.config_decode(self.config.get(s, k))) for k in self.config.options(s)])
class application(QtGui.QApplication):
class application(QtWidgets.QApplication):
"""A pythonOCC, PyQt based IfcOpenShell application
with two tree views and a graphical 3d view"""
class abstract_treeview(QtGui.QTreeWidget):
class abstract_treeview(QtWidgets.QTreeWidget):
"""Base class for the two treeview controls"""
@@ -126,7 +129,7 @@ class application(QtGui.QApplication):
instanceDisplayModeChanged = QtCore.pyqtSignal([object, int])
def __init__(self):
QtGui.QTreeView.__init__(self)
QtWidgets.QTreeView.__init__(self)
self.setColumnCount(len(self.ATTRIBUTES))
self.setHeaderLabels(self.ATTRIBUTES)
self.children = defaultdict(list)
@@ -140,23 +143,23 @@ class application(QtGui.QApplication):
return c
def contextMenuEvent(self, event):
menu = QtGui.QMenu(self)
menu = QtWidgets.QMenu(self)
visibility = [menu.addAction("Show"), menu.addAction("Hide")]
displaymode = [menu.addAction("Solid"), menu.addAction("Wireframe")]
action = menu.exec_(self.mapToGlobal(event.pos()))
index = self.selectionModel().currentIndex()
inst = index.data(QtCore.Qt.UserRole)
if hasattr(inst, 'toPyObject'):
inst = inst.toPyObject()
inst = inst
if action in visibility:
self.instanceVisibilityChanged.emit(inst, visibility.index(action))
elif action in displaymode:
self.instanceDisplayModeChanged.emit(inst, displaymode.index(action))
def clicked(self, index):
def clicked_(self, index):
inst = index.data(QtCore.Qt.UserRole)
if hasattr(inst, 'toPyObject'):
inst = inst.toPyObject()
inst = inst
if inst:
self.instanceSelected.emit(inst)
@@ -165,7 +168,7 @@ class application(QtGui.QApplication):
if itm is None:
return
self.selectionModel().setCurrentIndex(itm,
QtGui.QItemSelectionModel.SelectCurrent | QtGui.QItemSelectionModel.Rows)
QtCore.QItemSelectionModel.SelectCurrent | QtCore.QItemSelectionModel.Rows)
class decomposition_treeview(abstract_treeview):
@@ -206,11 +209,11 @@ class application(QtGui.QApplication):
sl.append(product.is_a())
else:
sl.append(getattr(product, attr) or '')
itm = items[product] = QtGui.QTreeWidgetItem(items.get(parent, self), sl)
itm = items[product] = QtWidgets.QTreeWidgetItem(items.get(parent, self), sl)
itm.setData(0, QtCore.Qt.UserRole, product)
self.children[parent].append(product)
self.product_to_item = dict(zip(items.keys(), map(self.indexFromItem, items.values())))
self.connect(self, QtCore.SIGNAL("clicked(const QModelIndex &)"), self.clicked)
self.clicked.connect(self.clicked_)
self.expandAll()
class type_treeview(abstract_treeview):
@@ -230,33 +233,34 @@ class application(QtGui.QApplication):
add(s)
s2, t2 = map(QString, (s, t))
if t2 not in items:
itm = items[t2] = QtGui.QTreeWidgetItem(items.get(s2, self), [t2])
itm = items[t2] = QtWidgets.QTreeWidgetItem(items.get(s2, self), [t2])
itm.setData(0, QtCore.Qt.UserRole, t2)
self.children[s2].append(t2)
add(t)
if ifcopenshell_version < "0.6":
add(t)
for p in products:
t = QString(p.is_a())
itm = items[p] = QtGui.QTreeWidgetItem(items.get(t, self), [p.Name or '<no name>'])
itm = items[p] = QtWidgets.QTreeWidgetItem(items.get(t, self), [p.Name or '<no name>'])
itm.setData(0, QtCore.Qt.UserRole, t)
self.children[t].append(p)
self.product_to_item = dict(zip(items.keys(), map(self.indexFromItem, items.values())))
self.connect(self, QtCore.SIGNAL("clicked(const QModelIndex &)"), self.clicked)
self.clicked.connect(self.clicked)
self.expandAll()
class property_table(QtGui.QWidget):
class property_table(QtWidgets.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.layout = QtGui.QVBoxLayout(self)
QtWidgets.QWidget.__init__(self)
self.layout = QtWidgets.QVBoxLayout(self)
self.setLayout(self.layout)
self.scroll = QtGui.QScrollArea(self)
self.scroll = QtWidgets.QScrollArea(self)
self.layout.addWidget(self.scroll)
self.scroll.setWidgetResizable(True)
self.scrollContent = QtGui.QWidget(self.scroll)
self.scrollLayout = QtGui.QVBoxLayout(self.scrollContent)
self.scrollContent = QtWidgets.QWidget(self.scroll)
self.scrollLayout = QtWidgets.QVBoxLayout(self.scrollContent)
self.scrollContent.setLayout(self.scrollLayout)
self.scroll.setWidget(self.scrollContent)
self.prop_dict = {}
@@ -271,17 +275,17 @@ class application(QtGui.QApplication):
if child.widget() is not None:
child.widget().deleteLater()
self.scroll = QtGui.QScrollArea()
self.scroll = QtWidgets.QScrollArea()
self.scroll.setWidgetResizable(True)
prop_sets = self.prop_dict.get(str(product))
if prop_sets is not None:
for k, v in prop_sets:
group_box = QtGui.QGroupBox()
group_box = QtWidgets.QGroupBox()
group_box.setTitle(k)
group_layout = QtGui.QVBoxLayout()
group_layout = QtWidgets.QVBoxLayout()
group_box.setLayout(group_layout)
for name, value in v.items():
@@ -300,7 +304,7 @@ class application(QtGui.QApplication):
type_str = " <i>(%s)</i>" % value.is_a()
else:
type_str = ""
label = QtGui.QLabel("<b>%s</b>: %s%s" % (prop_name, value_str, type_str))
label = QtWidgets.QLabel("<b>%s</b>: %s%s" % (prop_name, value_str, type_str))
group_layout.addWidget(label)
group_layout.addStretch()
@@ -308,7 +312,7 @@ class application(QtGui.QApplication):
self.scrollLayout.addStretch()
else:
label = QtGui.QLabel("No IfcPropertySets asscociated with selected entity instance")
label = QtWidgets.QLabel("No IfcPropertySets asscociated with selected entity instance")
self.scrollLayout.addWidget(label)
def load_file(self, f, **kwargs):
@@ -431,7 +435,7 @@ class application(QtGui.QApplication):
self.ais_to_product[self.counter] = product
self.product_to_ais[product] = ais
self.counter += 1
QtGui.QApplication.processEvents()
QtWidgets.QApplication.processEvents()
if product.is_a() in {'IfcSpace', 'IfcOpeningElement'}:
v.Context.Erase(ais, True)
progress = it.progress() // 2
@@ -493,14 +497,14 @@ class application(QtGui.QApplication):
inst = self.ais_to_product[ais.GetObject().SelectionPriority()]
self.instanceSelected.emit(inst)
class window(QtGui.QMainWindow):
class window(QtWidgets.QMainWindow):
TITLE = "IfcOpenShell IFC viewer"
window_closed = QtCore.pyqtSignal([])
def __init__(self):
QtGui.QMainWindow.__init__(self)
QtWidgets.QMainWindow.__init__(self)
self.setWindowTitle(self.TITLE)
self.menu = self.menuBar()
self.menus = {}
@@ -515,9 +519,9 @@ class application(QtGui.QApplication):
self.menus[menu] = m
if icon:
a = QtGui.QAction(QtGui.QIcon(icon), label, self)
a = QtWidgets.QAction(QtGui.QIcon(icon), label, self)
else:
a = QtGui.QAction(label, self)
a = QtWidgets.QAction(label, self)
if shortcut:
a.setShortcut(shortcut)
@@ -534,20 +538,20 @@ class application(QtGui.QApplication):
return handler
def __init__(self, settings=None):
QtGui.QApplication.__init__(self, sys.argv)
QtWidgets.QApplication.__init__(self, sys.argv)
self.window = application.window()
self.tree = application.decomposition_treeview()
self.tree2 = application.type_treeview()
self.propview = self.property_table()
self.canvas = application.viewer(self.window)
self.tabs = QtGui.QTabWidget()
self.tabs = QtWidgets.QTabWidget()
self.window.resize(800, 600)
splitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
splitter.addWidget(self.tabs)
self.tabs.addTab(self.tree, 'Decomposition')
self.tabs.addTab(self.tree2, 'Types')
self.tabs.addTab(self.propview, "Properties")
splitter2 = QtGui.QSplitter(QtCore.Qt.Vertical)
splitter2 = QtWidgets.QSplitter(QtCore.Qt.Vertical)
splitter2.addWidget(self.canvas)
self.editor = code_edit(self.canvas, configuration().options('snippets'))
splitter2.addWidget(self.editor)
@@ -585,8 +589,8 @@ class application(QtGui.QApplication):
sys.exit(self.exec_())
def browse(self):
filename = QtGui.QFileDialog.getOpenFileName(self.window, 'Open file', ".",
"Industry Foundation Classes (*.ifc)")
filename = QtWidgets.QFileDialog.getOpenFileName(self.window, 'Open file', ".",
"Industry Foundation Classes (*.ifc)")[0]
self.load(filename)
def clear(self):
@@ -7,10 +7,10 @@ import sys
import logging
from code import InteractiveConsole
from PyQt4 import QtCore, QtGui
from PyQt5 import QtCore, QtGui, QtWidgets
try:
from PyQt4 import QtWidgets
from PyQt5 import QtWidgets
except BaseException:
QtWidgets = QtGui
@@ -32,23 +32,23 @@ except BaseException:
class StdoutRedirector(object):
'''A class for redirecting stdout to this Text widget.'''
"""A class for redirecting stdout to this Text widget."""
def __init__(self, widget):
self.widget = widget
self.isError = False
def write(self, str):
def write(self, myStr):
self.widget.moveCursor(QtGui.QTextCursor.End)
if self.isError:
self.widget.setTextColor(QtCore.Qt.red)
else:
self.widget.setTextColor(QtCore.Qt.white)
self.widget.insertPlainText(str)
self.widget.insertPlainText(myStr)
self.widget.moveCursor(QtGui.QTextCursor.End)
class code_edit(QtGui.QWidget):
class code_edit(QtWidgets.QWidget):
class Console(InteractiveConsole):
def __init__(*args):
InteractiveConsole.__init__(*args)
@@ -73,18 +73,15 @@ class code_edit(QtGui.QWidget):
self.c = self.Console({'model': self.model, 'viewer': self.viewer, 'selection': product})
def __init__(self, viewer, snippets=None):
self.model = None
self.viewer = viewer
QtGui.QWidget.__init__(self)
self.layout = QtGui.QVBoxLayout(self)
QtWidgets.QWidget.__init__(self)
self.layout = QtWidgets.QVBoxLayout(self)
self.setLayout(self.layout)
self.c = None
self.tools = QtGui.QHBoxLayout(self)
self.tools = QtWidgets.QHBoxLayout(self)
self.layout.addLayout(self.tools)
self.runbutton = QtGui.QPushButton("Run")
self.runbutton = QtWidgets.QPushButton("Run")
width = self.runbutton.fontMetrics().boundingRect("Run").width() + 20
self.runbutton.setMaximumWidth(width)
self.tools.addWidget(self.runbutton)
@@ -129,11 +126,10 @@ class code_edit(QtGui.QWidget):
for snip_name in self.snippets.keys():
self.list.addItem(snip_name)
self.tools.addWidget(self.list)
QtCore.QObject.connect(self.list, QtCore.SIGNAL("currentIndexChanged(int)"), self.replace_snippet)
self.list.currentIndexChanged[int].connect(self.replace_snippet)
self.layout.addWidget(self.editor)
self.output = QtGui.QTextEdit()
self.output = QtWidgets.QTextEdit()
self.output.setReadOnly(True)
self.output.setStyleSheet('font-size: 10pt; font-family: Consolas, Courier; background-color: #444;')
self.layout.addWidget(self.output)
@@ -133,7 +133,7 @@ def display_shape(shape, clr=None, viewer_handle=None):
if isinstance(clr, tuple) and len(clr) == 4 and clr[3] < 1.:
ais.SetTransparency(1. - clr[3])
elif representation:
elif representation and hasattr(OCC.AIS, "AIS_MultipleConnectedShape"):
default_style_applied = None
ais = OCC.AIS.AIS_MultipleConnectedShape(shape)
-4
View File
@@ -38,10 +38,6 @@
#include <boost/shared_ptr.hpp>
#include <boost/dynamic_bitset.hpp>
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH
#define rforeach BOOST_REVERSE_FOREACH
class Argument;
class IfcEntityList;
+5 -1
View File
@@ -70,6 +70,10 @@
using namespace IfcParse;
using namespace IfcWrite;
#ifdef HAVE_ICU
#include <unicode/unistr.h>
#endif
void IfcCharacterDecoder::addChar(std::stringstream& s,const UChar32& ch) {
#ifdef HAVE_ICU
if ( destination ) {
@@ -386,4 +390,4 @@ IfcCharacterEncoder::operator std::string() {
#ifdef HAVE_ICU
UErrorCode IfcCharacterEncoder::status = U_ZERO_ERROR;
UConverter* IfcCharacterEncoder::converter = 0;
#endif
#endif
+1 -1
View File
@@ -65,7 +65,7 @@ public:
virtual ~IfcEntityInstanceData();
boost::shared_ptr<IfcEntityList> getInverse(const IfcParse::declaration* type, int attribute_index);
boost::shared_ptr<IfcEntityList> getInverse (const IfcParse::declaration* type, int attribute_index) const;
Argument* getArgument(unsigned int i) const;
+2 -1
View File
@@ -22,6 +22,7 @@
#include <map>
#include <set>
#include <boost/unordered_map.hpp>
#include "ifc_parse_api.h"
@@ -36,7 +37,7 @@ namespace IfcParse {
class IFC_PARSE_API IfcFile {
public:
typedef std::map<const IfcParse::declaration*, IfcEntityList::ptr> entities_by_type_t;
typedef std::map<unsigned int, IfcUtil::IfcBaseClass*> entity_by_id_t;
typedef boost::unordered_map<unsigned int, IfcUtil::IfcBaseClass*> entity_by_id_t;
typedef std::map<std::string, IfcUtil::IfcBaseClass*> entity_by_guid_t;
typedef std::map<unsigned int, std::vector<unsigned int> > entities_by_ref_t;
typedef entity_by_id_t::const_iterator const_iterator;
+49 -12
View File
@@ -25,9 +25,44 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/optional.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/version.hpp>
#include <iostream>
#include <algorithm>
using boost::property_tree::ptree;
namespace {
static const char* severity_strings[] = {"Notice", "Warning", "Error"};
void plain_text_message(std::ostream& os, const boost::optional<IfcUtil::IfcBaseClass*>& current_product, Logger::Severity type, const std::string& message, const IfcUtil::IfcBaseClass* instance) {
os << "[" << severity_strings[type] << "] ";
if (current_product) {
std::string global_id = *(**current_product).data().getArgument((**current_product).declaration().as_entity()->attribute_index("GlobalId"));
os << "{" << global_id << "} ";
}
os << message << std::endl;
if (instance) {
os << instance->data().toString() << std::endl;
}
}
void json_message(std::ostream& os, const boost::optional<IfcUtil::IfcBaseClass*>& current_product, Logger::Severity type, const std::string& message, const IfcUtil::IfcBaseClass* instance) {
ptree pt;
pt.put("level", severity_strings[type]);
if (current_product) {
pt.put("product", (**current_product).data().toString());
}
pt.put("message", message);
if (instance) {
pt.put("instance", instance->data().toString());
}
boost::property_tree::write_json(os, pt, false);
}
}
void Logger::SetOutput(std::ostream* l1, std::ostream* l2) {
log1 = l1;
log2 = l2;
@@ -37,15 +72,11 @@ void Logger::SetOutput(std::ostream* l1, std::ostream* l2) {
}
void Logger::Message(Logger::Severity type, const std::string& message, const IfcUtil::IfcBaseClass* instance) {
if ( log2 && type >= verbosity ) {
(*log2) << "[" << severity_strings[type] << "] ";
if ( current_product ) {
std::string global_id = *(**current_product).data().getArgument((**current_product).declaration().as_entity()->attribute_index("GlobalId"));
(*log2) << "{" << global_id << "} ";
}
(*log2) << message << std::endl;
if (instance) {
(*log2) << instance->data().toString() << std::endl;
if (log2 && type >= verbosity) {
if (format == FMT_PLAIN) {
plain_text_message(*log2, current_product, type, message, instance);
} else if (format == FMT_JSON) {
json_message(*log2, current_product, type, message, instance);
}
}
}
@@ -55,26 +86,32 @@ void Logger::Message(Logger::Severity type, const std::exception& exception, con
}
void Logger::Status(const std::string& message, bool new_line) {
if ( log1 ) {
if (log1) {
(*log1) << message;
if ( new_line ) (*log1) << std::endl;
else (*log1) << std::flush;
}
}
void Logger::ProgressBar(int progress) {
if ( log1 ) {
if (log1) {
Status("\r[" + std::string(progress,'#') + std::string(50 - progress,' ') + "]", false);
}
}
std::string Logger::GetLog() {
return log_stream.str();
}
void Logger::Verbosity(Logger::Severity v) { verbosity = v; }
Logger::Severity Logger::Verbosity() { return verbosity; }
void Logger::OutputFormat(Format f) { format = f; }
Logger::Format Logger::OutputFormat() { return format; }
std::ostream* Logger::log1 = 0;
std::ostream* Logger::log2 = 0;
std::stringstream Logger::log_stream;
Logger::Severity Logger::verbosity = Logger::LOG_NOTICE;
const char* Logger::severity_strings[] = { "Notice","Warning","Error" };
Logger::Format Logger::format = Logger::FMT_PLAIN;
boost::optional<IfcUtil::IfcBaseClass*> Logger::current_product;
+7 -1
View File
@@ -36,12 +36,13 @@
class IFC_PARSE_API Logger {
public:
typedef enum { LOG_NOTICE, LOG_WARNING, LOG_ERROR } Severity;
typedef enum { FMT_PLAIN, FMT_JSON } Format;
private:
static std::ostream* log1;
static std::ostream* log2;
static std::stringstream log_stream;
static Severity verbosity;
static const char* severity_strings[];
static Format format;
static boost::optional<IfcUtil::IfcBaseClass*> current_product;
public:
@@ -51,9 +52,14 @@ public:
/// Determines to what stream respectively progress and errors are logged
static void SetOutput(std::ostream* l1, std::ostream* l2);
/// Determines the types of log messages to get logged
static void Verbosity(Severity v);
static Severity Verbosity();
/// Determines output format: plain text or sequence of JSON objects
static void OutputFormat(Format f);
static Format OutputFormat();
/// Log a message to the output stream
static void Message(Severity type, const std::string& message, const IfcUtil::IfcBaseClass* instance = 0);
+17 -12
View File
@@ -988,7 +988,7 @@ unsigned IfcEntityInstanceData::set_id(boost::optional<unsigned> i) {
//
// Returns the entities of Entity type that have this entity in their ArgumentList
//
IfcEntityList::ptr IfcEntityInstanceData::getInverse(const IfcParse::declaration* type, int attribute_index) {
IfcEntityList::ptr IfcEntityInstanceData::getInverse(const IfcParse::declaration* type, int attribute_index) const {
return file->getInverse(id_, type, attribute_index);
}
@@ -1891,7 +1891,7 @@ IfcEntityList::ptr IfcFile::instances_by_reference(int t) {
IfcUtil::IfcBaseClass* IfcFile::instance_by_id(int id) {
entity_by_id_t::const_iterator it = byid.find(id);
if (it == byid.end()) {
throw IfcException("Entity not found");
throw IfcException("Instance #" + boost::lexical_cast<std::string>(id) + " not found");
}
return it->second;
}
@@ -1899,7 +1899,7 @@ IfcUtil::IfcBaseClass* IfcFile::instance_by_id(int id) {
IfcUtil::IfcBaseClass* IfcFile::instance_by_guid(const std::string& guid) {
entity_by_guid_t::const_iterator it = byguid.find(guid);
if ( it == byguid.end() ) {
throw IfcException("Entity not found");
throw IfcException("Instance with GlobalId '" + guid + "' not found");
} else {
return it->second;
}
@@ -1980,15 +1980,20 @@ IfcEntityList::ptr IfcFile::getInverse(int instance_id, const IfcParse::declarat
for(IfcEntityList::it it = all->begin(); it != all->end(); ++it) {
bool valid = type == 0 || (*it)->declaration().is(*type);
if (valid && attribute_index >= 0) {
Argument* arg = (*it)->data().getArgument(attribute_index);
if (arg->type() == IfcUtil::Argument_ENTITY_INSTANCE) {
valid = instance == *arg;
} else if (arg->type() == IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE) {
IfcEntityList::ptr li = *arg;
valid = li->contains(instance);
} else if (arg->type() == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE) {
IfcEntityListList::ptr li = *arg;
valid = li->contains(instance);
try {
Argument* arg = (*it)->data().getArgument(attribute_index);
if (arg->type() == IfcUtil::Argument_ENTITY_INSTANCE) {
valid = instance == *arg;
} else if (arg->type() == IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE) {
IfcEntityList::ptr li = *arg;
valid = li->contains(instance);
} else if (arg->type() == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE) {
IfcEntityListList::ptr li = *arg;
valid = li->contains(instance);
}
} catch (const IfcException& e) {
valid = false;
Logger::Error(e);
}
}
if (valid) {
+5 -3
View File
@@ -21,6 +21,8 @@
#include "ColladaSerializer.h"
#include <boost/foreach.hpp>
#include <COLLADASWPrimitves.h>
#include <COLLADASWSource.h>
#include <COLLADASWScene.h>
@@ -223,7 +225,7 @@ void ColladaSerializer::ColladaExporter::ColladaScene::add(
node.addMatrix(matrix_array);
COLLADASW::InstanceGeometry instanceGeometry(mSW);
instanceGeometry.setUrl ("#" + geom_name);
foreach(std::string material_name, material_ids) {
BOOST_FOREACH(std::string material_name, material_ids) {
/// @todo This is done 6 times in this file, try to perform this once and be done with the material naming for the export.
collada_id(material_name);
COLLADASW::InstanceMaterial material (material_name, "#" + material_name);
@@ -362,7 +364,7 @@ bool ColladaSerializer::ColladaExporter::ColladaMaterials::contains(const IfcGeo
void ColladaSerializer::ColladaExporter::ColladaMaterials::write() {
effects.close();
foreach(const IfcGeom::Material& material, materials) {
BOOST_FOREACH(const IfcGeom::Material& material, materials) {
std::string material_name = (serializer->settings().get(SerializerSettings::USE_MATERIAL_NAMES)
? material.original_name() : material.name());
std::string material_name_unescaped = material_name; // workaround double-escaping that would occur in addInstanceEffect()
@@ -408,7 +410,7 @@ void ColladaSerializer::ColladaExporter::write(const IfcGeom::TriangulationEleme
collada_id(representation_id);
std::vector<std::string> material_references;
foreach(const IfcGeom::Material& material, mesh.materials()) {
BOOST_FOREACH(const IfcGeom::Material& material, mesh.materials()) {
if (!materials.contains(material)) {
materials.add(material);
}
+12 -12
View File
@@ -360,26 +360,26 @@ void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
if (pnt.Z() < zmin) { zmin = pnt.Z(); }
if (pnt.Z() > zmax) { zmax = pnt.Z(); }
}}
if (section_height) {
if (zmin > section_height || zmax < section_height) continue;
} else {
if (zmin == inf || (zmax - zmin) < 1.) continue;
}
// Priority:
// 1) section_height
// 2) Storey elevation + 1m
// 3) zmin + 1m
// Empty geometry, no vertices encountered
if (zmin == inf) continue;
// Determine slicing plane z coordinate, priority:
// 1) explicitly set global section height
// 2) containing building storey elevation + 1m
// 3) zmin (from geometry bounding box) + 1m
double cut_z;
if (section_height) {
cut_z = section_height.get();
} else if (storey_elevation) {
} else if (storey_elevation && !(zmin > *storey_elevation || zmax < *storey_elevation)) {
cut_z = storey_elevation.get() + 1.;
} else {
cut_z = zmin + 1.;
}
// No intersection with bounding box, fail early
if (zmin > cut_z || zmax < cut_z) continue;
// Create a horizontal cross section 1 meter above the bottom point of the shape
TopoDS_Shape result = BRepAlgoAPI_Section(subshape, gp_Pln(gp_Pnt(0, 0, cut_z), gp::DZ()));
@@ -22,6 +22,7 @@
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/version.hpp>
#include <boost/foreach.hpp>
#include "XmlSerializer.h"
@@ -279,6 +280,9 @@ ptree& descend(IfcSchema::IfcObjectDefinition* product, ptree& tree) {
if (pset->declaration().is(IfcSchema::IfcElementQuantity::Class())) {
format_entity_instance(pset, child, true);
}
if (pset->declaration().is(IfcSchema::IfcElementQuantity::Class())) {
format_entity_instance(pset, child, true);
}
}
#ifdef USE_IFC4
@@ -358,16 +362,16 @@ void MAKE_TYPE_NAME(XmlSerializer)::finalize() {
ptree root, header, units, decomposition, properties, quantities, types, layers, materials;
// Write the SPF header as XML nodes.
foreach(const std::string& s, file->header().file_description().description()) {
BOOST_FOREACH(const std::string& s, file->header().file_description().description()) {
header.add_child("file_description.description", ptree(s));
}
foreach(const std::string& s, file->header().file_name().author()) {
BOOST_FOREACH(const std::string& s, file->header().file_name().author()) {
header.add_child("file_name.author", ptree(s));
}
foreach(const std::string& s, file->header().file_name().organization()) {
BOOST_FOREACH(const std::string& s, file->header().file_name().organization()) {
header.add_child("file_name.organization", ptree(s));
}
foreach(const std::string& s, file->header().file_schema().schema_identifiers()) {
BOOST_FOREACH(const std::string& s, file->header().file_schema().schema_identifiers()) {
header.add_child("file_schema.schema_identifiers", ptree(s));
}
header.put("file_description.implementation_level", file->header().file_description().implementation_level());
@@ -450,8 +454,11 @@ void MAKE_TYPE_NAME(XmlSerializer)::finalize() {
emitted_materials.insert(mat);
ptree node;
node.put("<xmlattr>.id", qualify_unrooted_instance(mat));
if (mat->as<IfcSchema::IfcMaterialLayerSetUsage>()) {
IfcSchema::IfcMaterialLayerSet* layerset = mat->as<IfcSchema::IfcMaterialLayerSetUsage>()->ForLayerSet();
if (mat->as<IfcSchema::IfcMaterialLayerSetUsage>() || mat->as<IfcSchema::IfcMaterialLayerSet>()) {
IfcSchema::IfcMaterialLayerSet* layerset = mat->as<IfcSchema::IfcMaterialLayerSet>();
if (!layerset) {
layerset = mat->as<IfcSchema::IfcMaterialLayerSetUsage>()->ForLayerSet();
}
if (layerset->hasLayerSetName()) {
node.put("<xmlattr>.LayerSetName", layerset->LayerSetName());
}