mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-08 17:01:40 +00:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4dc3dc37a5 | |||
| b5b73fd079 | |||
| 4a2465674e | |||
| 46d24f112c | |||
| 5b13db129d | |||
| a57f08a9da | |||
| 4ec24e93a6 | |||
| 7812ecb189 | |||
| 7852c06ab7 | |||
| db44eaf221 | |||
| 1064e2fec8 | |||
| a55187600e | |||
| 059de764d6 | |||
| e39c7e7195 | |||
| fb59cd4004 | |||
| 7bc411ac9b | |||
| d181d03a21 | |||
| 625c757326 | |||
| df1225ce95 | |||
| 4881238ce6 | |||
| 336b628be2 | |||
| 575f9e015b | |||
| b3ee2c6511 | |||
| bf4bf3d327 | |||
| 00e4a24153 | |||
| 7b26d6d541 | |||
| 3fb822b07b | |||
| b09fc12868 | |||
| 04ae29759b | |||
| 2905900d5c | |||
| e45ad15d91 | |||
| 8ef971cfb0 | |||
| 630c742a77 | |||
| 2885cd74e3 | |||
| 286a76ddda | |||
| aa3ccf5cb5 | |||
| c9ce6a6df9 | |||
| 1cfb15f060 | |||
| 104896b3e2 | |||
| b8c90eaa5e | |||
| 8bd91113a0 | |||
| 52942f8a3e | |||
| c40f9fdd97 | |||
| a1c33af488 | |||
| a72223acfd | |||
| ed9718af2a | |||
| e15e5a2d20 | |||
| 51dc0e506c | |||
| feac80aa9d | |||
| 842dcb7579 | |||
| 6685aa16d3 | |||
| 91517a9dd1 | |||
| 891732d22f | |||
| 2cf06c0d6a | |||
| 5dd146f357 | |||
| cfc4a03312 | |||
| 9346947476 | |||
| d4a5b33b8f | |||
| 9ba2266d0c | |||
| 75ce57c203 | |||
| 23a05c47d4 | |||
| 606dbf7226 | |||
| c01db8a86a | |||
| c7e2fc5551 | |||
| 63638a4d26 | |||
| badac24d6c | |||
| 549cc76a2c | |||
| 9a94a5ed5a | |||
| 7fe9c3ccb1 |
@@ -18,7 +18,7 @@
|
||||
################################################################################
|
||||
|
||||
cmake_minimum_required(VERSION 3.1.3)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
|
||||
|
||||
project (IfcOpenShell)
|
||||
@@ -352,7 +352,9 @@ endif()
|
||||
if(OCCT_STATIC)
|
||||
find_package(Threads)
|
||||
# OPENCASCADE_LIBRARIES repeated three times below in order to fix cyclic dependencies - use --start-group ... --end-group instead?
|
||||
set(OPENCASCADE_LIBRARIES -Wl,--start-group ${OPENCASCADE_LIBRARIES} -Wl,--end-group ${CMAKE_THREAD_LIBS_INIT})
|
||||
if(NOT APPLE)
|
||||
set(OPENCASCADE_LIBRARIES -Wl,--start-group ${OPENCASCADE_LIBRARIES} -Wl,--end-group ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
if (NOT APPLE AND NOT WIN32)
|
||||
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "rt")
|
||||
endif()
|
||||
@@ -541,7 +543,7 @@ function(files_for_ifc_version IFC_VERSION RESULT_NAME)
|
||||
)
|
||||
endfunction()
|
||||
|
||||
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2")
|
||||
set(SCHEMA_VERSIONS "2x3" "4") # "4x1" "4x2")
|
||||
|
||||
if(COMPILE_SCHEMA)
|
||||
# @todo, this appears to be untested at the moment
|
||||
@@ -711,8 +713,8 @@ set_target_properties(serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS $
|
||||
TARGET_LINK_LIBRARIES(serializers ${SERIALIZER_SCHEMA_LIBRARIES})
|
||||
|
||||
# IfcConvert
|
||||
file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/*.cpp)
|
||||
file(GLOB IFCCONVERT_H_FILES ../src/ifcconvert/*.h)
|
||||
file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/IfcConvert.cpp)
|
||||
file(GLOB IFCCONVERT_H_FILES ../src/ifcconvert/skip-for-now.h)
|
||||
set(IFCCONVERT_FILES ${IFCCONVERT_CPP_FILES} ${IFCCONVERT_H_FILES})
|
||||
ADD_EXECUTABLE(IfcConvert ${IFCCONVERT_FILES})
|
||||
set_target_properties(IfcConvert PROPERTIES COMPILE_FLAGS "${CONVERT_PRECISION}")
|
||||
|
||||
+30
-38
@@ -97,7 +97,7 @@ SWIG_VERSION="3.0.12"
|
||||
OPENCOLLADA_VERSION="v1.6.68"
|
||||
GMP_VERSION="6.1.2"
|
||||
MPFR_VERSION="3.1.5"
|
||||
CGAL_VERSION="4.13"
|
||||
CGAL_VERSION="5.2"
|
||||
|
||||
# binaries
|
||||
cp="cp"
|
||||
@@ -253,7 +253,7 @@ print("Building:", *sorted(targets, key=lambda t: len(list(v(t)))))
|
||||
|
||||
# Check that required tools are in PATH
|
||||
|
||||
for cmd in [git, bunzip2, tar, cc, cplusplus, autoconf, automake, yacc, make, "patch", "m4"]:
|
||||
for cmd in [git, bunzip2, tar, cc, cplusplus, autoconf, automake, yacc, make, "patch", "m4", "cmake"]:
|
||||
if which(cmd) is None:
|
||||
raise ValueError("Required tool '%s' not installed or not added to PATH" % (cmd,))
|
||||
|
||||
@@ -320,7 +320,8 @@ def run_cmake(arg1, cmake_args, cmake_dir=None, cwd=None):
|
||||
P=".."
|
||||
else:
|
||||
P=cmake_dir
|
||||
cmake_path= os.path.join(DEPS_DIR, "install", "cmake-%s" % (CMAKE_VERSION,), "bin", "cmake")
|
||||
# @todo this is already in v0.6
|
||||
cmake_path = "cmake"
|
||||
run([cmake_path, P]+cmake_args+["-DCMAKE_BUILD_TYPE=%s" % (BUILD_CFG,)], cwd=cwd)
|
||||
|
||||
def git_clone_or_pull_repository(clone_url, target_dir, revision=None):
|
||||
@@ -428,7 +429,7 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d
|
||||
logger.info( "\rInstalled %s \n" % (name,))
|
||||
else:
|
||||
logger.info( "\rConfiguring %s..." % (name,))
|
||||
run([bash, "./bootstrap.sh"], cwd=extract_dir)
|
||||
run([bash, "./bootstrap.sh"] + (['--with-toolset=clang'] if "clang" in os.environ.get('CXX', '') else []), cwd=extract_dir)
|
||||
logger.info("\rBuilding %s... " % (name,))
|
||||
run(["./b2", "-j%s" % (IFCOS_NUM_BUILD_PROCS,)]+build_tool_args, cwd=extract_dir)
|
||||
logger.info("\rInstalling %s... " % (name,))
|
||||
@@ -481,27 +482,6 @@ os.environ["CXXFLAGS"] = CXXFLAGS
|
||||
os.environ["CFLAGS"] = CFLAGS
|
||||
os.environ["LDFLAGS"] = LDFLAGS
|
||||
|
||||
# Some dependencies need a more recent CMake version than most distros provide
|
||||
build_dependency(name="cmake-%s" % (CMAKE_VERSION,), mode="autoconf", build_tool_args=[], download_url="https://cmake.org/files/v%s" % (CMAKE_VERSION_2,), download_name="cmake-%s.tar.gz" % (CMAKE_VERSION,))
|
||||
|
||||
# Extract compiler flags from CMake to harmonize settings with other autoconf dependencies
|
||||
CMAKE_FLAG_EXTRACT_DIR="ifcopenshell_cmake_test_%s" % (time.time(),)
|
||||
# was sp.check_output([bash, "-c", "cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | head -c 32"]), in bash script, unclear what the exact required format is and whether it's needed
|
||||
if os.path.exists(CMAKE_FLAG_EXTRACT_DIR):
|
||||
shutil.rmtree(CMAKE_FLAG_EXTRACT_DIR)
|
||||
os.makedirs(CMAKE_FLAG_EXTRACT_DIR)
|
||||
BUILD_CFG_UPPER=BUILD_CFG.upper()
|
||||
for FL in ["C", "CXX"]:
|
||||
run([bash, "-c", """echo "
|
||||
message(\"\${CMAKE_%s_FLAGS_%s}\")
|
||||
" > CMakeLists.txt""" % (FL, BUILD_CFG_UPPER)], cwd=CMAKE_FLAG_EXTRACT_DIR)
|
||||
FL="%sFLAGS" % (FL,)
|
||||
FLM="%sFLAGS_MINIMAL" % (FL,)
|
||||
# @TODO: bash code unclear
|
||||
# exec("%sFLAGS=%s" % (FL, sp.check_output([os.path.join(DEPS_DIR, "install", "cmake-%s" % (CMAKE_VERSION,), "bin", "cmake"), "."
|
||||
# declare ${FL}FLAGS_MINIMAL="`$DEPS_DIR/install/cmake-$CMAKE_VERSION/bin/cmake . 2>&1 >/dev/null` ${!FLM}"
|
||||
shutil.rmtree(CMAKE_FLAG_EXTRACT_DIR)
|
||||
|
||||
if "json" in targets:
|
||||
json_url = "https://github.com/nlohmann/json/releases/download/{JSON_VERSION}/json.hpp".format(**locals())
|
||||
json_install_path = "{DEPS_DIR}/install/json/nlohmann/json.hpp".format(**locals())
|
||||
@@ -511,15 +491,7 @@ if "json" in targets:
|
||||
urlretrieve(json_url, json_install_path)
|
||||
|
||||
if "eigen" in targets:
|
||||
eigen_url = "http://bitbucket.org/eigen/eigen/get/3.3.7.tar.gz"
|
||||
eigen_build_path = "{DEPS_DIR}/build/Eigen-3.3.7.tar.gz".format(**locals())
|
||||
eigen_install_path = "{DEPS_DIR}/install/Eigen-3.3.7/Eigen".format(**locals())
|
||||
if not os.path.exists(eigen_build_path):
|
||||
urlretrieve(eigen_url, eigen_build_path)
|
||||
if not os.path.exists(eigen_install_path):
|
||||
if not os.path.exists(os.path.dirname(eigen_install_path)):
|
||||
os.makedirs(os.path.dirname(eigen_install_path))
|
||||
run([tar, "-xf", eigen_build_path, "--strip-components", "1", "-C", os.path.dirname(eigen_install_path), "eigen-eigen-323c052e1731/Eigen"])
|
||||
git_clone_or_pull_repository("https://gitlab.com/libeigen/eigen.git", "{DEPS_DIR}/install/eigen-3.3.7".format(**locals()), revision="3.3.7")
|
||||
|
||||
if "pcre" in targets:
|
||||
build_dependency(
|
||||
@@ -662,11 +634,12 @@ if "boost" in targets:
|
||||
"--with-thread",
|
||||
"--with-date_time",
|
||||
"--with-iostreams",
|
||||
"link={LINK_TYPE}".format(**locals())
|
||||
"link={LINK_TYPE}".format(**locals()),
|
||||
] + \
|
||||
BOOST_ADDRESS_MODEL + \
|
||||
list(map(str_concat("cxxflags"), CXXFLAGS.strip().split(' '))) + \
|
||||
list(map(str_concat("linkflags"), LDFLAGS.strip().split(' '))) + \
|
||||
(["toolset=clang"] if "clang" in os.environ.get('CXX', '') else []) +\
|
||||
["stage", "-s", "NO_BZIP2=1"],
|
||||
download_url="http://downloads.sourceforge.net/project/boost/boost/{BOOST_VERSION}/".format(**locals()),
|
||||
download_name="boost_{BOOST_VERSION_UNDERSCORE}.tar.bz2".format(**locals())
|
||||
@@ -680,7 +653,26 @@ if "cgal" in targets:
|
||||
if BUILD_CFG != "Debug":
|
||||
# CGAL only supports Debug and Release for CMAKE_BUILD_TYPE
|
||||
BUILD_CFG = "Release"
|
||||
build_dependency(name="cgal-{CGAL_VERSION}".format(**locals()), mode="cmake", build_tool_args=["-DGMP_LIBRARIES=%s/install/gmp-%s/lib/libgmp.a" % (DEPS_DIR, GMP_VERSION), "-DGMP_INCLUDE_DIR=%s/install/gmp-%s/include" % (DEPS_DIR, GMP_VERSION), "-DMPFR_LIBRARIES=%s/install/mpfr-%s/lib/libmpfr.a" % (DEPS_DIR, MPFR_VERSION), "-DMPFR_INCLUDE_DIR=%s/install/mpfr-%s/include" % (DEPS_DIR, MPFR_VERSION), "-DBoost_INCLUDE_DIR=%s/install/boost-%s" % (DEPS_DIR, BOOST_VERSION), "-DCMAKE_INSTALL_PREFIX=%s/install/cgal-%s/" % (DEPS_DIR, CGAL_VERSION), "-DBUILD_SHARED_LIBS=Off"], download_url="https://github.com/CGAL/cgal.git", download_name="cgal", download_tool=download_tool_git, revision="releases/CGAL-{CGAL_VERSION}".format(**locals()))
|
||||
build_dependency(
|
||||
name="cgal-{CGAL_VERSION}".format(**locals()),
|
||||
mode="cmake",
|
||||
# applied upstream
|
||||
# patch="./patches/cgal/cgal_4768.patch",
|
||||
build_tool_args=[
|
||||
"-DGMP_LIBRARIES=%s/install/gmp-%s/lib/libgmp.a" % (DEPS_DIR, GMP_VERSION),
|
||||
"-DGMP_INCLUDE_DIR=%s/install/gmp-%s/include" % (DEPS_DIR, GMP_VERSION),
|
||||
"-DMPFR_LIBRARIES=%s/install/mpfr-%s/lib/libmpfr.a" % (DEPS_DIR, MPFR_VERSION),
|
||||
"-DMPFR_INCLUDE_DIR=%s/install/mpfr-%s/include" % (DEPS_DIR, MPFR_VERSION),
|
||||
"-DBoost_INCLUDE_DIR=%s/install/boost-%s" % (DEPS_DIR, BOOST_VERSION),
|
||||
"-DCMAKE_INSTALL_PREFIX=%s/install/cgal-%s/" % (DEPS_DIR, CGAL_VERSION),
|
||||
# for now as the IfOpSh CMakeL expects libraries, we can probably safely turn this off though
|
||||
"-DCGAL_HEADER_ONLY=OFF",
|
||||
"-DBUILD_SHARED_LIBS=Off"],
|
||||
download_url="https://github.com/CGAL/cgal.git",
|
||||
download_name="cgal",
|
||||
download_tool=download_tool_git,
|
||||
revision="v{CGAL_VERSION}".format(**locals())
|
||||
)
|
||||
BUILD_CFG = OLD_BUILD_CFG
|
||||
|
||||
if "voxel" in targets:
|
||||
@@ -727,7 +719,7 @@ cmake_args=[
|
||||
"-DBOOST_ROOT=" "{DEPS_DIR}/install/boost-{BOOST_VERSION}".format(**locals()),
|
||||
"-DGLTF_SUPPORT=" "ON",
|
||||
"-DJSON_INCLUDE_DIR=" "{DEPS_DIR}/install/json".format(**locals()),
|
||||
"-DEIGEN_DIR=" "{DEPS_DIR}/install/Eigen-3.3.7".format(**locals())
|
||||
"-DEIGEN_DIR=" "{DEPS_DIR}/install/eigen-3.3.7".format(**locals())
|
||||
]
|
||||
|
||||
if "occ" in targets:
|
||||
@@ -809,7 +801,7 @@ if "IfcOpenShell-Python" in targets:
|
||||
"-DPYTHON_EXECUTABLE=" +PYTHON_EXECUTABLE,
|
||||
"-DPYTHON_INCLUDE_DIR=" +PYTHON_INCLUDE,
|
||||
"-DUSE_CGAL=ON",
|
||||
"-DEIGEN_DIR=" "{DEPS_DIR}/install/Eigen-3.3.7".format(**locals()),
|
||||
"-DEIGEN_DIR=" "{DEPS_DIR}/install/eigen-3.3.7".format(**locals()),
|
||||
"-DSWIG_EXECUTABLE=" "{DEPS_DIR}/install/swig/bin/swig".format(**locals()),
|
||||
"-DCMAKE_INSTALL_PREFIX=" "{DEPS_DIR}/install/ifcopenshell/tmp".format(**locals()),
|
||||
"-DLIBXML2_INCLUDE_DIR=" "{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2".format(**locals()),
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
From a86712d1aae9a1a6d2004db3fc8fcdac6ef6b782 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Krijnen <t.krijnen@gmail.com>
|
||||
Date: Sun, 7 Jun 2020 15:25:18 +0200
|
||||
Subject: [PATCH] Don't segfault on empty Nef_nary_x
|
||||
|
||||
---
|
||||
Nef_3/include/CGAL/Nef_nary_intersection_3.h | 3 ++-
|
||||
Nef_3/include/CGAL/Nef_nary_union_3.h | 3 ++-
|
||||
2 files changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/Nef_3/include/CGAL/Nef_nary_intersection_3.h b/Nef_3/include/CGAL/Nef_nary_intersection_3.h
|
||||
index 70fc5791fae..5178705dc2f 100644
|
||||
--- a/Nef_3/include/CGAL/Nef_nary_intersection_3.h
|
||||
+++ b/Nef_3/include/CGAL/Nef_nary_intersection_3.h
|
||||
@@ -52,7 +52,8 @@ class Nef_nary_intersection_3 {
|
||||
}
|
||||
|
||||
Polyhedron get_intersection() {
|
||||
-
|
||||
+ if (queue.empty())
|
||||
+ return empty;
|
||||
while(queue.size() > 1)
|
||||
intersect();
|
||||
inserted = 0;
|
||||
diff --git a/Nef_3/include/CGAL/Nef_nary_union_3.h b/Nef_3/include/CGAL/Nef_nary_union_3.h
|
||||
index ecbbae2db59..c8663bb911b 100644
|
||||
--- a/Nef_3/include/CGAL/Nef_nary_union_3.h
|
||||
+++ b/Nef_3/include/CGAL/Nef_nary_union_3.h
|
||||
@@ -52,7 +52,8 @@ class Nef_nary_union_3 {
|
||||
}
|
||||
|
||||
Polyhedron get_union() {
|
||||
-
|
||||
+ if (queue.empty())
|
||||
+ return empty;
|
||||
while(queue.size() > 1)
|
||||
unite();
|
||||
inserted = 0;
|
||||
@@ -142,10 +142,13 @@ bool file_exists(const std::string& filename) {
|
||||
|
||||
static std::basic_stringstream<path_t::value_type> log_stream;
|
||||
void write_log(bool);
|
||||
|
||||
/*
|
||||
void fix_quantities(IfcParse::IfcFile&, bool, bool, bool);
|
||||
void fix_spaceboundaries(IfcParse::IfcFile&, bool, bool, bool);
|
||||
void fix_storeycontainment(IfcParse::IfcFile&, bool, bool, bool);
|
||||
void fix_wallconnectivity(IfcParse::IfcFile&, bool, bool, bool);
|
||||
*/
|
||||
|
||||
std::string format_duration(time_t start, time_t end);
|
||||
|
||||
@@ -583,6 +586,7 @@ int main(int argc, char** argv) {
|
||||
time(&start);
|
||||
std::ofstream fs(output_filename.c_str());
|
||||
if (fs.is_open()) {
|
||||
/*
|
||||
if (vmap.count("calculate-quantities")) {
|
||||
fix_quantities(*ifc_file, no_progress, quiet, stderr_progress);
|
||||
}
|
||||
@@ -595,6 +599,7 @@ int main(int argc, char** argv) {
|
||||
if (vmap.count("fix-wall-connectivity")) {
|
||||
fix_wallconnectivity(*ifc_file, no_progress, quiet, stderr_progress);
|
||||
}
|
||||
*/
|
||||
fs << *ifc_file;
|
||||
exit_code = EXIT_SUCCESS;
|
||||
} else {
|
||||
|
||||
@@ -72,7 +72,7 @@ void fix_spaceboundaries(IfcParse::IfcFile& f, bool no_progress, bool quiet, boo
|
||||
for (auto& e : wire->children) {
|
||||
auto edge = (taxonomy::edge*) e;
|
||||
auto p3 = boost::get<taxonomy::point3>(edge->start);
|
||||
auto p4 = *((taxonomy::geom_item*)item)->matrix.components * p3.components->homogeneous();
|
||||
auto p4 = ((taxonomy::geom_item*)item)->matrix.ccomponents() * p3.ccomponents().homogeneous();
|
||||
Kernel_::Point_3 P(p4(0), p4(1), p4(2));
|
||||
elem_to_space_boundary_coords[{g1, g2}].emplace_back(P);
|
||||
}
|
||||
|
||||
@@ -132,8 +132,8 @@ void fix_storeycontainment(IfcParse::IfcFile& f, bool no_progress, bool quiet, b
|
||||
|
||||
for (auto& g : geom_object->geometry()) {
|
||||
auto s = ((ifcopenshell::geometry::CgalShape*) g.Shape())->shape();
|
||||
const auto& m = *g.Placement().components;
|
||||
const auto& n = *geom_object->transformation().data().components;
|
||||
const auto& m = g.Placement().ccomponents();
|
||||
const auto& n = geom_object->transformation().data().ccomponents();
|
||||
|
||||
const cgal_placement_t trsf(
|
||||
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
|
||||
|
||||
@@ -123,8 +123,8 @@ void fix_wallconnectivity(IfcParse::IfcFile& f, bool no_progress, bool quiet, bo
|
||||
auto p0 = boost::get<taxonomy::point3>(first_vertex);
|
||||
auto p1 = boost::get<taxonomy::point3>(last_vertex);
|
||||
|
||||
auto v0 = *((taxonomy::geom_item*)item)->matrix.components * p0.components->homogeneous();
|
||||
auto v1 = *((taxonomy::geom_item*)item)->matrix.components * p1.components->homogeneous();
|
||||
auto v0 = ((taxonomy::geom_item*)item)->matrix.ccomponents() * p0.ccomponents().homogeneous();
|
||||
auto v1 = ((taxonomy::geom_item*)item)->matrix.ccomponents() * p1.ccomponents().homogeneous();
|
||||
|
||||
auto P0 = Kernel_::Point_3(v0(0), v0(1), v0(2));
|
||||
auto P1 = Kernel_::Point_3(v1(0), v1(1), v1(2));
|
||||
|
||||
@@ -475,8 +475,8 @@ struct intersection_validator {
|
||||
|
||||
for (auto& g : geom_object->geometry()) {
|
||||
auto s = ((ifcopenshell::geometry::CgalShape*) g.Shape())->shape();
|
||||
const auto& m = *g.Placement().components;
|
||||
const auto& n = *geom_object->transformation().data().components;
|
||||
const auto& m = g.Placement().ccomponents();
|
||||
const auto& n = geom_object->transformation().data().ccomponents();
|
||||
|
||||
const cgal_placement_t trsf(
|
||||
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "ConversionSettings.h"
|
||||
|
||||
void ifcopenshell::geometry::ConversionSettings::setValue(GeomValue var, double value) {
|
||||
values_[var] = value;
|
||||
}
|
||||
|
||||
double ifcopenshell::geometry::ConversionSettings::getValue(GeomValue var) const {
|
||||
return values_[var];
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef CONVERSIONSETTINGS_H
|
||||
#define CONVERSIONSETTINGS_H
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace ifcopenshell { namespace geometry {
|
||||
|
||||
class NativeElement;
|
||||
|
||||
class ConversionSettings {
|
||||
public:
|
||||
// Tolerances and settings for various geometrical operations:
|
||||
enum GeomValue {
|
||||
// Specifies the deflection of the mesher
|
||||
// Default: 0.001m / 1mm
|
||||
GV_DEFLECTION_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
|
||||
// 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
|
||||
GV_LENGTH_UNIT,
|
||||
// The plane angle unit used for the creation of TopoDS_Shapes, primarily affects
|
||||
// the interpretation of IfcParamaterValues of IfcTrimmedCurves
|
||||
// Default: -1.0 (= not set, fist try degrees, then radians)
|
||||
GV_PLANEANGLE_UNIT,
|
||||
// The precision used in boolean operations, setting this value too low results
|
||||
// in artefacts and potentially modelling failures
|
||||
// Default: 0.00001 (obtained from IfcGeometricRepresentationContext if available)
|
||||
GV_PRECISION,
|
||||
// Whether to process shapes of type Face or higher (1) Wire or lower (-1) or all (0)
|
||||
GV_DIMENSIONALITY
|
||||
};
|
||||
|
||||
void setValue(GeomValue var, double value);
|
||||
|
||||
double getValue(GeomValue var) const;
|
||||
|
||||
private:
|
||||
std::array<double, 8> values_ = {
|
||||
/* deflection_tolerance = */ 0.001,
|
||||
/* wire_creation_tolerance = */ 0.0001,
|
||||
/* point_equality_tolerance = */ 0.00001,
|
||||
/* max_faces_to_sew = */ -1.0,
|
||||
/* ifc_length_unit = */ 1.0,
|
||||
/* ifc_planeangle_unit = */ -1.0,
|
||||
/* modelling_precision = */ 0.00001,
|
||||
/* dimensionality = */ 1.,
|
||||
};
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
#endif
|
||||
@@ -9,14 +9,14 @@ ifcopenshell::geometry::impl::MappingFactoryImplementation& ifcopenshell::geomet
|
||||
|
||||
extern void init_MappingImplementation_Ifc2x3(ifcopenshell::geometry::impl::MappingFactoryImplementation*);
|
||||
extern void init_MappingImplementation_Ifc4(ifcopenshell::geometry::impl::MappingFactoryImplementation*);
|
||||
extern void init_MappingImplementation_Ifc4x1(ifcopenshell::geometry::impl::MappingFactoryImplementation*);
|
||||
extern void init_MappingImplementation_Ifc4x2(ifcopenshell::geometry::impl::MappingFactoryImplementation*);
|
||||
// extern void init_MappingImplementation_Ifc4x1(ifcopenshell::geometry::impl::MappingFactoryImplementation*);
|
||||
// extern void init_MappingImplementation_Ifc4x2(ifcopenshell::geometry::impl::MappingFactoryImplementation*);
|
||||
|
||||
ifcopenshell::geometry::impl::MappingFactoryImplementation::MappingFactoryImplementation() {
|
||||
init_MappingImplementation_Ifc2x3(this);
|
||||
init_MappingImplementation_Ifc4(this);
|
||||
init_MappingImplementation_Ifc4x1(this);
|
||||
init_MappingImplementation_Ifc4x2(this);
|
||||
// init_MappingImplementation_Ifc4x1(this);
|
||||
// init_MappingImplementation_Ifc4x2(this);
|
||||
}
|
||||
|
||||
void ifcopenshell::geometry::impl::MappingFactoryImplementation::bind(const std::string& schema_name, ifcopenshell::geometry::impl::mapping_fn fn) {
|
||||
@@ -33,3 +33,24 @@ ifcopenshell::geometry::abstract_mapping* ifcopenshell::geometry::impl::MappingF
|
||||
}
|
||||
return it->second(file, s);
|
||||
}
|
||||
|
||||
void ifcopenshell::geometry::remove_duplicate_points_from_loop(std::vector<taxonomy::point3>& polygon, bool closed, double tol) {
|
||||
for (;;) {
|
||||
bool removed = false;
|
||||
int n = polygon.size() - (closed ? 0 : 1);
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
// wrap around to the first point in case of a closed loop
|
||||
int j = (i % polygon.size()) + 1;
|
||||
double dist = (polygon.at(i - 1).components() - polygon.at(j - 1).components()).squaredNorm();
|
||||
if (dist < tol) {
|
||||
// do not remove the first or last point to
|
||||
// maintain connectivity with other wires
|
||||
if ((closed && j == 1) || (!closed && j == n)) polygon.erase(polygon.begin() + i - 1);
|
||||
else polygon.erase(polygon.begin() + j - 1);
|
||||
removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!removed) break;
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,8 @@ namespace geometry {
|
||||
|
||||
MappingFactoryImplementation& mapping_implementations();
|
||||
}
|
||||
|
||||
|
||||
void remove_duplicate_points_from_loop(std::vector<taxonomy::point3>& polygon, bool closed, double tol);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
#include "schema_agnostic/ifc_geom_api.h"
|
||||
|
||||
namespace ifcopenshell {
|
||||
namespace geometry {
|
||||
|
||||
class IFC_GEOM_API geometry_exception : public std::exception {
|
||||
protected:
|
||||
std::string message;
|
||||
public:
|
||||
geometry_exception(const std::string& m)
|
||||
: message(m) {}
|
||||
virtual ~geometry_exception() throw () {}
|
||||
virtual const char* what() const throw() {
|
||||
return message.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class IFC_GEOM_API too_many_faces_exception : public geometry_exception {
|
||||
public:
|
||||
too_many_faces_exception()
|
||||
: geometry_exception("Too many faces for operation") {}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,9 @@ bool ifcopenshell::geometry::kernels::AbstractKernel::convert(const taxonomy::it
|
||||
ifcopenshell::geometry::kernels::AbstractKernel* ifcopenshell::geometry::kernels::construct(const std::string& geometry_library, IfcParse::IfcFile* file) {
|
||||
const std::string geometry_library_lower = boost::to_lower_copy(geometry_library);
|
||||
if (geometry_library_lower == "opencascade") {
|
||||
return new OpenCascadeKernel;
|
||||
return new OpenCascadeKernel(settings);
|
||||
} else if (geometry_library_lower == "cgal") {
|
||||
return new CgalKernel;
|
||||
return new CgalKernel(settings);
|
||||
} else {
|
||||
throw IfcParse::IfcException("No geometry kernel registered for " + geometry_library);
|
||||
}
|
||||
@@ -34,79 +34,9 @@ bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonom
|
||||
}
|
||||
for (auto i = s; i < r.size(); ++i) {
|
||||
r[i].prepend(collection->matrix);
|
||||
if (!r[i].hasStyle()) {
|
||||
r[i].setStyle(collection->surface_style);
|
||||
}
|
||||
}
|
||||
return r.size() > s;
|
||||
}
|
||||
|
||||
//void ifcopenshell::geometry::kernels::AbstractKernel::set_conversion_placement_rel_to(const IfcParse::declaration* type) {
|
||||
// placement_rel_to = type;
|
||||
//}
|
||||
//
|
||||
//void ifcopenshell::geometry::kernels::AbstractKernel::setValue(GeomValue var, double value) {
|
||||
// switch (var) {
|
||||
// case GV_DEFLECTION_TOLERANCE:
|
||||
// deflection_tolerance = value;
|
||||
// break;
|
||||
// case GV_POINT_EQUALITY_TOLERANCE:
|
||||
// point_equality_tolerance = value;
|
||||
// break;
|
||||
// case GV_LENGTH_UNIT:
|
||||
// ifc_length_unit = value;
|
||||
// break;
|
||||
// case GV_PLANEANGLE_UNIT:
|
||||
// ifc_planeangle_unit = value;
|
||||
// break;
|
||||
// case GV_PRECISION:
|
||||
// modelling_precision = value;
|
||||
// break;
|
||||
// case GV_DIMENSIONALITY:
|
||||
// dimensionality = value;
|
||||
// break;
|
||||
// default:
|
||||
// assert(!"never reach here");
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//double ifcopenshell::geometry::kernels::AbstractKernel::getValue(GeomValue var) const {
|
||||
// switch (var) {
|
||||
// case GV_DEFLECTION_TOLERANCE:
|
||||
// return deflection_tolerance;
|
||||
// case GV_MINIMAL_FACE_AREA:
|
||||
// // Considering a right-angled triangle, this about the smallest
|
||||
// // area you can obtain without the vertices being confused.
|
||||
// return modelling_precision * modelling_precision / 2.;
|
||||
// case GV_POINT_EQUALITY_TOLERANCE:
|
||||
// return point_equality_tolerance;
|
||||
// case GV_LENGTH_UNIT:
|
||||
// return ifc_length_unit;
|
||||
// break;
|
||||
// case GV_PLANEANGLE_UNIT:
|
||||
// return ifc_planeangle_unit;
|
||||
// break;
|
||||
// case GV_PRECISION:
|
||||
// return modelling_precision;
|
||||
// break;
|
||||
// case GV_DIMENSIONALITY:
|
||||
// return dimensionality;
|
||||
// break;
|
||||
// }
|
||||
// assert(!"never reach here");
|
||||
// return 0;
|
||||
//}
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement<float, float>* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_representation_and_product<float, float>(
|
||||
// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product);
|
||||
//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement<float, double>* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_representation_and_product<float, double>(
|
||||
// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product);
|
||||
//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement<double, double>* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_representation_and_product<double, double>(
|
||||
// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product);
|
||||
//
|
||||
//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement<float, float>* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_processed_representation<float, float>(
|
||||
// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, ifcopenshell::geometry::kernels::NativeElement<float, float>* brep);
|
||||
//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement<float, double>* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_processed_representation<float, double>(
|
||||
// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, ifcopenshell::geometry::kernels::NativeElement<float, double>* brep);
|
||||
//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement<double, double>* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_processed_representation<double, double>(
|
||||
// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, ifcopenshell::geometry::kernels::NativeElement<double, double>* brep);
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../../ifcgeom/schema_agnostic/ifc_geom_api.h"
|
||||
#include "../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h"
|
||||
#include "../../ifcgeom/taxonomy.h"
|
||||
#include "../../ifcgeom/ConversionSettings.h"
|
||||
|
||||
static const double ALMOST_ZERO = 1.e-9;
|
||||
|
||||
@@ -18,31 +19,15 @@ namespace ifcopenshell { namespace geometry { namespace kernels {
|
||||
class IFC_GEOM_API AbstractKernel {
|
||||
protected:
|
||||
// For stopping PlacementRelTo recursion in convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& trsf)
|
||||
const IfcParse::declaration* placement_rel_to;
|
||||
|
||||
double deflection_tolerance;
|
||||
double wire_creation_tolerance;
|
||||
double point_equality_tolerance;
|
||||
double max_faces_to_sew;
|
||||
double ifc_length_unit;
|
||||
double ifc_planeangle_unit;
|
||||
double modelling_precision;
|
||||
double dimensionality;
|
||||
|
||||
std::string geometry_library;
|
||||
const IfcParse::declaration* placement_rel_to = nullptr;
|
||||
std::string geometry_library_;
|
||||
ConversionSettings settings_;
|
||||
|
||||
public:
|
||||
AbstractKernel(const std::string& geometry_library)
|
||||
: geometry_library(geometry_library)
|
||||
, deflection_tolerance(0.001)
|
||||
, wire_creation_tolerance(0.0001)
|
||||
, point_equality_tolerance(0.00001)
|
||||
, max_faces_to_sew(-1.0)
|
||||
, ifc_length_unit(1.0)
|
||||
, ifc_planeangle_unit(-1.0)
|
||||
, modelling_precision(0.00001)
|
||||
, dimensionality(1.)
|
||||
, placement_rel_to(0) {}
|
||||
AbstractKernel(const std::string& geometry_library, const ConversionSettings& settings)
|
||||
: geometry_library_(geometry_library)
|
||||
, settings_(settings)
|
||||
{}
|
||||
|
||||
bool convert(const taxonomy::item*, ifcopenshell::geometry::ConversionResults&);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "CgalConversionResult.h"
|
||||
#include "CgalKernel.h"
|
||||
|
||||
#include "../../../ifcparse/IfcLogger.h"
|
||||
#include "../../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h"
|
||||
@@ -7,8 +8,8 @@ void ifcopenshell::geometry::CgalShape::Triangulate(const settings& settings, co
|
||||
// Copy is made because triangulate_faces() does not accept a const argument
|
||||
cgal_shape_t s = shape_;
|
||||
|
||||
if (!place.components->isIdentity()) {
|
||||
const auto& m = *place.components;
|
||||
if (!place.is_identity()) {
|
||||
const auto& m = place.ccomponents();
|
||||
|
||||
// @todo check
|
||||
const cgal_placement_t trsf(
|
||||
@@ -100,3 +101,25 @@ void ifcopenshell::geometry::CgalShape::Triangulate(const settings& settings, co
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/bbox.h>
|
||||
|
||||
double ifcopenshell::geometry::CgalShape::bounding_box(void *& b) const {
|
||||
if (b == nullptr) {
|
||||
b = new CGAL::Bbox_3;
|
||||
}
|
||||
auto& bb = (*((CGAL::Bbox_3*)b));
|
||||
bb += CGAL::Polygon_mesh_processing::bbox(shape_);
|
||||
return (bb.xmax() - bb.xmin()) * (bb.ymax() - bb.ymin()) * (bb.zmax() - bb.zmin());
|
||||
}
|
||||
|
||||
int ifcopenshell::geometry::CgalShape::num_vertices() const {
|
||||
return shape_.size_of_vertices();
|
||||
}
|
||||
|
||||
void ifcopenshell::geometry::CgalShape::set_box(void * b) {
|
||||
auto& bb = (*((CGAL::Bbox_3*)b));
|
||||
Kernel_::Point_3 lower(bb.xmin(), bb.ymin(), bb.zmin());
|
||||
Kernel_::Point_3 upper(bb.xmax(), bb.ymax(), bb.zmax());
|
||||
shape_ = ifcopenshell::geometry::utils::create_cube(lower, upper);
|
||||
}
|
||||
|
||||
@@ -75,6 +75,12 @@ namespace ifcopenshell { namespace geometry {
|
||||
return new CgalShape(shape_);
|
||||
}
|
||||
|
||||
virtual double bounding_box(void*& b) const;
|
||||
|
||||
virtual int num_vertices() const;
|
||||
|
||||
virtual void set_box(void* b);
|
||||
|
||||
virtual bool is_manifold() const {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
+1151
-101
File diff suppressed because it is too large
Load Diff
@@ -46,45 +46,11 @@ if ( it != cache.T.end() ) { e = it->second; return true; }
|
||||
|
||||
struct PolyhedronBuilder : public CGAL::Modifier_base<CGAL::Polyhedron_3<Kernel_>::HalfedgeDS> {
|
||||
private:
|
||||
std::list<cgal_face_t> *face_list;
|
||||
std::list<cgal_face_t> *face_list;
|
||||
public:
|
||||
PolyhedronBuilder(std::list<cgal_face_t> *face_list) {
|
||||
this->face_list = face_list;
|
||||
}
|
||||
|
||||
void operator()(CGAL::Polyhedron_3<Kernel_>::HalfedgeDS &hds) {
|
||||
std::list<Kernel_::Point_3> points;
|
||||
std::list<std::list<std::size_t>> facet_vertices;
|
||||
CGAL::Polyhedron_incremental_builder_3<CGAL::Polyhedron_3<Kernel_>::HalfedgeDS> builder(hds, true);
|
||||
|
||||
for (auto &face: *face_list) {
|
||||
facet_vertices.push_back(std::list<std::size_t>());
|
||||
for (auto &point: face.outer) {
|
||||
facet_vertices.back().push_back(points.size());
|
||||
points.push_back(point);
|
||||
}
|
||||
}
|
||||
|
||||
builder.begin_surface(points.size(), facet_vertices.size());
|
||||
|
||||
for (auto &point: points) {
|
||||
// std::cout << "Adding point " << point << std::endl;
|
||||
builder.add_vertex(point);
|
||||
}
|
||||
|
||||
for (auto &facet: facet_vertices) {
|
||||
builder.begin_facet();
|
||||
// std::cout << "Adding facet ";
|
||||
for (auto &vertex: facet) {
|
||||
// std::cout << vertex << " ";
|
||||
builder.add_vertex_to_facet(vertex);
|
||||
}
|
||||
// std::cout << std::endl;
|
||||
builder.end_facet();
|
||||
}
|
||||
|
||||
builder.end_surface();
|
||||
}
|
||||
boost::optional<cgal_shape_t> from_soup;
|
||||
PolyhedronBuilder(std::list<cgal_face_t> *face_list);
|
||||
void operator()(CGAL::Polyhedron_3<Kernel_>::HalfedgeDS &hds);
|
||||
};
|
||||
|
||||
namespace ifcopenshell {
|
||||
@@ -92,7 +58,7 @@ namespace geometry {
|
||||
namespace utils {
|
||||
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_cube(double d);
|
||||
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_cube(const Kernel_::Point_3& lower, const Kernel_::Point_3& upper);
|
||||
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_polyhedron(std::list<cgal_face_t> &face_list);
|
||||
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_polyhedron(std::list<cgal_face_t> &face_list, bool stitch_borders=false);
|
||||
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_polyhedron(const CGAL::Nef_polyhedron_3<Kernel_> &nef_polyhedron);
|
||||
IFC_GEOM_API CGAL::Nef_polyhedron_3<Kernel_> create_nef_polyhedron(std::list<cgal_face_t> &face_list);
|
||||
IFC_GEOM_API CGAL::Nef_polyhedron_3<Kernel_> create_nef_polyhedron(CGAL::Polyhedron_3<Kernel_> &polyhedron);
|
||||
@@ -104,21 +70,21 @@ namespace kernels {
|
||||
private:
|
||||
double precision_;
|
||||
size_t circle_segments_;
|
||||
CGAL::Nef_polyhedron_3<Kernel_> precision_cube_;
|
||||
// CGAL::Nef_polyhedron_3<Kernel_> precision_cube_;
|
||||
|
||||
bool preprocess_boolean_operand(const IfcUtil::IfcBaseClass* log_reference, const cgal_shape_t& shape_const, CGAL::Nef_polyhedron_3<Kernel_>& result, bool dilate);
|
||||
bool thin_solid(const CGAL::Nef_polyhedron_3<Kernel_>& a, CGAL::Nef_polyhedron_3<Kernel_>& result);
|
||||
|
||||
CGAL::Nef_polyhedron_3<Kernel_> create_precision_cube_() const {
|
||||
auto cc = utils::create_cube(precision_);
|
||||
return CGAL::Nef_polyhedron_3<Kernel_>(cc);
|
||||
}
|
||||
public:
|
||||
|
||||
CgalKernel()
|
||||
: AbstractKernel("cgal")
|
||||
// @todo
|
||||
, precision_(1.e-5)
|
||||
CgalKernel(const ConversionSettings& settings)
|
||||
: AbstractKernel("cgal", settings)
|
||||
, circle_segments_(16)
|
||||
{
|
||||
auto cc = utils::create_cube(precision_);
|
||||
precision_cube_ = CGAL::Nef_polyhedron_3<Kernel_>(cc);
|
||||
}
|
||||
{}
|
||||
|
||||
void remove_duplicate_points_from_loop(cgal_wire_t& polygon);
|
||||
|
||||
@@ -127,13 +93,16 @@ namespace kernels {
|
||||
bool convert(const taxonomy::loop*, cgal_wire_t&);
|
||||
// bool convert(const taxonomy::matrix4*, cgal_placement_t&);
|
||||
bool convert(const taxonomy::shell*, cgal_shape_t&);
|
||||
|
||||
bool process_extrusion(const cgal_face_t& bottom_face, const taxonomy::direction3& direction, double height, cgal_shape_t& shape);
|
||||
bool process_as_2d_polygon(const taxonomy::boolean_result* br, std::list<CGAL::Polygon_2<Kernel_>>& loops, double& z0, double& z1);
|
||||
bool process_as_2d_polygon(const std::list<std::list<std::pair<const IfcUtil::IfcBaseClass*, cgal_shape_t>>>& operands, std::list<CGAL::Polygon_2<Kernel_>>& loops, double& z0, double& z1);
|
||||
|
||||
// virtual bool convert_impl(const taxonomy::face*, ifcopenshell::geometry::ConversionResults&);
|
||||
virtual bool convert_impl(const taxonomy::shell*, ifcopenshell::geometry::ConversionResults&);
|
||||
virtual bool convert_impl(const taxonomy::extrusion*, ifcopenshell::geometry::ConversionResults&);
|
||||
virtual bool convert_impl(const taxonomy::boolean_result*, ifcopenshell::geometry::ConversionResults&);
|
||||
|
||||
const CGAL::Nef_polyhedron_3<Kernel_>& precision_cube() const { return precision_cube_; }
|
||||
CGAL::Nef_polyhedron_3<Kernel_> precision_cube() const { return create_precision_cube_(); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,198 +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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
/********************************************************************************
|
||||
* *
|
||||
* Implementations of the various conversion functions defined in IfcRegister.h *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <gp_Pnt.hxx>
|
||||
#include <gp_Vec.hxx>
|
||||
#include <gp_Dir.hxx>
|
||||
#include <gp_Pnt2d.hxx>
|
||||
#include <gp_Vec2d.hxx>
|
||||
#include <gp_Dir2d.hxx>
|
||||
#include <gp_Mat.hxx>
|
||||
#include <gp_Mat2d.hxx>
|
||||
#include <gp_GTrsf.hxx>
|
||||
#include <gp_GTrsf2d.hxx>
|
||||
#include <gp_Trsf.hxx>
|
||||
#include <gp_Trsf2d.hxx>
|
||||
#include <gp_Ax3.hxx>
|
||||
#include <gp_Ax2d.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
#include <gp_Circ.hxx>
|
||||
|
||||
#include <TColgp_Array1OfPnt.hxx>
|
||||
#include <TColgp_Array1OfPnt2d.hxx>
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TColStd_Array1OfInteger.hxx>
|
||||
#include <Geom_Line.hxx>
|
||||
#include <Geom_Circle.hxx>
|
||||
#include <Geom_Ellipse.hxx>
|
||||
#include <Geom_TrimmedCurve.hxx>
|
||||
|
||||
#include <BRepOffsetAPI_Sewing.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRepBuilderAPI_MakePolygon.hxx>
|
||||
#include <BRepBuilderAPI_MakeVertex.hxx>
|
||||
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
|
||||
#include <BRepPrimAPI_MakePrism.hxx>
|
||||
#include <BRepBuilderAPI_MakeShell.hxx>
|
||||
#include <BRepBuilderAPI_MakeSolid.hxx>
|
||||
#include <BRepPrimAPI_MakeHalfSpace.hxx>
|
||||
#include <BRepAlgoAPI_Cut.hxx>
|
||||
|
||||
#include <ShapeFix_Shape.hxx>
|
||||
#include <ShapeFix_ShapeTolerance.hxx>
|
||||
#include <ShapeFix_Solid.hxx>
|
||||
|
||||
#include <BRepFilletAPI_MakeFillet2d.hxx>
|
||||
|
||||
#include <TopLoc_Location.hxx>
|
||||
|
||||
#include "../../../ifcgeom/kernels/opencascade/IfcGeom.h"
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcBSplineCurveWithKnots
|
||||
#include <Geom_BSplineCurve.hxx>
|
||||
#endif
|
||||
|
||||
#define Kernel POSTFIX_SCHEMA(Kernel)
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircle* l, Handle(Geom_Curve)& curve) {
|
||||
const double r = l->Radius() * getValue(GV_LENGTH_UNIT);
|
||||
if ( r < ALMOST_ZERO ) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Radius not greater than zero for:", l);
|
||||
return false;
|
||||
}
|
||||
gp_Trsf trsf;
|
||||
IfcSchema::IfcAxis2Placement* placement = l->Position();
|
||||
if (placement->declaration().is(IfcSchema::IfcAxis2Placement3D::Class())) {
|
||||
IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf);
|
||||
} else {
|
||||
gp_Trsf2d trsf2d;
|
||||
IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf2d);
|
||||
trsf = trsf2d;
|
||||
}
|
||||
gp_Ax2 ax = gp_Ax2().Transformed(trsf);
|
||||
curve = new Geom_Circle(ax, r);
|
||||
return true;
|
||||
}
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcEllipse* l, Handle(Geom_Curve)& curve) {
|
||||
double x = l->SemiAxis1() * getValue(GV_LENGTH_UNIT);
|
||||
double y = l->SemiAxis2() * getValue(GV_LENGTH_UNIT);
|
||||
if (x < ALMOST_ZERO || y < ALMOST_ZERO) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Radius not greater than zero for:", l);
|
||||
return false;
|
||||
}
|
||||
// Open Cascade does not allow ellipses of which the minor radius
|
||||
// is greater than the major radius. Hence, in this case, the
|
||||
// ellipse is rotated. Note that special care needs to be taken
|
||||
// when creating a trimmed curve off of an ellipse like this.
|
||||
const bool rotated = y > x;
|
||||
gp_Trsf trsf;
|
||||
IfcSchema::IfcAxis2Placement* placement = l->Position();
|
||||
if (placement->declaration().is(IfcSchema::IfcAxis2Placement3D::Class())) {
|
||||
convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf);
|
||||
} else {
|
||||
gp_Trsf2d trsf2d;
|
||||
convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf2d);
|
||||
trsf = trsf2d;
|
||||
}
|
||||
gp_Ax2 ax = gp_Ax2();
|
||||
if (rotated) {
|
||||
ax.Rotate(ax.Axis(), M_PI / 2.);
|
||||
std::swap(x, y);
|
||||
}
|
||||
ax.Transform(trsf);
|
||||
curve = new Geom_Ellipse(ax, x, y);
|
||||
return true;
|
||||
}
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcLine* l, Handle(Geom_Curve)& curve) {
|
||||
gp_Pnt pnt;gp_Vec vec;
|
||||
convert(l->Pnt(),pnt);
|
||||
convert(l->Dir(),vec);
|
||||
// See note at IfcGeomWires.cpp:237
|
||||
curve = new Geom_Line(pnt,vec);
|
||||
return true;
|
||||
}
|
||||
|
||||
#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());
|
||||
|
||||
const IfcSchema::IfcCartesianPoint::list::ptr cps = l->ControlPointsList();
|
||||
const std::vector<int> mults = l->KnotMultiplicities();
|
||||
const std::vector<double> knots = l->Knots();
|
||||
|
||||
TColgp_Array1OfPnt Poles(0, cps->size() - 1);
|
||||
TColStd_Array1OfReal Weights(0, cps->size() - 1);
|
||||
TColStd_Array1OfReal Knots(0, (int)knots.size() - 1);
|
||||
TColStd_Array1OfInteger Mults(0, (int)mults.size() - 1);
|
||||
Standard_Integer Degree = l->Degree();
|
||||
Standard_Boolean Periodic = l->ClosedCurve();
|
||||
|
||||
int i;
|
||||
|
||||
if (is_rational) {
|
||||
IfcSchema::IfcRationalBSplineCurveWithKnots* rl = (IfcSchema::IfcRationalBSplineCurveWithKnots*)l;
|
||||
std::vector<double> weights = rl->WeightsData();
|
||||
|
||||
i = 0;
|
||||
for (std::vector<double>::const_iterator it = weights.begin(); it != weights.end(); ++it, ++i) {
|
||||
Weights(i) = *it;
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (IfcSchema::IfcCartesianPoint::list::it it = cps->begin(); it != cps->end(); ++it, ++i) {
|
||||
gp_Pnt pnt;
|
||||
if (!convert(*it, pnt)) return false;
|
||||
Poles(i) = pnt;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (std::vector<int>::const_iterator it = mults.begin(); it != mults.end(); ++it, ++i) {
|
||||
Mults(i) = *it;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (std::vector<double>::const_iterator it = knots.begin(); it != knots.end(); ++it, ++i) {
|
||||
Knots(i) = *it;
|
||||
}
|
||||
|
||||
if (is_rational) {
|
||||
curve = new Geom_BSplineCurve(Poles, Weights, Knots, Mults, Degree, Periodic);
|
||||
} else {
|
||||
curve = new Geom_BSplineCurve(Poles, Knots, Mults, Degree, Periodic);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,419 +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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
/********************************************************************************
|
||||
* *
|
||||
* Implementations of the various conversion functions defined in IfcRegister.h *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <gp_Pnt.hxx>
|
||||
#include <gp_Vec.hxx>
|
||||
#include <gp_Dir.hxx>
|
||||
#include <gp_Pnt2d.hxx>
|
||||
#include <gp_Vec2d.hxx>
|
||||
#include <gp_Dir2d.hxx>
|
||||
#include <gp_Mat.hxx>
|
||||
#include <gp_Mat2d.hxx>
|
||||
#include <gp_GTrsf.hxx>
|
||||
#include <gp_GTrsf2d.hxx>
|
||||
#include <gp_Trsf.hxx>
|
||||
#include <gp_Trsf2d.hxx>
|
||||
#include <gp_Ax3.hxx>
|
||||
#include <gp_Ax2d.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
#include <gp_Circ.hxx>
|
||||
|
||||
#include <TColgp_Array1OfPnt.hxx>
|
||||
#include <TColgp_Array1OfPnt2d.hxx>
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TColStd_Array1OfInteger.hxx>
|
||||
#include <Geom_Line.hxx>
|
||||
#include <Geom_Circle.hxx>
|
||||
#include <Geom_Ellipse.hxx>
|
||||
#include <Geom_TrimmedCurve.hxx>
|
||||
|
||||
#include <BRepOffsetAPI_Sewing.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRepBuilderAPI_MakePolygon.hxx>
|
||||
#include <BRepBuilderAPI_MakeVertex.hxx>
|
||||
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
|
||||
#include <BRepPrimAPI_MakePrism.hxx>
|
||||
#include <BRepBuilderAPI_MakeShell.hxx>
|
||||
#include <BRepBuilderAPI_MakeSolid.hxx>
|
||||
#include <BRepPrimAPI_MakeHalfSpace.hxx>
|
||||
#include <BRepAlgoAPI_Cut.hxx>
|
||||
|
||||
#include <ShapeFix_Shape.hxx>
|
||||
#include <ShapeFix_ShapeTolerance.hxx>
|
||||
#include <ShapeFix_Solid.hxx>
|
||||
|
||||
#include <BRepFilletAPI_MakeFillet2d.hxx>
|
||||
|
||||
#include <TopLoc_Location.hxx>
|
||||
|
||||
#include "../../../ifcgeom/kernels/opencascade/IfcGeom.h"
|
||||
|
||||
#define Kernel POSTFIX_SCHEMA(Kernel)
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper functions (re)set gp_(G)Trsf(2d) forms explicitly to 'Identity'
|
||||
// so that it can be easily identified in the IfcMappedItem processing
|
||||
|
||||
// For axis placements detect equality early in order for the
|
||||
// relatively computionaly expensive gp_Trsf calculation to be skipped
|
||||
template <typename T>
|
||||
bool axis_equal(const T& a, const T& b, double tolerance);
|
||||
template <>
|
||||
bool axis_equal(const gp_Ax3& a, const gp_Ax3& b, double tolerance) {
|
||||
if (!a.Location().IsEqual(b.Location(), tolerance)) return false;
|
||||
// Note that the tolerance below is angular, above is linear. Since architectural
|
||||
// objects are about 1m'ish in scale, it should be somewhat equivalent. Besides,
|
||||
// this is mostly a filter for NULL or default values in the placements.
|
||||
if (!a.Direction().IsEqual(b.Direction(), tolerance)) return false;
|
||||
if (!a.XDirection().IsEqual(b.XDirection(), tolerance)) return false;
|
||||
if (!a.YDirection().IsEqual(b.YDirection(), tolerance)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool axis_equal(const gp_Ax2d& a, const gp_Ax2d& b, double tolerance) {
|
||||
if (!a.Location().IsEqual(b.Location(), tolerance)) return false;
|
||||
if (!a.Direction().IsEqual(b.Direction(), tolerance)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> struct dimension_count {};
|
||||
template <> struct dimension_count <gp_Trsf2d > { static const int n = 2; };
|
||||
template <> struct dimension_count <gp_GTrsf2d> { static const int n = 2; };
|
||||
template <> struct dimension_count < gp_Trsf > { static const int n = 3; };
|
||||
template <> struct dimension_count < gp_GTrsf > { static const int n = 3; };
|
||||
|
||||
template <typename T>
|
||||
bool is_identity(const T& t, double tolerance) {
|
||||
// Note the {1, n+1} range due to Open Cascade's 1-based indexing
|
||||
// Note the {1, n+2} range due to the translation part of the matrix
|
||||
for (int i = 1; i < dimension_count<T>::n + 2; ++i) {
|
||||
for (int j = 1; j < dimension_count<T>::n + 1; ++j) {
|
||||
const double iden_value = i == j ? 1. : 0.;
|
||||
const double trsf_value = t.Value(j, i);
|
||||
if (fabs(trsf_value - iden_value) > tolerance) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianPoint* l, gp_Pnt& point) {
|
||||
IN_CACHE(IfcCartesianPoint,l,gp_Pnt,point)
|
||||
std::vector<double> xyz = l->Coordinates();
|
||||
point = gp_Pnt(
|
||||
xyz.size() ? (xyz[0]*getValue(GV_LENGTH_UNIT)) : 0.0f,
|
||||
xyz.size() > 1 ? (xyz[1]*getValue(GV_LENGTH_UNIT)) : 0.0f,
|
||||
xyz.size() > 2 ? (xyz[2]*getValue(GV_LENGTH_UNIT)) : 0.0f
|
||||
);
|
||||
CACHE(IfcCartesianPoint,l,point)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcDirection* l, gp_Dir& dir) {
|
||||
IN_CACHE(IfcDirection,l,gp_Dir,dir)
|
||||
std::vector<double> xyz = l->DirectionRatios();
|
||||
dir = gp_Dir(
|
||||
xyz.size() ? xyz[0] : 0.0f,
|
||||
xyz.size() > 1 ? xyz[1] : 0.0f,
|
||||
xyz.size() > 2 ? xyz[2] : 0.0f
|
||||
);
|
||||
CACHE(IfcDirection,l,dir)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcVector* l, gp_Vec& v) {
|
||||
IN_CACHE(IfcVector,l,gp_Vec,v)
|
||||
gp_Dir d;
|
||||
IfcGeom::Kernel::convert(l->Orientation(),d);
|
||||
v = l->Magnitude() * getValue(GV_LENGTH_UNIT) * d;
|
||||
CACHE(IfcVector,l,v)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcAxis2Placement3D* l, gp_Trsf& trsf) {
|
||||
IN_CACHE(IfcAxis2Placement3D, l, gp_Trsf, trsf)
|
||||
|
||||
gp_Pnt o;
|
||||
gp_Dir axis(0, 0, 1);
|
||||
gp_Dir refDirection;
|
||||
|
||||
IfcGeom::Kernel::convert(l->Location(), o);
|
||||
const bool hasAxis = l->hasAxis();
|
||||
const bool hasRef = l->hasRefDirection();
|
||||
|
||||
if (hasAxis != hasRef) {
|
||||
Logger::Warning("Axis and RefDirection should be specified together", l);
|
||||
}
|
||||
|
||||
if (hasAxis) {
|
||||
IfcGeom::Kernel::convert(l->Axis(), axis);
|
||||
}
|
||||
|
||||
if (hasRef) {
|
||||
IfcGeom::Kernel::convert(l->RefDirection(), refDirection);
|
||||
} else {
|
||||
if (!axis.IsParallel(gp::DX(), 1.e-5)) {
|
||||
refDirection = gp::DX();
|
||||
} else {
|
||||
refDirection = gp::DZ();
|
||||
}
|
||||
gp_Vec Xvec = axis.Dot(refDirection) * axis;
|
||||
gp_Vec Xaxis = refDirection.XYZ() - Xvec.XYZ();
|
||||
refDirection = Xaxis;
|
||||
}
|
||||
|
||||
gp_Ax3 ax3(o, axis, refDirection);
|
||||
|
||||
if (!axis_equal(ax3, (gp_Ax3) gp::XOY(), getValue(GV_PRECISION))) {
|
||||
trsf.SetTransformation(ax3, gp::XOY());
|
||||
}
|
||||
|
||||
CACHE(IfcAxis2Placement3D,l,trsf)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcAxis1Placement* l, gp_Ax1& ax) {
|
||||
IN_CACHE(IfcAxis1Placement,l,gp_Ax1,ax)
|
||||
gp_Pnt o;gp_Dir axis = gp_Dir(0,0,1);
|
||||
IfcGeom::Kernel::convert(l->Location(),o);
|
||||
if ( l->hasAxis() ) IfcGeom::Kernel::convert(l->Axis(), axis);
|
||||
ax = gp_Ax1(o, axis);
|
||||
CACHE(IfcAxis1Placement,l,ax)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperator3D* l, gp_Trsf& trsf) {
|
||||
IN_CACHE(IfcCartesianTransformationOperator3D,l,gp_Trsf,trsf)
|
||||
gp_Pnt origin;
|
||||
IfcGeom::Kernel::convert(l->LocalOrigin(),origin);
|
||||
gp_Dir axis1 (1.,0.,0.);
|
||||
gp_Dir axis2 (0.,1.,0.);
|
||||
gp_Dir axis3 (0.,0.,1.);
|
||||
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
|
||||
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
|
||||
if ( l->hasAxis3() ) IfcGeom::Kernel::convert(l->Axis3(),axis3);
|
||||
gp_Ax3 ax3 (origin,axis3,axis1);
|
||||
if ( axis2.Dot(ax3.YDirection()) < 0 ) ax3.YReverse();
|
||||
|
||||
if (!axis_equal(ax3, (gp_Ax3) gp::XOY(), getValue(GV_PRECISION))) {
|
||||
trsf.SetTransformation(ax3);
|
||||
trsf.Invert();
|
||||
}
|
||||
|
||||
if (l->hasScale() && !ALMOST_THE_SAME(l->Scale(), 1.)) {
|
||||
trsf.SetScaleFactor(l->Scale());
|
||||
}
|
||||
|
||||
CACHE(IfcCartesianTransformationOperator3D,l,trsf)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperator2D* l, gp_Trsf2d& trsf) {
|
||||
IN_CACHE(IfcCartesianTransformationOperator2D,l,gp_Trsf2d,trsf)
|
||||
|
||||
gp_Pnt origin;
|
||||
gp_Dir axis1 (1.,0.,0.);
|
||||
gp_Dir axis2 (0.,1.,0.);
|
||||
|
||||
IfcGeom::Kernel::convert(l->LocalOrigin(),origin);
|
||||
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
|
||||
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
|
||||
|
||||
const gp_Pnt2d origin2d(origin.X(), origin.Y());
|
||||
const gp_Dir2d axis12d(axis1.X(), axis1.Y());
|
||||
const gp_Dir2d axis22d(axis2.X(), axis2.Y());
|
||||
|
||||
// A better match to represent the IfcCartesianTransformationOperator2D would
|
||||
// be the gp_Ax22d, but to my knowledge no easy way exists to convert it into
|
||||
// a gp_Trsf2d. Easiest would probably be to simply update the underlying
|
||||
// gp_Mat2d directly.
|
||||
|
||||
const gp_Ax2d ax2d (origin2d, axis12d);
|
||||
trsf.SetTransformation(ax2d);
|
||||
|
||||
if ( ax2d.Direction().Rotated(M_PI / 2.).Dot(axis22d) < 0. ) {
|
||||
gp_Trsf2d mirror; mirror.SetMirror(ax2d);
|
||||
trsf.Multiply(mirror);
|
||||
}
|
||||
|
||||
trsf.Invert();
|
||||
if ( l->hasScale() && !ALMOST_THE_SAME(l->Scale(), 1.) ) trsf.SetScaleFactor(l->Scale());
|
||||
|
||||
if (is_identity(trsf, getValue(GV_PRECISION))) {
|
||||
trsf = gp_Trsf2d();
|
||||
}
|
||||
|
||||
CACHE(IfcCartesianTransformationOperator2D,l,trsf)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperator3DnonUniform* l, gp_GTrsf& gtrsf) {
|
||||
IN_CACHE(IfcCartesianTransformationOperator3DnonUniform,l,gp_GTrsf,gtrsf)
|
||||
gp_Trsf trsf;
|
||||
gp_Pnt origin;
|
||||
IfcGeom::Kernel::convert(l->LocalOrigin(),origin);
|
||||
gp_Dir axis1 (1.,0.,0.);
|
||||
gp_Dir axis2 (0.,1.,0.);
|
||||
gp_Dir axis3 (0.,0.,1.);
|
||||
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
|
||||
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
|
||||
if ( l->hasAxis3() ) IfcGeom::Kernel::convert(l->Axis3(),axis3);
|
||||
gp_Ax3 ax3 (origin,axis3,axis1);
|
||||
if ( axis2.Dot(ax3.YDirection()) < 0 ) ax3.YReverse();
|
||||
trsf.SetTransformation(ax3);
|
||||
trsf.Invert();
|
||||
const double scale1 = l->hasScale() ? l->Scale() : 1.0f;
|
||||
const double scale2 = l->hasScale2() ? l->Scale2() : scale1;
|
||||
const double scale3 = l->hasScale3() ? l->Scale3() : scale1;
|
||||
gtrsf = gp_GTrsf();
|
||||
gtrsf.SetValue(1,1,scale1);
|
||||
gtrsf.SetValue(2,2,scale2);
|
||||
gtrsf.SetValue(3,3,scale3);
|
||||
gtrsf.PreMultiply(trsf);
|
||||
|
||||
if (is_identity(gtrsf, getValue(GV_PRECISION))) {
|
||||
gtrsf = gp_GTrsf();
|
||||
}
|
||||
|
||||
CACHE(IfcCartesianTransformationOperator3DnonUniform,l,gtrsf)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperator2DnonUniform* l, gp_GTrsf2d& gtrsf) {
|
||||
IN_CACHE(IfcCartesianTransformationOperator2DnonUniform,l,gp_GTrsf2d,gtrsf)
|
||||
|
||||
gp_Trsf2d trsf;
|
||||
gp_Pnt origin;
|
||||
gp_Dir axis1 (1.,0.,0.);
|
||||
gp_Dir axis2 (0.,1.,0.);
|
||||
|
||||
IfcGeom::Kernel::convert(l->LocalOrigin(),origin);
|
||||
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
|
||||
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
|
||||
|
||||
const gp_Pnt2d origin2d(origin.X(), origin.Y());
|
||||
const gp_Dir2d axis12d(axis1.X(), axis1.Y());
|
||||
const gp_Dir2d axis22d(axis2.X(), axis2.Y());
|
||||
|
||||
const gp_Ax2d ax2d (origin2d, axis12d);
|
||||
trsf.SetTransformation(ax2d);
|
||||
|
||||
if ( ax2d.Direction().Rotated(M_PI / 2.).Dot(axis22d) < 0. ) {
|
||||
gp_Trsf2d mirror; mirror.SetMirror(ax2d);
|
||||
trsf.Multiply(mirror);
|
||||
}
|
||||
|
||||
trsf.Invert();
|
||||
|
||||
const double scale1 = l->hasScale() ? l->Scale() : 1.0f;
|
||||
const double scale2 = l->hasScale2() ? l->Scale2() : scale1;
|
||||
gtrsf = gp_GTrsf2d();
|
||||
gtrsf.SetValue(1,1,scale1);
|
||||
gtrsf.SetValue(2,2,scale2);
|
||||
gtrsf.Multiply(trsf);
|
||||
|
||||
if (is_identity(gtrsf, getValue(GV_PRECISION))) {
|
||||
gtrsf = gp_GTrsf2d();
|
||||
}
|
||||
|
||||
CACHE(IfcCartesianTransformationOperator2DnonUniform,l,gtrsf)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcPlane* pln, gp_Pln& plane) {
|
||||
IN_CACHE(IfcPlane,pln,gp_Pln,plane)
|
||||
IfcSchema::IfcAxis2Placement3D* l = pln->Position();
|
||||
gp_Pnt o;gp_Dir axis = gp_Dir(0,0,1);gp_Dir refDirection;
|
||||
IfcGeom::Kernel::convert(l->Location(),o);
|
||||
bool hasRef = l->hasRefDirection();
|
||||
if ( l->hasAxis() ) IfcGeom::Kernel::convert(l->Axis(),axis);
|
||||
if ( hasRef ) IfcGeom::Kernel::convert(l->RefDirection(),refDirection);
|
||||
gp_Ax3 ax3;
|
||||
if ( hasRef ) ax3 = gp_Ax3(o,axis,refDirection);
|
||||
else ax3 = gp_Ax3(o,axis);
|
||||
plane = gp_Pln(ax3);
|
||||
CACHE(IfcPlane,pln,plane)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcAxis2Placement2D* l, gp_Trsf2d& trsf) {
|
||||
IN_CACHE(IfcAxis2Placement2D,l,gp_Trsf2d,trsf)
|
||||
gp_Pnt P; gp_Dir V (1,0,0);
|
||||
IfcGeom::Kernel::convert(l->Location(),P);
|
||||
if ( l->hasRefDirection() )
|
||||
IfcGeom::Kernel::convert(l->RefDirection(),V);
|
||||
|
||||
gp_Ax2d axis(gp_Pnt2d(P.X(),P.Y()), gp_Dir2d(V.X(),V.Y()));
|
||||
|
||||
if (!axis_equal(axis, gp_Ax2d(), getValue(GV_PRECISION))) {
|
||||
trsf.SetTransformation(axis, gp_Ax2d());
|
||||
}
|
||||
|
||||
CACHE(IfcAxis2Placement2D,l,trsf)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& trsf) {
|
||||
IN_CACHE(IfcObjectPlacement,l,gp_Trsf,trsf)
|
||||
if ( ! l->declaration().is(IfcSchema::IfcLocalPlacement::Class()) ) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Unsupported IfcObjectPlacement:", l);
|
||||
return false;
|
||||
}
|
||||
IfcSchema::IfcLocalPlacement* current = (IfcSchema::IfcLocalPlacement*)l;
|
||||
for (;;) {
|
||||
gp_Trsf trsf2;
|
||||
IfcSchema::IfcAxis2Placement* relplacement = current->RelativePlacement();
|
||||
if ( relplacement->declaration().is(IfcSchema::IfcAxis2Placement3D::Class()) ) {
|
||||
IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement3D*)relplacement,trsf2);
|
||||
trsf.PreMultiply(trsf2);
|
||||
}
|
||||
if ( current->hasPlacementRelTo() ) {
|
||||
IfcSchema::IfcObjectPlacement* parent = current->PlacementRelTo();
|
||||
IfcSchema::IfcProduct::list::ptr parentPlaces = parent->PlacesObject();
|
||||
bool parentPlacesType = false;
|
||||
for ( IfcSchema::IfcProduct::list::it iter = parentPlaces->begin();
|
||||
iter != parentPlaces->end(); ++iter) {
|
||||
if ( (*iter)->declaration().is(*placement_rel_to) ) parentPlacesType = true;
|
||||
}
|
||||
|
||||
if ( parentPlacesType ) break;
|
||||
else if ( parent->declaration().is(IfcSchema::IfcLocalPlacement::Class()) )
|
||||
current = (IfcSchema::IfcLocalPlacement*)current->PlacementRelTo();
|
||||
else break;
|
||||
} else break;
|
||||
}
|
||||
CACHE(IfcObjectPlacement,l,trsf)
|
||||
return true;
|
||||
}
|
||||
@@ -1,660 +0,0 @@
|
||||
#include <Geom_Line.hxx>
|
||||
#include <Geom_Circle.hxx>
|
||||
#include <Geom_Ellipse.hxx>
|
||||
#include <Geom_BSplineCurve.hxx>
|
||||
|
||||
#include <Geom_Plane.hxx>
|
||||
#include <Geom_BSplineSurface.hxx>
|
||||
#include <Geom_CylindricalSurface.hxx>
|
||||
|
||||
#include <BRepTools_WireExplorer.hxx>
|
||||
|
||||
#include <TColgp_Array2OfPnt.hxx>
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TColStd_Array2OfReal.hxx>
|
||||
#include <TColStd_Array1OfInteger.hxx>
|
||||
|
||||
#include "IfcGeom.h"
|
||||
|
||||
template <typename T, typename U>
|
||||
int convert_to_ifc(const T& t, U*& u, bool /*advanced*/) {
|
||||
std::vector<double> coords(3);
|
||||
coords[0] = t.X(); coords[1] = t.Y(); coords[2] = t.Z();
|
||||
u = new U(coords);
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Vertex& v, IfcSchema::IfcCartesianPoint*& p, bool advanced) {
|
||||
gp_Pnt pnt = BRep_Tool::Pnt(v);
|
||||
return convert_to_ifc(pnt, p, advanced);
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Vertex& v, IfcSchema::IfcVertex*& vertex, bool advanced) {
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
convert_to_ifc(v, p, advanced);
|
||||
vertex = new IfcSchema::IfcVertexPoint(p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const gp_Ax2& a, IfcSchema::IfcAxis2Placement3D*& ax, bool advanced) {
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
IfcSchema::IfcDirection *x, *z;
|
||||
if (!(convert_to_ifc(a.Location(), p, advanced) && convert_to_ifc(a.Direction(), z, advanced) && convert_to_ifc(a.XDirection(), x, advanced))) {
|
||||
ax = 0;
|
||||
return 0;
|
||||
}
|
||||
ax = new IfcSchema::IfcAxis2Placement3D(p, z, x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
void opencascade_array_to_vector(T& t, std::vector<U>& u) {
|
||||
u.reserve(t.Length());
|
||||
for (int i = t.Lower(); i <= t.Upper(); ++i) {
|
||||
u.push_back(t.Value(i));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
void opencascade_array_to_vector2(T& t, std::vector< std::vector<U> >& u) {
|
||||
u.reserve(t.RowLength());
|
||||
for (int j = t.LowerRow(); j <= t.UpperRow(); ++j) {
|
||||
std::vector<U> v;
|
||||
v.reserve(t.ColLength());
|
||||
for (int i = t.LowerCol(); i <= t.UpperCol(); ++i) {
|
||||
v.push_back(t.Value(j, i));
|
||||
}
|
||||
u.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
IfcSchema::IfcKnotType::Value opencascade_knotspec_to_ifc(GeomAbs_BSplKnotDistribution bspline_knot_spec) {
|
||||
IfcSchema::IfcKnotType::Value knot_spec = IfcSchema::IfcKnotType::IfcKnotType_UNSPECIFIED;
|
||||
if (bspline_knot_spec == GeomAbs_Uniform) {
|
||||
knot_spec = IfcSchema::IfcKnotType::IfcKnotType_UNIFORM_KNOTS;
|
||||
} else if (bspline_knot_spec == GeomAbs_QuasiUniform) {
|
||||
knot_spec = IfcSchema::IfcKnotType::IfcKnotType_QUASI_UNIFORM_KNOTS;
|
||||
} else if (bspline_knot_spec == GeomAbs_PiecewiseBezier) {
|
||||
knot_spec = IfcSchema::IfcKnotType::IfcKnotType_PIECEWISE_BEZIER_KNOTS;
|
||||
}
|
||||
return knot_spec;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const Handle_Geom_Curve& c, IfcSchema::IfcCurve*& curve, bool advanced) {
|
||||
if (c->DynamicType() == STANDARD_TYPE(Geom_Line)) {
|
||||
IfcSchema::IfcDirection* d;
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
|
||||
Handle_Geom_Line line = Handle_Geom_Line::DownCast(c);
|
||||
|
||||
if (!convert_to_ifc(line->Position().Location(), p, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
if (!convert_to_ifc(line->Position().Direction(), d, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
IfcSchema::IfcVector* v = new IfcSchema::IfcVector(d, 1.);
|
||||
curve = new IfcSchema::IfcLine(p, v);
|
||||
|
||||
return 1;
|
||||
} else if (c->DynamicType() == STANDARD_TYPE(Geom_Circle)) {
|
||||
IfcSchema::IfcAxis2Placement3D* ax;
|
||||
|
||||
Handle_Geom_Circle circle = Handle_Geom_Circle::DownCast(c);
|
||||
|
||||
convert_to_ifc(circle->Position(), ax, advanced);
|
||||
curve = new IfcSchema::IfcCircle(ax, circle->Radius());
|
||||
|
||||
return 1;
|
||||
} else if (c->DynamicType() == STANDARD_TYPE(Geom_Ellipse)) {
|
||||
IfcSchema::IfcAxis2Placement3D* ax;
|
||||
|
||||
Handle_Geom_Ellipse ellipse = Handle_Geom_Ellipse::DownCast(c);
|
||||
|
||||
convert_to_ifc(ellipse->Position(), ax, advanced);
|
||||
curve = new IfcSchema::IfcEllipse(ax, ellipse->MajorRadius(), ellipse->MinorRadius());
|
||||
|
||||
return 1;
|
||||
}
|
||||
#ifdef USE_IFC4
|
||||
else if (c->DynamicType() == STANDARD_TYPE(Geom_BSplineCurve)) {
|
||||
Handle_Geom_BSplineCurve bspline = Handle_Geom_BSplineCurve::DownCast(c);
|
||||
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
|
||||
TColgp_Array1OfPnt poles(1, bspline->NbPoles());
|
||||
bspline->Poles(poles);
|
||||
for (int i = 1; i <= bspline->NbPoles(); ++i) {
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
if (!convert_to_ifc(poles.Value(i), p, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
points->push(p);
|
||||
}
|
||||
IfcSchema::IfcKnotType::Value knot_spec = opencascade_knotspec_to_ifc(bspline->KnotDistribution());
|
||||
|
||||
std::vector<int> mults;
|
||||
std::vector<double> knots;
|
||||
std::vector<double> weights;
|
||||
|
||||
TColStd_Array1OfInteger bspline_mults(1, bspline->NbKnots());
|
||||
TColStd_Array1OfReal bspline_knots(1, bspline->NbKnots());
|
||||
TColStd_Array1OfReal bspline_weights(1, bspline->NbPoles());
|
||||
|
||||
bspline->Multiplicities(bspline_mults);
|
||||
bspline->Knots(bspline_knots);
|
||||
bspline->Weights(bspline_weights);
|
||||
|
||||
opencascade_array_to_vector(bspline_mults, mults);
|
||||
opencascade_array_to_vector(bspline_knots, knots);
|
||||
opencascade_array_to_vector(bspline_weights, weights);
|
||||
|
||||
bool rational = false;
|
||||
for (std::vector<double>::const_iterator it = weights.begin(); it != weights.end(); ++it) {
|
||||
if ((*it) != 1.) {
|
||||
rational = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rational) {
|
||||
curve = new IfcSchema::IfcRationalBSplineCurveWithKnots(
|
||||
bspline->Degree(),
|
||||
points,
|
||||
IfcSchema::IfcBSplineCurveForm::IfcBSplineCurveForm_UNSPECIFIED,
|
||||
bspline->IsClosed() != 0,
|
||||
false,
|
||||
mults,
|
||||
knots,
|
||||
knot_spec,
|
||||
weights
|
||||
);
|
||||
} else {
|
||||
curve = new IfcSchema::IfcBSplineCurveWithKnots(
|
||||
bspline->Degree(),
|
||||
points,
|
||||
IfcSchema::IfcBSplineCurveForm::IfcBSplineCurveForm_UNSPECIFIED,
|
||||
bspline->IsClosed() != 0,
|
||||
false,
|
||||
mults,
|
||||
knots,
|
||||
knot_spec
|
||||
);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const Handle_Geom_Surface& s, IfcSchema::IfcSurface*& surface, bool advanced) {
|
||||
if (s->DynamicType() == STANDARD_TYPE(Geom_Plane)) {
|
||||
Handle_Geom_Plane plane = Handle_Geom_Plane::DownCast(s);
|
||||
IfcSchema::IfcAxis2Placement3D* place;
|
||||
/// @todo: Note that the Ax3 is converted to an Ax2 here
|
||||
if (!convert_to_ifc(plane->Position().Ax2(), place, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
surface = new IfcSchema::IfcPlane(place);
|
||||
return 1;
|
||||
}
|
||||
#ifdef USE_IFC4
|
||||
else if (s->DynamicType() == STANDARD_TYPE(Geom_CylindricalSurface)) {
|
||||
Handle_Geom_CylindricalSurface cyl = Handle_Geom_CylindricalSurface::DownCast(s);
|
||||
IfcSchema::IfcAxis2Placement3D* place;
|
||||
/// @todo: Note that the Ax3 is converted to an Ax2 here
|
||||
if (!convert_to_ifc(cyl->Position().Ax2(), place, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
surface = new IfcSchema::IfcCylindricalSurface(place, cyl->Radius());
|
||||
return 1;
|
||||
} else if (s->DynamicType() == STANDARD_TYPE(Geom_BSplineSurface)) {
|
||||
typedef IfcTemplatedEntityListList<IfcSchema::IfcCartesianPoint> points_t;
|
||||
|
||||
Handle_Geom_BSplineSurface bspline = Handle_Geom_BSplineSurface::DownCast(s);
|
||||
points_t::ptr points(new points_t);
|
||||
|
||||
TColgp_Array2OfPnt poles(1, bspline->NbUPoles(), 1, bspline->NbVPoles());
|
||||
bspline->Poles(poles);
|
||||
for (int i = 1; i <= bspline->NbUPoles(); ++i) {
|
||||
std::vector<IfcSchema::IfcCartesianPoint*> ps;
|
||||
ps.reserve(bspline->NbVPoles());
|
||||
for (int j = 1; j <= bspline->NbVPoles(); ++j) {
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
if (!convert_to_ifc(poles.Value(i, j), p, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
ps.push_back(p);
|
||||
}
|
||||
points->push(ps);
|
||||
}
|
||||
|
||||
IfcSchema::IfcKnotType::Value knot_spec_u = opencascade_knotspec_to_ifc(bspline->UKnotDistribution());
|
||||
IfcSchema::IfcKnotType::Value knot_spec_v = opencascade_knotspec_to_ifc(bspline->VKnotDistribution());
|
||||
|
||||
if (knot_spec_u != knot_spec_v) {
|
||||
knot_spec_u = IfcSchema::IfcKnotType::IfcKnotType_UNSPECIFIED;
|
||||
}
|
||||
|
||||
std::vector<int> umults;
|
||||
std::vector<int> vmults;
|
||||
std::vector<double> uknots;
|
||||
std::vector<double> vknots;
|
||||
std::vector< std::vector<double> > weights;
|
||||
|
||||
TColStd_Array1OfInteger bspline_umults(1, bspline->NbUKnots());
|
||||
TColStd_Array1OfInteger bspline_vmults(1, bspline->NbVKnots());
|
||||
TColStd_Array1OfReal bspline_uknots(1, bspline->NbUKnots());
|
||||
TColStd_Array1OfReal bspline_vknots(1, bspline->NbVKnots());
|
||||
TColStd_Array2OfReal bspline_weights(1, bspline->NbUPoles(), 1, bspline->NbVPoles());
|
||||
|
||||
bspline->UMultiplicities(bspline_umults);
|
||||
bspline->VMultiplicities(bspline_vmults);
|
||||
bspline->UKnots(bspline_uknots);
|
||||
bspline->VKnots(bspline_vknots);
|
||||
bspline->Weights(bspline_weights);
|
||||
|
||||
opencascade_array_to_vector(bspline_umults, umults);
|
||||
opencascade_array_to_vector(bspline_vmults, vmults);
|
||||
opencascade_array_to_vector(bspline_uknots, uknots);
|
||||
opencascade_array_to_vector(bspline_vknots, vknots);
|
||||
opencascade_array_to_vector2(bspline_weights, weights);
|
||||
|
||||
bool rational = false;
|
||||
for (std::vector< std::vector<double> >::const_iterator it = weights.begin(); it != weights.end(); ++it) {
|
||||
for (std::vector<double>::const_iterator jt = it->begin(); jt != it->end(); ++jt) {
|
||||
if ((*jt) != 1.) {
|
||||
rational = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rational) {
|
||||
surface = new IfcSchema::IfcRationalBSplineSurfaceWithKnots(
|
||||
bspline->UDegree(),
|
||||
bspline->VDegree(),
|
||||
points,
|
||||
IfcSchema::IfcBSplineSurfaceForm::IfcBSplineSurfaceForm_UNSPECIFIED,
|
||||
bspline->IsUClosed() != 0,
|
||||
bspline->IsVClosed() != 0,
|
||||
false,
|
||||
umults,
|
||||
vmults,
|
||||
uknots,
|
||||
vknots,
|
||||
knot_spec_u,
|
||||
weights
|
||||
);
|
||||
} else {
|
||||
surface = new IfcSchema::IfcBSplineSurfaceWithKnots(
|
||||
bspline->UDegree(),
|
||||
bspline->VDegree(),
|
||||
points,
|
||||
IfcSchema::IfcBSplineSurfaceForm::IfcBSplineSurfaceForm_UNSPECIFIED,
|
||||
bspline->IsUClosed() != 0,
|
||||
bspline->IsVClosed() != 0,
|
||||
false,
|
||||
umults,
|
||||
vmults,
|
||||
uknots,
|
||||
vknots,
|
||||
knot_spec_u
|
||||
);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Edge& e, IfcSchema::IfcCurve*& c, bool advanced) {
|
||||
double a, b;
|
||||
IfcSchema::IfcCurve* base;
|
||||
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b);
|
||||
if (!convert_to_ifc(crv, base, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
IfcEntityList::ptr trim1(new IfcEntityList);
|
||||
IfcEntityList::ptr trim2(new IfcEntityList);
|
||||
trim1->push(new IfcSchema::IfcParameterValue(a));
|
||||
trim2->push(new IfcSchema::IfcParameterValue(b));
|
||||
|
||||
c = new IfcSchema::IfcTrimmedCurve(base, trim1, trim2, true, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Edge& e, IfcSchema::IfcEdge*& edge, bool advanced) {
|
||||
double a, b;
|
||||
|
||||
TopExp_Explorer exp(e, TopAbs_VERTEX);
|
||||
if (!exp.More()) return 0;
|
||||
TopoDS_Vertex v1 = TopoDS::Vertex(exp.Current());
|
||||
exp.Next();
|
||||
if (!exp.More()) return 0;
|
||||
TopoDS_Vertex v2 = TopoDS::Vertex(exp.Current());
|
||||
|
||||
IfcSchema::IfcVertex *vertex1, *vertex2;
|
||||
if (!(convert_to_ifc(v1, vertex1, advanced) && convert_to_ifc(v2, vertex2, advanced))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b);
|
||||
|
||||
if (crv.IsNull()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (crv->DynamicType() == STANDARD_TYPE(Geom_Line) && !advanced) {
|
||||
IfcSchema::IfcEdge* edge2 = new IfcSchema::IfcEdge(vertex1, vertex2);
|
||||
edge = new IfcSchema::IfcOrientedEdge(edge2, true);
|
||||
return 1;
|
||||
} else {
|
||||
IfcSchema::IfcCurve* curve;
|
||||
if (!convert_to_ifc(crv, curve, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
/// @todo probably not correct
|
||||
const bool sense = e.Orientation() == TopAbs_FORWARD;
|
||||
IfcSchema::IfcEdge* edge2 = new IfcSchema::IfcEdgeCurve(vertex1, vertex2, curve, true);
|
||||
edge = new IfcSchema::IfcOrientedEdge(edge2, sense);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Wire& wire, IfcSchema::IfcLoop*& loop, bool advanced) {
|
||||
bool polygonal = true;
|
||||
for (TopExp_Explorer exp(wire, TopAbs_EDGE); exp.More(); exp.Next()) {
|
||||
double a, b;
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b);
|
||||
if (crv.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) {
|
||||
polygonal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!polygonal && !advanced) {
|
||||
return 0;
|
||||
} else if (polygonal && !advanced) {
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
|
||||
BRepTools_WireExplorer exp(wire);
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
for (; exp.More(); exp.Next()) {
|
||||
if (convert_to_ifc(exp.CurrentVertex(), p, advanced)) {
|
||||
points->push(p);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
loop = new IfcSchema::IfcPolyLoop(points);
|
||||
return 1;
|
||||
} else {
|
||||
IfcSchema::IfcOrientedEdge::list::ptr edges(new IfcSchema::IfcOrientedEdge::list);
|
||||
BRepTools_WireExplorer exp(wire);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
IfcSchema::IfcEdge* edge;
|
||||
// With advanced set to true convert_to_ifc(TopoDS_Edge&) will always create an IfcOrientedEdge
|
||||
if (!convert_to_ifc(exp.Current(), edge, true)) {
|
||||
double a, b;
|
||||
if (BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b).IsNull()) {
|
||||
continue;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
edges->push(edge->as<IfcSchema::IfcOrientedEdge>());
|
||||
}
|
||||
loop = new IfcSchema::IfcEdgeLoop(edges);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Face& f, IfcSchema::IfcFace*& face, bool advanced) {
|
||||
Handle_Geom_Surface surf = BRep_Tool::Surface(f);
|
||||
TopExp_Explorer exp(f, TopAbs_WIRE);
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds(new IfcSchema::IfcFaceBound::list);
|
||||
int index = 0;
|
||||
for (; exp.More(); exp.Next(), ++index) {
|
||||
IfcSchema::IfcLoop* loop;
|
||||
if (!convert_to_ifc(TopoDS::Wire(exp.Current()), loop, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
IfcSchema::IfcFaceBound* bnd;
|
||||
if (index == 0) {
|
||||
bnd = new IfcSchema::IfcFaceOuterBound(loop, true);
|
||||
} else {
|
||||
bnd = new IfcSchema::IfcFaceBound(loop, true);
|
||||
}
|
||||
bounds->push(bnd);
|
||||
}
|
||||
|
||||
const bool is_planar = surf->DynamicType() == STANDARD_TYPE(Geom_Plane);
|
||||
|
||||
if (!is_planar && !advanced) {
|
||||
return 0;
|
||||
}
|
||||
if (is_planar && !advanced) {
|
||||
face = new IfcSchema::IfcFace(bounds);
|
||||
return 1;
|
||||
} else {
|
||||
#ifdef USE_IFC4
|
||||
IfcSchema::IfcSurface* surface;
|
||||
if (!convert_to_ifc(surf, surface, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
face = new IfcSchema::IfcAdvancedFace(bounds, surface, f.Orientation() == TopAbs_FORWARD);
|
||||
return 1;
|
||||
#else
|
||||
// No IfcAdvancedFace in Ifc2x3
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
int convert_to_ifc(const TopoDS_Shape& s, U*& item, bool advanced) {
|
||||
IfcSchema::IfcFace::list::ptr faces(new IfcSchema::IfcFace::list);
|
||||
IfcSchema::IfcFace* f;
|
||||
|
||||
for (TopExp_Explorer exp(s, TopAbs_FACE); exp.More(); exp.Next()) {
|
||||
if (convert_to_ifc(TopoDS::Face(exp.Current()), f, advanced)) {
|
||||
faces->push(f);
|
||||
} else {
|
||||
/// Cleanup:
|
||||
for (IfcSchema::IfcFace::list::it it = faces->begin(); it != faces->end(); ++it) {
|
||||
IfcEntityList::ptr data = IfcParse::traverse(*it)->unique();
|
||||
for (IfcEntityList::it jt = data->begin(); jt != data->end(); ++jt) {
|
||||
delete *jt;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
item = new U(faces);
|
||||
return faces->size();
|
||||
}
|
||||
|
||||
IfcUtil::IfcBaseClass* IfcGeom::POSTFIX_SCHEMA(serialise_)(const TopoDS_Shape& shape, bool advanced) {
|
||||
#ifndef USE_IFC4
|
||||
advanced = false;
|
||||
#endif
|
||||
|
||||
for (TopExp_Explorer exp(shape, TopAbs_COMPSOLID); exp.More();) {
|
||||
/// @todo CompSolids are not supported
|
||||
return 0;
|
||||
}
|
||||
|
||||
IfcSchema::IfcRepresentation* rep = 0;
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items(new IfcSchema::IfcRepresentationItem::list);
|
||||
|
||||
// First check if there is a solid with one or more shells
|
||||
for (TopExp_Explorer exp(shape, TopAbs_SOLID); exp.More(); exp.Next()) {
|
||||
IfcSchema::IfcClosedShell* outer = 0;
|
||||
IfcSchema::IfcClosedShell::list::ptr inner(new IfcSchema::IfcClosedShell::list);
|
||||
for (TopExp_Explorer exp2(exp.Current(), TopAbs_SHELL); exp2.More(); exp2.Next()) {
|
||||
IfcSchema::IfcClosedShell* shell;
|
||||
if (!convert_to_ifc(exp2.Current(), shell, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
/// @todo Are shells always in this order or does Orientation() needs to be checked?
|
||||
if (outer) {
|
||||
inner->push(shell);
|
||||
} else {
|
||||
outer = shell;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
if (advanced) {
|
||||
if (inner->size()) {
|
||||
items->push(new IfcSchema::IfcAdvancedBrepWithVoids(outer, inner));
|
||||
} else {
|
||||
items->push(new IfcSchema::IfcAdvancedBrep(outer));
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
|
||||
/// @todo this is not necessarily correct as the shell is not necessarily facetted.
|
||||
if (inner->size()) {
|
||||
items->push(new IfcSchema::IfcFacetedBrepWithVoids(outer, inner));
|
||||
} else {
|
||||
items->push(new IfcSchema::IfcFacetedBrep(outer));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (items->size() > 0) {
|
||||
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items);
|
||||
} else {
|
||||
|
||||
// If not, see if there is a shell
|
||||
IfcSchema::IfcOpenShell::list::ptr shells(new IfcSchema::IfcOpenShell::list);
|
||||
for (TopExp_Explorer exp(shape, TopAbs_SHELL); exp.More(); exp.Next()) {
|
||||
IfcSchema::IfcOpenShell* shell;
|
||||
if (!convert_to_ifc(exp.Current(), shell, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
shells->push(shell);
|
||||
}
|
||||
|
||||
if (shells->size() > 0) {
|
||||
items->push(new IfcSchema::IfcShellBasedSurfaceModel(shells->generalize()));
|
||||
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items);
|
||||
} else {
|
||||
|
||||
// If not, see if there is are one of more faces. Note that they will be grouped into a shell.
|
||||
IfcSchema::IfcOpenShell* shell;
|
||||
int face_count = convert_to_ifc(shape, shell, advanced);
|
||||
|
||||
if (face_count > 0) {
|
||||
items->push(shell);
|
||||
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items);
|
||||
} else {
|
||||
|
||||
// If not, see if there are any edges. Note that wires are skipped as
|
||||
// they are not commonly top-level geometrical descriptions in IFC.
|
||||
// Also note that edges are written as trimmed curves rather than edges.
|
||||
|
||||
IfcEntityList::ptr edges(new IfcEntityList);
|
||||
|
||||
for (TopExp_Explorer exp(shape, TopAbs_EDGE); exp.More(); exp.Next()) {
|
||||
IfcSchema::IfcCurve* c;
|
||||
if (!convert_to_ifc(TopoDS::Edge(exp.Current()), c, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
edges->push(c);
|
||||
}
|
||||
|
||||
if (edges->size() == 0) {
|
||||
return 0;
|
||||
} else if (edges->size() == 1) {
|
||||
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Axis"), std::string("Curve2D"), edges->as<IfcSchema::IfcRepresentationItem>());
|
||||
} else {
|
||||
// A geometric set is created as that probably (?) makes more sense in IFC
|
||||
IfcSchema::IfcGeometricCurveSet* curves = new IfcSchema::IfcGeometricCurveSet(edges);
|
||||
items->push(curves);
|
||||
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Axis"), std::string("GeometricCurveSet"), items->as<IfcSchema::IfcRepresentationItem>());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list);
|
||||
reps->push(rep);
|
||||
return new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
}
|
||||
|
||||
IfcUtil::IfcBaseClass* IfcGeom::POSTFIX_SCHEMA(tesselate_)(const TopoDS_Shape& shape, double deflection) {
|
||||
BRepMesh_IncrementalMesh(shape, deflection);
|
||||
|
||||
IfcSchema::IfcFace::list::ptr faces(new IfcSchema::IfcFace::list);
|
||||
|
||||
for (TopExp_Explorer exp(shape, TopAbs_FACE); exp.More(); exp.Next()) {
|
||||
const TopoDS_Face& face = TopoDS::Face(exp.Current());
|
||||
TopLoc_Location loc;
|
||||
Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(face, loc);
|
||||
|
||||
if (!tri.IsNull()) {
|
||||
const TColgp_Array1OfPnt& nodes = tri->Nodes();
|
||||
std::vector<IfcSchema::IfcCartesianPoint*> vertices;
|
||||
for (int i = 1; i <= nodes.Length(); ++i) {
|
||||
gp_Pnt pnt = nodes(i).Transformed(loc);
|
||||
std::vector<double> xyz; xyz.push_back(pnt.X()); xyz.push_back(pnt.Y()); xyz.push_back(pnt.Z());
|
||||
IfcSchema::IfcCartesianPoint* cpnt = new IfcSchema::IfcCartesianPoint(xyz);
|
||||
vertices.push_back(cpnt);
|
||||
}
|
||||
const Poly_Array1OfTriangle& triangles = tri->Triangles();
|
||||
for (int i = 1; i <= triangles.Length(); ++i) {
|
||||
int n1, n2, n3;
|
||||
triangles(i).Get(n1, n2, n3);
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
|
||||
points->push(vertices[n1 - 1]);
|
||||
points->push(vertices[n2 - 1]);
|
||||
points->push(vertices[n3 - 1]);
|
||||
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
|
||||
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, face.Orientation() != TopAbs_REVERSED);
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds(new IfcSchema::IfcFaceBound::list);
|
||||
bounds->push(bound);
|
||||
IfcSchema::IfcFace* face2 = new IfcSchema::IfcFace(bounds);
|
||||
faces->push(face2);
|
||||
}
|
||||
}
|
||||
}
|
||||
IfcSchema::IfcOpenShell* shell = new IfcSchema::IfcOpenShell(faces);
|
||||
IfcSchema::IfcConnectedFaceSet::list::ptr shells(new IfcSchema::IfcConnectedFaceSet::list);
|
||||
shells->push(shell);
|
||||
IfcSchema::IfcFaceBasedSurfaceModel* surface_model = new IfcSchema::IfcFaceBasedSurfaceModel(shells);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list);
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items(new IfcSchema::IfcRepresentationItem::list);
|
||||
|
||||
items->push(surface_model);
|
||||
|
||||
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
|
||||
0, std::string("Facetation"), std::string("SurfaceModel"), items);
|
||||
|
||||
reps->push(rep);
|
||||
IfcSchema::IfcProductDefinitionShape* shapedef = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
|
||||
return shapedef;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,929 +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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
/********************************************************************************
|
||||
* *
|
||||
* Implementations of the various conversion functions defined in IfcRegister.h *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
|
||||
#include <gp_Pnt.hxx>
|
||||
#include <gp_Vec.hxx>
|
||||
#include <gp_Dir.hxx>
|
||||
#include <gp_Pnt2d.hxx>
|
||||
#include <gp_Vec2d.hxx>
|
||||
#include <gp_Dir2d.hxx>
|
||||
#include <gp_Mat.hxx>
|
||||
#include <gp_Mat2d.hxx>
|
||||
#include <gp_GTrsf.hxx>
|
||||
#include <gp_GTrsf2d.hxx>
|
||||
#include <gp_Trsf.hxx>
|
||||
#include <gp_Trsf2d.hxx>
|
||||
#include <gp_Ax3.hxx>
|
||||
#include <gp_Ax2d.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
#include <gp_Circ.hxx>
|
||||
|
||||
#include <GC_MakeCircle.hxx>
|
||||
|
||||
#include <TColgp_Array1OfPnt.hxx>
|
||||
#include <TColgp_Array1OfPnt2d.hxx>
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TColStd_Array1OfInteger.hxx>
|
||||
|
||||
#include <Geom_Line.hxx>
|
||||
#include <Geom_Circle.hxx>
|
||||
#include <Geom_Ellipse.hxx>
|
||||
#include <Geom_TrimmedCurve.hxx>
|
||||
|
||||
#include <BRepBuilderAPI_MakeVertex.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRepBuilderAPI_MakeShell.hxx>
|
||||
#include <BRepBuilderAPI_MakeSolid.hxx>
|
||||
#include <BRepBuilderAPI_MakePolygon.hxx>
|
||||
#include <BRepBuilderAPI_MakeVertex.hxx>
|
||||
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
|
||||
#include <TopExp.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <TopLoc_Location.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
|
||||
#include <BRepAlgoAPI_Cut.hxx>
|
||||
#include <BRepOffsetAPI_Sewing.hxx>
|
||||
#include <BRepPrimAPI_MakePrism.hxx>
|
||||
#include <BRepPrimAPI_MakeHalfSpace.hxx>
|
||||
#include <BRepFilletAPI_MakeFillet2d.hxx>
|
||||
|
||||
#include <BRep_Tool.hxx>
|
||||
|
||||
#include <ShapeFix_Shape.hxx>
|
||||
#include <ShapeFix_ShapeTolerance.hxx>
|
||||
#include <ShapeFix_Solid.hxx>
|
||||
|
||||
#include <Geom_BSplineCurve.hxx>
|
||||
#include <BRepTools_WireExplorer.hxx>
|
||||
#include <ShapeBuild_ReShape.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
#include <TopTools_ListIteratorOfListOfShape.hxx>
|
||||
|
||||
#include <BRepAdaptor_CompCurve.hxx>
|
||||
#include <BRepAdaptor_HCompCurve.hxx>
|
||||
#include <Approx_Curve3d.hxx>
|
||||
|
||||
#include "../../../ifcgeom/kernels/opencascade/IfcGeom.h"
|
||||
|
||||
#define Kernel POSTFIX_SCHEMA(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) {
|
||||
TopTools_IndexedDataMapOfShapeListOfShape map;
|
||||
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, map);
|
||||
|
||||
bool all_linear = true, single_circle = false, first = true;
|
||||
|
||||
const TopTools_ListOfShape& edges = map.FindFromKey(v);
|
||||
TopTools_ListIteratorOfListOfShape it(edges);
|
||||
for (; it.More(); it.Next()) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(it.Value());
|
||||
double _, __;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(e, _, __);
|
||||
const bool is_line = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
|
||||
const bool is_circle = crv->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
||||
all_linear = all_linear && is_line;
|
||||
single_circle = first && is_circle;
|
||||
}
|
||||
|
||||
if (all_linear) {
|
||||
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));
|
||||
} else if (single_circle) {
|
||||
TopoDS_Vertex v1, v2;
|
||||
TopExp::Vertices(w, v1, v2);
|
||||
|
||||
gp_Pnt p1, p2, p3;
|
||||
p1 = v.IsEqual(v1) ? p : BRep_Tool::Pnt(v1);
|
||||
p3 = v.IsEqual(v2) ? p : BRep_Tool::Pnt(v2);
|
||||
|
||||
double a, b;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(edges.First()), a, b);
|
||||
crv->D0((a + b) / 2., p2);
|
||||
|
||||
GC_MakeCircle mc(p1, p2, p3);
|
||||
if (!mc.IsDone()) {
|
||||
throw IfcGeom::geometry_exception("Failed to adjust circle");
|
||||
}
|
||||
|
||||
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(mc.Value(), p1, p3).Edge();
|
||||
BRepBuilderAPI_MakeWire builder;
|
||||
builder.Add(edge);
|
||||
return builder.Wire();
|
||||
} else {
|
||||
throw IfcGeom::geometry_exception("Unexpected wire to adjust");
|
||||
}
|
||||
}
|
||||
|
||||
// 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 tolerance, 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::Warning("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);
|
||||
|
||||
const bool is_circle1 = c1->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
||||
const bool is_circle2 = c2->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
||||
|
||||
// Preferably adjust the segment that is linear
|
||||
if (is_line1 || (is_circle1 && !is_line2)) {
|
||||
mw_.Add(adjust(w1, w12, p2));
|
||||
Logger::Notice("Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
|
||||
} else if ((is_line2 || is_circle2) && !last) {
|
||||
mw_.Add(w1);
|
||||
override_next_ = true;
|
||||
next_override_ = p1;
|
||||
Logger::Notice("Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
|
||||
} else {
|
||||
// In all other cases an edge is added
|
||||
mw_.Add(w1);
|
||||
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
|
||||
Logger::Warning("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);
|
||||
|
||||
// Temporarily pretend we do have unit information
|
||||
setValue(GV_PLANEANGLE_UNIT,1.0);
|
||||
|
||||
bool succes_radians = false;
|
||||
bool succes_degrees = false;
|
||||
bool use_radians = false;
|
||||
bool use_degrees = false;
|
||||
|
||||
// First try radians
|
||||
TopoDS_Wire wire_radians, wire_degrees;
|
||||
try {
|
||||
succes_radians = IfcGeom::Kernel::convert(l,wire_radians);
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Notice(e);
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Notice(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Notice("Unknown error using radians");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Notice("Unknown error using radians");
|
||||
}
|
||||
|
||||
// Now try degrees
|
||||
setValue(GV_PLANEANGLE_UNIT,0.0174532925199433);
|
||||
try {
|
||||
succes_degrees = IfcGeom::Kernel::convert(l,wire_degrees);
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Notice(e);
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Notice(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Notice("Unknown error using degrees");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Notice("Unknown error using degrees");
|
||||
}
|
||||
|
||||
// Restore to unknown unit state
|
||||
setValue(GV_PLANEANGLE_UNIT,-1.0);
|
||||
|
||||
if ( succes_degrees && ! succes_radians ) {
|
||||
use_degrees = true;
|
||||
} else if ( succes_radians && ! succes_degrees ) {
|
||||
use_radians = true;
|
||||
} else if ( succes_radians && succes_degrees ) {
|
||||
if ( wire_degrees.Closed() && ! wire_radians.Closed() ) {
|
||||
use_degrees = true;
|
||||
} else if ( wire_radians.Closed() && ! wire_degrees.Closed() ) {
|
||||
use_radians = true;
|
||||
} else {
|
||||
// No heuristic left to prefer the one over the other,
|
||||
// apparently both variants are equally successful.
|
||||
// The curve might be composed of only straight segments.
|
||||
// Let's go with the wire created using radians as that
|
||||
// at least is a SI unit.
|
||||
use_radians = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( use_radians ) {
|
||||
Logger::Message(Logger::LOG_NOTICE,"Used radians to create composite curve");
|
||||
wire = wire_radians;
|
||||
} else if ( use_degrees ) {
|
||||
Logger::Message(Logger::LOG_NOTICE,"Used degrees to create composite curve");
|
||||
wire = wire_degrees;
|
||||
}
|
||||
|
||||
return use_radians || use_degrees;
|
||||
}
|
||||
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment::list::ptr segments = l->Segments();
|
||||
|
||||
TopTools_ListOfShape converted_segments;
|
||||
|
||||
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);
|
||||
|
||||
converted_segments.Append(segment);
|
||||
|
||||
}
|
||||
|
||||
if (converted_segments.Extent() == 0) {
|
||||
Logger::Message(Logger::LOG_ERROR, "No segment succesfully converted:", l);
|
||||
return false;
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
TopoDS_Vertex wire_first_vertex, wire_last_vertex, edge_first_vertex, edge_last_vertex;
|
||||
|
||||
TopTools_ListIteratorOfListOfShape it(converted_segments);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
Below is code to deduce the formula below in SageMath
|
||||
|
||||
| R, b = var('R b')
|
||||
|
|
||||
| Bxy = R * cos(b), R * sin(b)
|
||||
| Cxy = R * cos(b/2), R * sin(b/2)
|
||||
|
|
||||
| def dot(v, w):
|
||||
| return v[0] * w[0] + v[1] * w[1]
|
||||
|
|
||||
| def norm(v):
|
||||
| l = sqrt(v[0]^2 + v[1]^2)
|
||||
| return v[0] / l, v[1] / l
|
||||
|
|
||||
| (R - R*dot(norm(Cxy), norm(Bxy))).full_simplify()
|
||||
*/
|
||||
|
||||
double deflection_for_approximating_circle(double radius, double param) {
|
||||
return -radius * std::cos(1. / 2. * param) * std::cos(param) - radius * std::sin(1. / 2. * param) * std::sin(param) + radius;
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrimmedCurve* l, TopoDS_Wire& wire) {
|
||||
IfcSchema::IfcCurve* basis_curve = l->BasisCurve();
|
||||
bool isConic = basis_curve->declaration().is(IfcSchema::IfcConic::Class());
|
||||
double parameterFactor = isConic ? getValue(GV_PLANEANGLE_UNIT) : getValue(GV_LENGTH_UNIT);
|
||||
|
||||
Handle(Geom_Curve) curve;
|
||||
if (shape_type(basis_curve) == ST_CURVE) {
|
||||
if (!convert_curve(basis_curve, curve)) return false;
|
||||
} else if (shape_type(basis_curve) == ST_WIRE) {
|
||||
Logger::Warning("Approximating BasisCurve due to possible discontinuities", l);
|
||||
TopoDS_Wire w;
|
||||
if (!convert_wire(basis_curve, w)) return false;
|
||||
BRepAdaptor_CompCurve cc(w, true);
|
||||
Handle(Adaptor3d_HCurve) hcc = Handle(Adaptor3d_HCurve)(new BRepAdaptor_HCompCurve(cc));
|
||||
// @todo, arbitrary numbers here, note they cannot be too high as contiguous memory is allocated based on them.
|
||||
Approx_Curve3d approx(hcc, getValue(GV_PRECISION), GeomAbs_C0, 10, 10);
|
||||
curve = approx.Curve();
|
||||
} else {
|
||||
Logger::Error("Unknown BasisCurve", l);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool trim_cartesian = l->MasterRepresentation() != IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER;
|
||||
IfcEntityList::ptr trims1 = l->Trim1();
|
||||
IfcEntityList::ptr trims2 = l->Trim2();
|
||||
|
||||
unsigned sense_agreement = l->SenseAgreement() ? 0 : 1;
|
||||
double flts[2];
|
||||
gp_Pnt pnts[2];
|
||||
bool has_flts[2] = {false,false};
|
||||
bool has_pnts[2] = {false,false};
|
||||
|
||||
TopoDS_Edge e;
|
||||
|
||||
for ( IfcEntityList::it it = trims1->begin(); it != trims1->end(); it ++ ) {
|
||||
IfcUtil::IfcBaseClass* i = *it;
|
||||
if ( i->declaration().is(IfcSchema::IfcCartesianPoint::Class()) ) {
|
||||
IfcGeom::Kernel::convert((IfcSchema::IfcCartesianPoint*)i, pnts[sense_agreement] );
|
||||
has_pnts[sense_agreement] = true;
|
||||
} else if ( i->declaration().is(IfcSchema::IfcParameterValue::Class()) ) {
|
||||
const double value = *((IfcSchema::IfcParameterValue*)i);
|
||||
flts[sense_agreement] = value * parameterFactor;
|
||||
has_flts[sense_agreement] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for ( IfcEntityList::it it = trims2->begin(); it != trims2->end(); it ++ ) {
|
||||
IfcUtil::IfcBaseClass* i = *it;
|
||||
if ( i->declaration().is(IfcSchema::IfcCartesianPoint::Class()) ) {
|
||||
IfcGeom::Kernel::convert((IfcSchema::IfcCartesianPoint*)i, pnts[1-sense_agreement] );
|
||||
has_pnts[1-sense_agreement] = true;
|
||||
} else if ( i->declaration().is(IfcSchema::IfcParameterValue::Class()) ) {
|
||||
const double value = *((IfcSchema::IfcParameterValue*)i);
|
||||
flts[1-sense_agreement] = value * parameterFactor;
|
||||
has_flts[1-sense_agreement] = true;
|
||||
}
|
||||
}
|
||||
|
||||
trim_cartesian &= has_pnts[0] && has_pnts[1];
|
||||
bool trim_cartesian_failed = !trim_cartesian;
|
||||
if ( trim_cartesian ) {
|
||||
if ( pnts[0].Distance(pnts[1]) < 2 * getValue(GV_PRECISION) ) {
|
||||
Logger::Message(Logger::LOG_WARNING,"Skipping segment with length below tolerance level:",l);
|
||||
return false;
|
||||
}
|
||||
ShapeFix_ShapeTolerance FTol;
|
||||
TopoDS_Vertex v1 = BRepBuilderAPI_MakeVertex(pnts[0]);
|
||||
TopoDS_Vertex v2 = BRepBuilderAPI_MakeVertex(pnts[1]);
|
||||
FTol.SetTolerance(v1, getValue(GV_PRECISION), TopAbs_VERTEX);
|
||||
FTol.SetTolerance(v2, getValue(GV_PRECISION), TopAbs_VERTEX);
|
||||
BRepBuilderAPI_MakeEdge me (curve,v1,v2);
|
||||
if (!me.IsDone()) {
|
||||
BRepBuilderAPI_EdgeError err = me.Error();
|
||||
if ( err == BRepBuilderAPI_PointProjectionFailed ) {
|
||||
Logger::Message(Logger::LOG_WARNING,"Point projection failed for:",l);
|
||||
trim_cartesian_failed = true;
|
||||
}
|
||||
} else {
|
||||
e = me.Edge();
|
||||
}
|
||||
}
|
||||
|
||||
if ( (!trim_cartesian || trim_cartesian_failed) && (has_flts[0] && has_flts[1]) ) {
|
||||
// The Geom_Line is constructed from a gp_Pnt and gp_Dir, whereas the IfcLine
|
||||
// is defined by an IfcCartesianPoint and an IfcVector with Magnitude. Because
|
||||
// the vector is normalised when passed to Geom_Line constructor the magnitude
|
||||
// needs to be factored in with the IfcParameterValue here.
|
||||
if ( basis_curve->declaration().is(IfcSchema::IfcLine::Class()) ) {
|
||||
IfcSchema::IfcLine* line = static_cast<IfcSchema::IfcLine*>(basis_curve);
|
||||
const double magnitude = line->Dir()->Magnitude();
|
||||
flts[0] *= magnitude; flts[1] *= magnitude;
|
||||
}
|
||||
if ( basis_curve->declaration().is(IfcSchema::IfcEllipse::Class()) ) {
|
||||
IfcSchema::IfcEllipse* ellipse = static_cast<IfcSchema::IfcEllipse*>(basis_curve);
|
||||
double x = ellipse->SemiAxis1() * getValue(GV_LENGTH_UNIT);
|
||||
double y = ellipse->SemiAxis2() * getValue(GV_LENGTH_UNIT);
|
||||
const bool rotated = y > x;
|
||||
if (rotated) {
|
||||
flts[0] -= M_PI / 2.;
|
||||
flts[1] -= M_PI / 2.;
|
||||
}
|
||||
}
|
||||
if ( isConic && ALMOST_THE_SAME(fmod(flts[1]-flts[0],M_PI*2.),0.) ) {
|
||||
e = BRepBuilderAPI_MakeEdge(curve).Edge();
|
||||
} else {
|
||||
BRepBuilderAPI_MakeEdge me (curve,flts[0],flts[1]);
|
||||
e = me.Edge();
|
||||
}
|
||||
} else if ( trim_cartesian_failed && (has_pnts[0] && has_pnts[1]) ) {
|
||||
e = BRepBuilderAPI_MakeEdge(pnts[0], pnts[1]).Edge();
|
||||
}
|
||||
|
||||
if (isConic) {
|
||||
// Tiny circle segnments can cause issues later on, for example
|
||||
// when the comp curve is used as the sweeping directrix.
|
||||
double a, b;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(e, a, b);
|
||||
double radius = -1.;
|
||||
if (crv->DynamicType() == STANDARD_TYPE(Geom_Circle)) {
|
||||
radius = Handle(Geom_Circle)::DownCast(crv)->Radius();
|
||||
} else if (crv->DynamicType() == STANDARD_TYPE(Geom_Ellipse)) {
|
||||
// The formula above is for circles, but probably good enough
|
||||
radius = Handle(Geom_Ellipse)::DownCast(crv)->MajorRadius();
|
||||
}
|
||||
if (radius > 0. && deflection_for_approximating_circle(radius, b - a) < getValue(GV_PRECISION)) {
|
||||
TopoDS_Vertex v0, v1;
|
||||
TopExp::Vertices(e, v0, v1);
|
||||
e = TopoDS::Edge(BRepBuilderAPI_MakeEdge(v0, v1).Edge().Oriented(e.Orientation()));
|
||||
Logger::Warning("Subsituted edge with linear approximation", l);
|
||||
}
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
w.Add(e);
|
||||
|
||||
if (w.IsDone()) {
|
||||
wire = w.Wire();
|
||||
|
||||
// When SenseAgreement == .F. the vertices above have been reversed to
|
||||
// comply with the direction of conical curves. The ordering of the
|
||||
// vertices then still needs to be reversed in order to have begin and
|
||||
// end vertex consistent with IFC.
|
||||
if (sense_agreement != 0) { // .F.
|
||||
wire.Reverse();
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyline* l, TopoDS_Wire& result) {
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points = l->Points();
|
||||
|
||||
// Parse and store the points in a sequence
|
||||
TColgp_SequenceOfPnt polygon;
|
||||
for(IfcSchema::IfcCartesianPoint::list::it it = points->begin(); it != points->end(); ++ it) {
|
||||
gp_Pnt pnt;
|
||||
IfcGeom::Kernel::convert(*it, pnt);
|
||||
polygon.Append(pnt);
|
||||
}
|
||||
|
||||
const double eps = getValue(GV_PRECISION) * 10;
|
||||
const bool closed_by_proximity = polygon.Length() >= 3 && 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, closed_by_proximity, eps);
|
||||
|
||||
if (polygon.Length() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyLoop* l, TopoDS_Wire& result) {
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points = l->Polygon();
|
||||
|
||||
// Parse and store the points in a sequence
|
||||
TColgp_SequenceOfPnt polygon;
|
||||
for(IfcSchema::IfcCartesianPoint::list::it it = points->begin(); it != points->end(); ++ it) {
|
||||
gp_Pnt pnt;
|
||||
IfcGeom::Kernel::convert(*it, pnt);
|
||||
polygon.Append(pnt);
|
||||
}
|
||||
|
||||
// A loop should consist of at least three vertices
|
||||
int original_count = polygon.Length();
|
||||
if (original_count < 3) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Not enough edges for:", l);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove points that are too close to one another
|
||||
const double eps = getValue(GV_PRECISION) * 10;
|
||||
remove_duplicate_points_from_loop(polygon, true, eps);
|
||||
|
||||
int count = polygon.Length();
|
||||
if (original_count - count != 0) {
|
||||
std::stringstream ss; ss << (original_count - count) << " edges removed for:";
|
||||
Logger::Message(Logger::LOG_WARNING, ss.str(), l);
|
||||
}
|
||||
|
||||
if (count < 3) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Not enough edges for:", l);
|
||||
return false;
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakePolygon w;
|
||||
for (int i = 1; i <= polygon.Length(); ++i) {
|
||||
w.Add(polygon.Value(i));
|
||||
}
|
||||
w.Close();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryOpenProfileDef* l, TopoDS_Wire& result) {
|
||||
return convert_wire(l->Curve(), result);
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdgeCurve* l, TopoDS_Wire& result) {
|
||||
IfcSchema::IfcPoint* pnt1 = ((IfcSchema::IfcVertexPoint*) l->EdgeStart())->VertexGeometry();
|
||||
IfcSchema::IfcPoint* pnt2 = ((IfcSchema::IfcVertexPoint*) l->EdgeEnd())->VertexGeometry();
|
||||
if (!pnt1->declaration().is(IfcSchema::IfcCartesianPoint::Class()) || !pnt2->declaration().is(IfcSchema::IfcCartesianPoint::Class())) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Only IfcCartesianPoints are supported for VertexGeometry", l);
|
||||
return false;
|
||||
}
|
||||
|
||||
gp_Pnt p1, p2;
|
||||
if (!IfcGeom::Kernel::convert(((IfcSchema::IfcCartesianPoint*)pnt1), p1) ||
|
||||
!IfcGeom::Kernel::convert(((IfcSchema::IfcCartesianPoint*)pnt2), p2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeWire mw;
|
||||
Handle_Geom_Curve crv;
|
||||
|
||||
// The lack of a clear separation between topological and geometrical entities
|
||||
// is starting to get problematic. If the underlying curve is bounded it is
|
||||
// assumed that a topological wire can be crafted from it. After which an
|
||||
// attempt is made to reconstruct it from the individual curves and the vertices
|
||||
// of the IfcEdgeCurve.
|
||||
const bool is_bounded = l->EdgeGeometry()->declaration().is(IfcSchema::IfcBoundedCurve::Class());
|
||||
|
||||
if (!is_bounded && convert_curve(l->EdgeGeometry(), crv)) {
|
||||
BRepBuilderAPI_MakeEdge me(crv, p1, p2);
|
||||
if (!me.IsDone()) {
|
||||
return false;
|
||||
}
|
||||
mw.Add(me.Edge());
|
||||
result = mw;
|
||||
return true;
|
||||
} else if (is_bounded && convert_wire(l->EdgeGeometry(), result)) {
|
||||
if (!l->SameSense()) {
|
||||
result.Reverse();
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
TopExp_Explorer exp(result, TopAbs_EDGE);
|
||||
|
||||
while (exp.More()) {
|
||||
const TopoDS_Edge& ed = TopoDS::Edge(exp.Current());
|
||||
Standard_Real u1, u2;
|
||||
Handle(Geom_Curve) ecrv = BRep_Tool::Curve(ed, u1, u2);
|
||||
exp.Next();
|
||||
const bool last = !exp.More();
|
||||
|
||||
gp_Pnt a, b;
|
||||
|
||||
if (first && last) {
|
||||
a = p1;
|
||||
b = p2;
|
||||
} else if (first) {
|
||||
a = p1;
|
||||
ecrv->D0(u2, b);
|
||||
} else if (last) {
|
||||
ecrv->D0(u1, a);
|
||||
b = p2;
|
||||
} else {
|
||||
BRepBuilderAPI_MakeEdge me(ecrv, u1, u2);
|
||||
if (!me.IsDone()) {
|
||||
return false;
|
||||
}
|
||||
mw.Add(me.Edge());
|
||||
first = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
BRep_Builder builder;
|
||||
TopoDS_Vertex v1, v2;
|
||||
/// @todo project first and emit warnings accordingly
|
||||
builder.MakeVertex(v1, a, getValue(GV_PRECISION));
|
||||
builder.MakeVertex(v2, b, getValue(GV_PRECISION));
|
||||
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(ecrv, v1, v2));
|
||||
|
||||
first = false;
|
||||
}
|
||||
result = mw;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdgeLoop* l, TopoDS_Wire& result) {
|
||||
IfcSchema::IfcOrientedEdge::list::ptr li = l->EdgeList();
|
||||
BRepBuilderAPI_MakeWire mw;
|
||||
for (IfcSchema::IfcOrientedEdge::list::it it = li->begin(); it != li->end(); ++it) {
|
||||
TopoDS_Wire w;
|
||||
if (convert_wire(*it, w)) {
|
||||
mw.Add(TopoDS::Edge(TopoDS_Iterator(w).Value()));
|
||||
}
|
||||
}
|
||||
if (!mw.IsDone()) {
|
||||
return false;
|
||||
}
|
||||
result = mw.Wire();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdge* l, TopoDS_Wire& result) {
|
||||
if (!l->EdgeStart()->declaration().is(IfcSchema::IfcVertexPoint::Class()) || !l->EdgeEnd()->declaration().is(IfcSchema::IfcVertexPoint::Class())) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Only IfcVertexPoints are supported for EdgeStart and -End", l);
|
||||
return false;
|
||||
}
|
||||
|
||||
IfcSchema::IfcPoint* pnt1 = ((IfcSchema::IfcVertexPoint*) l->EdgeStart())->VertexGeometry();
|
||||
IfcSchema::IfcPoint* pnt2 = ((IfcSchema::IfcVertexPoint*) l->EdgeEnd())->VertexGeometry();
|
||||
if (!pnt1->declaration().is(IfcSchema::IfcCartesianPoint::Class()) || !pnt2->declaration().is(IfcSchema::IfcCartesianPoint::Class())) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Only IfcCartesianPoints are supported for VertexGeometry", l);
|
||||
return false;
|
||||
}
|
||||
|
||||
gp_Pnt p1, p2;
|
||||
if (!convert(((IfcSchema::IfcCartesianPoint*)pnt1), p1) ||
|
||||
!convert(((IfcSchema::IfcCartesianPoint*)pnt2), p2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeWire mw;
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(p1, p2));
|
||||
|
||||
result = mw.Wire();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcOrientedEdge* l, TopoDS_Wire& result) {
|
||||
if (convert_wire(l->EdgeElement(), result)) {
|
||||
if (!l->Orientation()) {
|
||||
result.Reverse();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcSubedge* l, TopoDS_Wire& result) {
|
||||
TopoDS_Wire temp;
|
||||
if (convert_wire(l->ParentEdge(), result) && convert((IfcSchema::IfcEdge*) l, temp)) {
|
||||
TopExp_Explorer exp(result, TopAbs_EDGE);
|
||||
TopoDS_Edge edge = TopoDS::Edge(exp.Current());
|
||||
Standard_Real u1, u2;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u1, u2);
|
||||
TopoDS_Vertex v1, v2;
|
||||
TopExp::Vertices(temp, v1, v2);
|
||||
BRepBuilderAPI_MakeWire mw;
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(crv, v1, v2));
|
||||
result = mw.Wire();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcIndexedPolyCurve
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcIndexedPolyCurve* l, TopoDS_Wire& result) {
|
||||
|
||||
IfcSchema::IfcCartesianPointList* point_list = l->Points();
|
||||
std::vector< std::vector<double> > coordinates;
|
||||
if (point_list->as<IfcSchema::IfcCartesianPointList2D>()) {
|
||||
coordinates = point_list->as<IfcSchema::IfcCartesianPointList2D>()->CoordList();
|
||||
} else if (point_list->as<IfcSchema::IfcCartesianPointList3D>()) {
|
||||
coordinates = point_list->as<IfcSchema::IfcCartesianPointList3D>()->CoordList();
|
||||
}
|
||||
|
||||
std::vector<gp_Pnt> points;
|
||||
points.reserve(coordinates.size());
|
||||
for (std::vector< std::vector<double> >::const_iterator it = coordinates.begin(); it != coordinates.end(); ++it) {
|
||||
const std::vector<double>& coords = *it;
|
||||
points.push_back(gp_Pnt(
|
||||
coords.size() < 1 ? 0. : coords[0] * getValue(GV_LENGTH_UNIT),
|
||||
coords.size() < 2 ? 0. : coords[1] * getValue(GV_LENGTH_UNIT),
|
||||
coords.size() < 3 ? 0. : coords[2] * getValue(GV_LENGTH_UNIT)));
|
||||
}
|
||||
|
||||
int max_index = points.size();
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
|
||||
if(l->hasSegments()) {
|
||||
IfcEntityList::ptr segments = l->Segments();
|
||||
for (IfcEntityList::it it = segments->begin(); it != segments->end(); ++it) {
|
||||
IfcUtil::IfcBaseClass* segment = *it;
|
||||
if (segment->declaration().is(IfcSchema::IfcLineIndex::Class())) {
|
||||
IfcSchema::IfcLineIndex* line = (IfcSchema::IfcLineIndex*) segment;
|
||||
std::vector<int> indices = *line;
|
||||
gp_Pnt previous;
|
||||
for (std::vector<int>::const_iterator jt = indices.begin(); jt != indices.end(); ++jt) {
|
||||
if (*jt < 1 || *jt > max_index) {
|
||||
throw IfcParse::IfcException("IfcIndexedPolyCurve index out of bounds for index " + boost::lexical_cast<std::string>(*jt));
|
||||
}
|
||||
const gp_Pnt& current = points[*jt - 1];
|
||||
if (jt != indices.begin()) {
|
||||
w.Add(BRepBuilderAPI_MakeEdge(previous, current));
|
||||
}
|
||||
previous = current;
|
||||
}
|
||||
} else if (segment->declaration().is(IfcSchema::IfcArcIndex::Class())) {
|
||||
IfcSchema::IfcArcIndex* arc = (IfcSchema::IfcArcIndex*) segment;
|
||||
std::vector<int> indices = *arc;
|
||||
if (indices.size() != 3) {
|
||||
throw IfcParse::IfcException("Invalid IfcArcIndex encountered");
|
||||
}
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const int& idx = indices[i];
|
||||
if (idx < 1 || idx > max_index) {
|
||||
throw IfcParse::IfcException("IfcIndexedPolyCurve index out of bounds for index " + boost::lexical_cast<std::string>(idx));
|
||||
}
|
||||
}
|
||||
const gp_Pnt& a = points[indices[0] - 1];
|
||||
const gp_Pnt& b = points[indices[1] - 1];
|
||||
const gp_Pnt& c = points[indices[2] - 1];
|
||||
Handle(Geom_Circle) circ = GC_MakeCircle(a, b, c).Value();
|
||||
w.Add(BRepBuilderAPI_MakeEdge(circ, a, c));
|
||||
} else {
|
||||
throw IfcParse::IfcException("Unexpected IfcIndexedPolyCurve segment of type " + segment->declaration().name());
|
||||
}
|
||||
}
|
||||
} else if (points.begin() < points.end()) {
|
||||
std::vector<gp_Pnt>::const_iterator previous = points.begin();
|
||||
for (std::vector<gp_Pnt>::const_iterator current = previous+1; current < points.end(); ++current){
|
||||
w.Add(BRepBuilderAPI_MakeEdge(*previous, *current));
|
||||
previous = current;
|
||||
}
|
||||
}
|
||||
|
||||
result = w.Wire();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -10,11 +10,13 @@
|
||||
namespace {
|
||||
// We bypass the conversion to gp_GTrsf, because it does not work
|
||||
void taxonomy_transform(const Eigen::Matrix4d* m, gp_XYZ& xyz) {
|
||||
Eigen::Vector4d v(xyz.X(), xyz.Y(), xyz.Z(), 1.0);
|
||||
auto v2 = (*m * v).eval();
|
||||
xyz.ChangeData()[0] = v2(0);
|
||||
xyz.ChangeData()[1] = v2(1);
|
||||
xyz.ChangeData()[2] = v2(2);
|
||||
if (m) {
|
||||
Eigen::Vector4d v(xyz.X(), xyz.Y(), xyz.Z(), 1.0);
|
||||
auto v2 = (*m * v).eval();
|
||||
xyz.ChangeData()[0] = v2(0);
|
||||
xyz.ChangeData()[1] = v2(1);
|
||||
xyz.ChangeData()[2] = v2(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,14 +25,17 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(const settings& setti
|
||||
// @todo remove duplication with OpenCascadeKernel::convert(const taxonomy::matrix4* matrix, gp_GTrsf& trsf);
|
||||
// above can be static?
|
||||
|
||||
const auto& m = *place.components;
|
||||
|
||||
// A 3x3 matrix to rotate the vertex normals
|
||||
gp_Mat rotation_matrix(
|
||||
m(0, 0), m(0, 1), m(0, 2),
|
||||
m(1, 0), m(1, 1), m(1, 2),
|
||||
m(2, 0), m(2, 1), m(2, 2)
|
||||
);
|
||||
boost::optional<gp_Mat> rotation_matrix;
|
||||
|
||||
if (place.components_) {
|
||||
const auto& m = *place.components_;
|
||||
rotation_matrix.emplace(
|
||||
m(0, 0), m(0, 1), m(0, 2),
|
||||
m(1, 0), m(1, 1), m(1, 2),
|
||||
m(2, 0), m(2, 1), m(2, 2)
|
||||
);
|
||||
}
|
||||
|
||||
// Triangulate the shape
|
||||
try {
|
||||
@@ -70,7 +75,7 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(const settings& setti
|
||||
|
||||
for (int i = 1; i <= nodes.Length(); ++i) {
|
||||
coords.push_back(nodes(i).Transformed(loc).XYZ());
|
||||
taxonomy_transform(place.components, *coords.rbegin());
|
||||
taxonomy_transform(place.components_, *coords.rbegin());
|
||||
const gp_XYZ& last = *coords.rbegin();
|
||||
dict[i] = t->addVertex(surface_style_id, last.X(), last.Y(), last.Z());
|
||||
|
||||
@@ -81,7 +86,11 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(const settings& setti
|
||||
prop.Normal(uv.X(), uv.Y(), p, normal_direction);
|
||||
gp_Vec normal(0., 0., 0.);
|
||||
if (normal_direction.Magnitude() > 1.e-9) {
|
||||
normal = gp_Dir(normal_direction.XYZ() * rotation_matrix);
|
||||
if (rotation_matrix) {
|
||||
normal = gp_Dir(normal_direction.XYZ() * *rotation_matrix);
|
||||
} else {
|
||||
normal = normal_direction;
|
||||
}
|
||||
}
|
||||
t->addNormal(normal.X(), normal.Y(), normal.Z());
|
||||
}
|
||||
|
||||
@@ -59,6 +59,18 @@ namespace ifcopenshell {
|
||||
|
||||
virtual bool is_manifold() const;
|
||||
|
||||
virtual double bounding_box(void*&) const {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
virtual int num_vertices() const {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
virtual void set_box(void*) {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
virtual int surface_genus() const;
|
||||
private:
|
||||
TopoDS_Shape shape_;
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "../../../ifcgeom/schema_agnostic/ifc_geom_api.h"
|
||||
|
||||
#include "../../../ifcgeom/taxonomy.h"
|
||||
#include "../../../ifcgeom/ConversionSettings.h"
|
||||
|
||||
// Define this in case you want to conserve memory usage at all cost. This has been
|
||||
// benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47
|
||||
@@ -74,24 +75,6 @@ namespace ifcopenshell {
|
||||
namespace geometry {
|
||||
namespace kernels {
|
||||
|
||||
class IFC_GEOM_API geometry_exception : public std::exception {
|
||||
protected:
|
||||
std::string message;
|
||||
public:
|
||||
geometry_exception(const std::string& m)
|
||||
: message(m) {}
|
||||
virtual ~geometry_exception() throw () {}
|
||||
virtual const char* what() const throw() {
|
||||
return message.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class IFC_GEOM_API too_many_faces_exception : public geometry_exception {
|
||||
public:
|
||||
too_many_faces_exception()
|
||||
: geometry_exception("Too many faces for operation") {}
|
||||
};
|
||||
|
||||
/*
|
||||
class IFC_GEOM_API POSTFIX_SCHEMA(Cache) {
|
||||
public:
|
||||
@@ -117,27 +100,7 @@ namespace kernels {
|
||||
double eps_;
|
||||
bool non_manifold_;
|
||||
|
||||
template <typename Fn>
|
||||
void loop_(const taxonomy::loop* ps, const Fn& callback) {
|
||||
if (ps->children.size() < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto a = boost::get<taxonomy::point3>(((taxonomy::edge*) ps->children.back())->start).instance;
|
||||
auto A = a->data().id();
|
||||
for (auto& b : ps->children) {
|
||||
auto B = boost::get<taxonomy::point3>(((taxonomy::edge*) b)->start).instance->data().id();
|
||||
auto C = vertex_mapping_[A], D = vertex_mapping_[B];
|
||||
bool fwd = C < D;
|
||||
if (!fwd) {
|
||||
std::swap(C, D);
|
||||
}
|
||||
if (C != D) {
|
||||
callback(C, D, fwd);
|
||||
A = B;
|
||||
}
|
||||
}
|
||||
}
|
||||
void loop_(const taxonomy::loop* ps, const std::function<void(int, int, bool)>& callback);
|
||||
public:
|
||||
faceset_helper(OpenCascadeKernel* kernel, const taxonomy::shell* l);
|
||||
|
||||
@@ -146,60 +109,11 @@ namespace kernels {
|
||||
bool non_manifold() const { return non_manifold_; }
|
||||
bool& non_manifold() { return non_manifold_; }
|
||||
|
||||
bool edge(const taxonomy::point3& a, const taxonomy::point3& b, TopoDS_Edge& e) {
|
||||
int A = vertex_mapping_[a.instance->data().id()];
|
||||
int B = vertex_mapping_[b.instance->data().id()];
|
||||
if (A == B) {
|
||||
return false;
|
||||
}
|
||||
bool edge(int A, int B, TopoDS_Edge& e);
|
||||
|
||||
return edge(A, B, e);
|
||||
}
|
||||
bool wire(const taxonomy::loop* loop, TopoDS_Wire& wire);
|
||||
|
||||
bool edge(int A, int B, TopoDS_Edge& e) {
|
||||
auto it = edges_.find({ A, B });
|
||||
if (it == edges_.end()) {
|
||||
return false;
|
||||
}
|
||||
e = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wire(const taxonomy::loop* loop, TopoDS_Wire& wire) {
|
||||
if (duplicates_.find(loop->instance->data().id()) != duplicates_.end()) {
|
||||
return false;
|
||||
}
|
||||
BRep_Builder builder;
|
||||
builder.MakeWire(wire);
|
||||
int count = 0;
|
||||
loop_(loop, [this, &builder, &wire, &count](int A, int B, bool fwd) {
|
||||
TopoDS_Edge e;
|
||||
if (edge(A, B, e)) {
|
||||
if (!fwd) {
|
||||
e.Reverse();
|
||||
}
|
||||
builder.Add(wire, e);
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
if (count >= 3) {
|
||||
wire.Closed(true);
|
||||
|
||||
/*
|
||||
@todo
|
||||
TopTools_ListOfShape results;
|
||||
if (kernel_->wire_intersections(wire, results)) {
|
||||
Logger::Warning("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected", loop);
|
||||
kernel_->select_largest(results, wire);
|
||||
non_manifold_ = true;
|
||||
}
|
||||
*/
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool wires(const taxonomy::loop* loop, TopTools_ListOfShape& wires);
|
||||
|
||||
double epsilon() const {
|
||||
return eps_;
|
||||
@@ -213,17 +127,15 @@ namespace kernels {
|
||||
*/
|
||||
|
||||
faceset_helper* faceset_helper_;
|
||||
double precision_;
|
||||
|
||||
public:
|
||||
OpenCascadeKernel()
|
||||
: AbstractKernel("opencascade")
|
||||
OpenCascadeKernel(ConversionSettings& settings)
|
||||
: AbstractKernel("opencascade", settings)
|
||||
, faceset_helper_(nullptr)
|
||||
// @todo
|
||||
, precision_(1.e-5) {}
|
||||
{}
|
||||
|
||||
OpenCascadeKernel(const OpenCascadeKernel& other)
|
||||
: AbstractKernel("opencascade") {
|
||||
: AbstractKernel("opencascade", other.settings_) {
|
||||
*this = other;
|
||||
}
|
||||
|
||||
@@ -240,8 +152,6 @@ namespace kernels {
|
||||
bool convert(const taxonomy::matrix4*, gp_GTrsf&);
|
||||
bool convert(const taxonomy::shell*, TopoDS_Shape&);
|
||||
|
||||
bool approximate_plane_through_wire(const TopoDS_Wire& wire, gp_Pln& plane, double eps = -1.);
|
||||
bool triangulate_wire(const std::vector<TopoDS_Wire>& wires, TopTools_ListOfShape& faces);
|
||||
bool boolean_operation(const TopoDS_Shape& a_, const TopTools_ListOfShape& b__, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness = -1.);
|
||||
const TopoDS_Shape& ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid);
|
||||
bool flatten_shape_list(const ifcopenshell::geometry::ConversionResults& shapes, TopoDS_Shape& result, bool fuse);
|
||||
@@ -262,6 +172,18 @@ namespace kernels {
|
||||
IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(serialise_)(const TopoDS_Shape& shape, bool advanced);
|
||||
*/
|
||||
|
||||
template <typename T, typename U>
|
||||
T convert_xyz(const U& u) {
|
||||
const auto& vs = u.ccomponents();
|
||||
return T(vs(0), vs(1), vs(2));
|
||||
}
|
||||
|
||||
// @todo eliminate
|
||||
template <typename T, typename U>
|
||||
T convert_xyz2(const U& vs) {
|
||||
return T(vs(0), vs(1), vs(2));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,804 @@
|
||||
#include "boolean_utils.h"
|
||||
|
||||
#include "IfcGeomTree.h"
|
||||
|
||||
#include <BRepBuilderAPI_Copy.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <BRepGProp.hxx>
|
||||
#include <TopExp.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <Bnd_Box.hxx>
|
||||
#include <Extrema_ExtPC.hxx>
|
||||
#include <Geom_Plane.hxx>
|
||||
#include <Geom_BSplineCurve.hxx>
|
||||
#include <ShapeUpgrade_UnifySameDomain.hxx>
|
||||
#include <GeomAPI_ExtremaCurveCurve.hxx>
|
||||
#include <ShapeAnalysis_Surface.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <Standard_Version.hxx>
|
||||
#include <BRepAdaptor_Curve.hxx>
|
||||
#include <BRepExtrema_DistShapeShape.hxx>
|
||||
#include <BRepBndLib.hxx>
|
||||
|
||||
#include <vector>
|
||||
|
||||
void ifcopenshell::geometry::util::copy_operand(const TopTools_ListOfShape & l, TopTools_ListOfShape & r) {
|
||||
#if OCC_VERSION_HEX < 0x70000
|
||||
r.Clear();
|
||||
TopTools_ListIteratorOfListOfShape it(l);
|
||||
for (; it.More(); it.Next()) {
|
||||
r.Append(BRepBuilderAPI_Copy(it.Value()));
|
||||
}
|
||||
#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.
|
||||
r.Assign(l);
|
||||
#endif
|
||||
}
|
||||
|
||||
TopoDS_Shape ifcopenshell::geometry::util::copy_operand(const TopoDS_Shape & s) {
|
||||
#if OCC_VERSION_HEX < 0x70000
|
||||
return BRepBuilderAPI_Copy(s);
|
||||
#else
|
||||
return s;
|
||||
#endif
|
||||
}
|
||||
|
||||
double ifcopenshell::geometry::util::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()) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(exp.Current());
|
||||
|
||||
TopoDS_Vertex v0, v1;
|
||||
TopExp::Vertices(e, v0, v1);
|
||||
if (!v0.IsNull() && !v1.IsNull() && v0.IsSame(v1)) {
|
||||
// Don't consider a 3d-degenerate edge (for example cone apex)
|
||||
// in calculating overall shape min edge length.
|
||||
continue;
|
||||
}
|
||||
|
||||
GProp_GProps prop;
|
||||
BRepGProp::LinearProperties(e, prop);
|
||||
double l = prop.Mass();
|
||||
if (l < min_edge_len) {
|
||||
min_edge_len = l;
|
||||
}
|
||||
}
|
||||
return min_edge_len;
|
||||
}
|
||||
|
||||
double ifcopenshell::geometry::util::min_vertex_edge_distance(const TopoDS_Shape & a, double min_search, double max_search) {
|
||||
double M = std::numeric_limits<double>::infinity();
|
||||
|
||||
TopTools_IndexedMapOfShape vertices, edges;
|
||||
|
||||
TopExp::MapShapes(a, TopAbs_VERTEX, vertices);
|
||||
TopExp::MapShapes(a, TopAbs_EDGE, edges);
|
||||
|
||||
impl::tree<int> tree;
|
||||
|
||||
// Add edges to tree
|
||||
for (int i = 1; i <= edges.Extent(); ++i) {
|
||||
tree.add(i, edges(i));
|
||||
}
|
||||
|
||||
for (int j = 1; j <= vertices.Extent(); ++j) {
|
||||
const TopoDS_Vertex& v = TopoDS::Vertex(vertices(j));
|
||||
gp_Pnt p = BRep_Tool::Pnt(v);
|
||||
|
||||
Bnd_Box b;
|
||||
b.Add(p);
|
||||
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) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(edges(*it));
|
||||
TopoDS_Vertex v1, v2;
|
||||
TopExp::Vertices(e, v1, v2);
|
||||
|
||||
if (v.IsSame(v1) || v.IsSame(v2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BRepAdaptor_Curve crv(e);
|
||||
Extrema_ExtPC ext(p, crv);
|
||||
if (!ext.IsDone()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 1; i <= ext.NbExt(); ++i) {
|
||||
const double m = sqrt(ext.SquareDistance(i));
|
||||
if (m < M && m > min_search) {
|
||||
M = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return M;
|
||||
}
|
||||
|
||||
bool ifcopenshell::geometry::util::faces_overlap(const TopoDS_Face & f, const TopoDS_Face & g) {
|
||||
points_on_planar_face_generator pgen(f);
|
||||
|
||||
BRep_Builder B;
|
||||
gp_Pnt test;
|
||||
double eps = BRep_Tool::Tolerance(f) + BRep_Tool::Tolerance(g);
|
||||
|
||||
BRepExtrema_DistShapeShape x;
|
||||
x.LoadS1(g);
|
||||
|
||||
while (pgen(test)) {
|
||||
TopoDS_Vertex V;
|
||||
B.MakeVertex(V, test, Precision::Confusion());
|
||||
x.LoadS2(V);
|
||||
x.Perform();
|
||||
if (x.IsDone() && x.NbSolution() == 1) {
|
||||
if (x.Value() > eps) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double ifcopenshell::geometry::util::min_face_face_distance(const TopoDS_Shape & a, double max_search) {
|
||||
/*
|
||||
NB: This is currently only implemented for planar surfaces.
|
||||
*/
|
||||
double M = std::numeric_limits<double>::infinity();
|
||||
|
||||
TopTools_IndexedMapOfShape faces;
|
||||
|
||||
TopExp::MapShapes(a, TopAbs_FACE, faces);
|
||||
|
||||
impl::tree<int> 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));
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 1; j <= faces.Extent(); ++j) {
|
||||
const TopoDS_Face& f = TopoDS::Face(faces(j));
|
||||
const Handle(Geom_Surface)& fs = BRep_Tool::Surface(f);
|
||||
|
||||
if (fs->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
points_on_planar_face_generator pgen(f);
|
||||
|
||||
Bnd_Box b;
|
||||
BRepBndLib::AddClose(f, b);
|
||||
b.Enlarge(max_search);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const TopoDS_Face& g = TopoDS::Face(faces(*it));
|
||||
const Handle(Geom_Surface)& gs = BRep_Tool::Surface(g);
|
||||
|
||||
auto p0 = Handle(Geom_Plane)::DownCast(fs);
|
||||
auto p1 = Handle(Geom_Plane)::DownCast(gs);
|
||||
|
||||
if (p0->Position().IsCoplanar(p1->Position(), max_search, asin(max_search))) {
|
||||
pgen.reset();
|
||||
|
||||
BRepTopAdaptor_FClass2d cls(g, BRep_Tool::Tolerance(g));
|
||||
|
||||
gp_Pnt test;
|
||||
while (pgen(test)) {
|
||||
gp_Vec d = test.XYZ() - p1->Position().Location().XYZ();
|
||||
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);
|
||||
double w = std::abs(gp_Vec(p1->Position().Direction().XYZ()).Dot(test2.XYZ() - test.XYZ()));
|
||||
if (w < M) {
|
||||
M = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return M;
|
||||
}
|
||||
|
||||
int ifcopenshell::geometry::util::bounding_box_overlap(double p, const TopoDS_Shape & a, const TopTools_ListOfShape & b, TopTools_ListOfShape & c) {
|
||||
int N = 0;
|
||||
|
||||
Bnd_Box A;
|
||||
BRepBndLib::Add(a, A);
|
||||
|
||||
if (A.IsVoid()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TopTools_ListIteratorOfListOfShape it(b);
|
||||
for (; it.More(); it.Next()) {
|
||||
Bnd_Box B;
|
||||
BRepBndLib::Add(it.Value(), B);
|
||||
|
||||
if (B.IsVoid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (A.Distance(B) < p) {
|
||||
c.Append(it.Value());
|
||||
} else {
|
||||
++N;
|
||||
}
|
||||
}
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
bool ifcopenshell::geometry::util::get_edge_axis(const TopoDS_Edge & e, gp_Ax1 & ax) {
|
||||
double _, __;
|
||||
|
||||
auto crv = BRep_Tool::Curve(e, _, __);
|
||||
auto line = Handle_Geom_Line::DownCast(crv);
|
||||
auto bsple = Handle_Geom_BSplineCurve::DownCast(crv);
|
||||
|
||||
if (line) {
|
||||
ax = line->Position();
|
||||
return true;
|
||||
} else if (bsple) {
|
||||
if (bsple->NbPoles() == 2 && bsple->Degree() == 1) {
|
||||
gp_Dir V(bsple->Poles().Last().XYZ() - bsple->Poles().First().XYZ());
|
||||
ax = gp_Ax1(bsple->Poles().First(), V);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ifcopenshell::geometry::util::is_subset(const TopTools_IndexedMapOfShape & lhs, const TopTools_IndexedMapOfShape & rhs) {
|
||||
if (rhs.Extent() < lhs.Extent()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < lhs.Extent(); ++i) {
|
||||
auto& s = lhs.FindKey(i);
|
||||
if (!rhs.Contains(s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ifcopenshell::geometry::util::is_extrusion(const gp_Vec & v, const TopoDS_Shape & s, TopoDS_Face & base, std::pair<double, double>& interval) {
|
||||
// This assumes UnifySameDomain has been processed on s, so that
|
||||
// the extrusion top and bottom are a single face.
|
||||
|
||||
TopTools_IndexedDataMapOfShapeListOfShape mapping;
|
||||
TopExp::MapShapesAndAncestors(s, TopAbs_EDGE, TopAbs_FACE, mapping);
|
||||
TopExp::MapShapesAndAncestors(s, TopAbs_VERTEX, TopAbs_FACE, mapping);
|
||||
|
||||
TopTools_ListOfShape parallel;
|
||||
TopTools_IndexedMapOfShape curved_orthogonal;
|
||||
gp_Ax1 ax;
|
||||
gp_Ax1 V(gp::Origin(), v);
|
||||
|
||||
// Segment edges in parallel to extrusion direction, and orthogonal or curved,
|
||||
// where the latter two categories have to make the edges part of the base or
|
||||
// top face. When neither of these categories the shape is not a extrusion
|
||||
// or the extrusion direction is not orthogonal to its basis.
|
||||
for (int i = 1; i < mapping.Extent(); ++i) {
|
||||
auto& s = mapping.FindKey(i);
|
||||
if (s.ShapeType() != TopAbs_EDGE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// @todo use a linear tolernace and the face extrimities, see #2218
|
||||
const TopoDS_Edge& e = TopoDS::Edge(s);
|
||||
if (!get_edge_axis(e, ax)) {
|
||||
// curved
|
||||
curved_orthogonal.Add(e);
|
||||
} else if (ax.IsParallel(V, 1.e-7)) {
|
||||
parallel.Append(e);
|
||||
} else if (ax.IsNormal(V, 1.e-7)) {
|
||||
// ortho
|
||||
curved_orthogonal.Add(e);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Select the two faces for which their edges are subsets
|
||||
// of the ortho/curved edges
|
||||
TopTools_IndexedMapOfShape ortho_faces;
|
||||
for (TopExp_Explorer exp(s, TopAbs_FACE); exp.More(); exp.Next()) {
|
||||
TopTools_IndexedMapOfShape face_edges;
|
||||
TopExp::MapShapes(exp.Current(), TopAbs_EDGE, face_edges);
|
||||
if (is_subset(face_edges, curved_orthogonal)) {
|
||||
ortho_faces.Add(exp.Current());
|
||||
}
|
||||
}
|
||||
|
||||
// There should be a basis and top face
|
||||
if (ortho_faces.Extent() != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For the parallel edges assert that its two vertices are part
|
||||
// of both the basis and the top face.
|
||||
for (TopTools_ListIteratorOfListOfShape it(parallel);
|
||||
it.More(); it.Next()) {
|
||||
TopoDS_Vertex v01[2];
|
||||
TopExp::Vertices(TopoDS::Edge(it.Value()), v01[0], v01[1]);
|
||||
|
||||
TopTools_IndexedMapOfShape v_ortho_faces;
|
||||
int nb_ortho_faces[2] = { 0,0 };
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
auto& faces = mapping.FindFromKey(v01[i]);
|
||||
|
||||
for (TopTools_ListIteratorOfListOfShape jt(faces);
|
||||
jt.More(); jt.Next()) {
|
||||
if (ortho_faces.Contains(jt.Value())) {
|
||||
nb_ortho_faces[i] ++;
|
||||
v_ortho_faces.Add(jt.Value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool sets_equal = v_ortho_faces.Size() == ortho_faces.Size() && is_subset(v_ortho_faces, ortho_faces);
|
||||
if (!sets_equal) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Assert the base/top faces are planar and get the interval
|
||||
// (dot products along axis) for which the extrusion is defined
|
||||
// If necessary swap the two faces so that the basis face has
|
||||
// the smallest dot product along the axis.
|
||||
auto f0 = TopoDS::Face(ortho_faces.FindKey(1));
|
||||
auto f1 = TopoDS::Face(ortho_faces.FindKey(2));
|
||||
|
||||
const Handle(Geom_Surface)& f0_s = BRep_Tool::Surface(f0);
|
||||
const Handle(Geom_Surface)& f1_s = BRep_Tool::Surface(f1);
|
||||
|
||||
auto p0 = Handle(Geom_Plane)::DownCast(f0_s);
|
||||
auto p1 = Handle(Geom_Plane)::DownCast(f1_s);
|
||||
|
||||
if (p0.IsNull() || p1.IsNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto dot0 = p0->Location().XYZ().Dot(v.XYZ());
|
||||
auto dot1 = p1->Location().XYZ().Dot(v.XYZ());
|
||||
|
||||
if (dot0 > dot1) {
|
||||
std::swap(dot0, dot1);
|
||||
std::swap(f0, f1);
|
||||
}
|
||||
|
||||
base = f0;
|
||||
interval = { dot0, dot1 };
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ifcopenshell::geometry::util::eliminate_touching_operands(double prec, const TopoDS_Shape & a, const TopTools_ListOfShape & bs, TopTools_ListOfShape & c) {
|
||||
TopTools_IndexedMapOfShape a_faces;
|
||||
TopExp::MapShapes(a, TopAbs_FACE, a_faces);
|
||||
|
||||
// Check if any of the faces in a are non-planar, which is
|
||||
// not supported by this quick check.
|
||||
for (int i = 1; i <= a_faces.Extent(); ++i) {
|
||||
auto surf = BRep_Tool::Surface(TopoDS::Face(a_faces(i)));
|
||||
if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
TopTools_IndexedMapOfShape a_vertices;
|
||||
TopExp::MapShapes(a, TopAbs_VERTEX, a_vertices);
|
||||
|
||||
ifcopenshell::geometry::impl::tree<int> tree;
|
||||
|
||||
// Add faces to tree
|
||||
for (int i = 1; i <= a_faces.Extent(); ++i) {
|
||||
tree.add(i, a_faces(i));
|
||||
}
|
||||
|
||||
int N = 0;
|
||||
|
||||
TopTools_ListIteratorOfListOfShape it(bs);
|
||||
for (; it.More(); it.Next()) {
|
||||
bool is_touching = false;
|
||||
|
||||
auto& b = it.Value();
|
||||
|
||||
TopTools_IndexedMapOfShape b_faces;
|
||||
TopExp::MapShapes(b, TopAbs_FACE, b_faces);
|
||||
|
||||
// Check if any of the faces in b are non-planar, which is
|
||||
// not supported by this quick check.
|
||||
for (int i = 1; i <= b_faces.Extent(); ++i) {
|
||||
auto surf = BRep_Tool::Surface(TopoDS::Face(b_faces(i)));
|
||||
if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
TopTools_IndexedMapOfShape b_vertices;
|
||||
TopExp::MapShapes(b, TopAbs_VERTEX, b_vertices);
|
||||
|
||||
for (int k = 1; k <= b_faces.Extent(); ++k) {
|
||||
const TopoDS_Face& f_b = TopoDS::Face(b_faces(k));
|
||||
Bnd_Box B;
|
||||
BRepBndLib::Add(f_b, B);
|
||||
|
||||
// Query tree using b_face bounding box
|
||||
for (auto& i : tree.select_box(B, false)) {
|
||||
const TopoDS_Face& f_a = TopoDS::Face(a_faces(i));
|
||||
|
||||
TopTools_IndexedMapOfShape f_a_vertices;
|
||||
TopExp::MapShapes(f_a, TopAbs_VERTEX, f_a_vertices);
|
||||
|
||||
BRepGProp_Face prop_a(f_a);
|
||||
BRepGProp_Face prop_b(f_b);
|
||||
|
||||
gp_Pnt p_a, p_b;
|
||||
gp_Vec v_a, v_b;
|
||||
|
||||
double u0, u1, v0, v1;
|
||||
prop_a.Bounds(u0, u1, v0, v1);
|
||||
prop_a.Normal((u0 + u1) / 2., (u0 + u1) / 2., p_a, v_a);
|
||||
|
||||
prop_b.Bounds(u0, u1, v0, v1);
|
||||
prop_b.Normal((u0 + u1) / 2., (u0 + u1) / 2., p_b, v_b);
|
||||
|
||||
bool all_vertices_behind_f_a = true;
|
||||
|
||||
// Check if all 'other' vertices in a are pointing
|
||||
// away from the face in a, so that there is no geometry
|
||||
// from a in front of the face that could participate
|
||||
// in the boolean subtraction.
|
||||
for (int j = 1; j <= a_vertices.Extent(); ++j) {
|
||||
if (!f_a_vertices.Contains(a_vertices(j))) {
|
||||
auto p = BRep_Tool::Pnt(TopoDS::Vertex(a_vertices(j)));
|
||||
if ((p.XYZ() - p_a.XYZ()).Dot(v_a.XYZ()) > prec) {
|
||||
all_vertices_behind_f_a = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!all_vertices_behind_f_a) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if surface normals are opposite
|
||||
if (v_a.IsOpposite(v_b, 1.e-5)) {
|
||||
// Check if faces are co-planar
|
||||
if ((p_b.XYZ() - p_a.XYZ()).Dot(v_a.XYZ()) <= prec) {
|
||||
|
||||
TopTools_IndexedMapOfShape f_b_vertices;
|
||||
TopExp::MapShapes(f_b, TopAbs_VERTEX, f_b_vertices);
|
||||
|
||||
bool all_vertices_behind_f_b = true;
|
||||
|
||||
// Check if all 'other' vertices in b are pointing
|
||||
// away from the face in a. So that a boolean subtraction
|
||||
// would not alter a.
|
||||
for (int j = 1; j <= b_vertices.Extent(); ++j) {
|
||||
if (!f_b_vertices.Contains(b_vertices(j))) {
|
||||
auto p = BRep_Tool::Pnt(TopoDS::Vertex(b_vertices(j)));
|
||||
if ((p.XYZ() - p_a.XYZ()).Dot(v_a.XYZ()) < prec * 10.) {
|
||||
all_vertices_behind_f_b = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (all_vertices_behind_f_b) {
|
||||
is_touching = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_touching) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_touching) {
|
||||
c.Append(it.Value());
|
||||
} else {
|
||||
++N;
|
||||
}
|
||||
}
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
TopoDS_Shape ifcopenshell::geometry::util::unify(const TopoDS_Shape & s, double tolerance) {
|
||||
tolerance = (std::min)(min_edge_length(s) / 2., tolerance);
|
||||
ShapeUpgrade_UnifySameDomain usd(s);
|
||||
#if OCC_VERSION_HEX >= 0x70200
|
||||
usd.SetSafeInputMode(true);
|
||||
#endif
|
||||
#if OCC_VERSION_HEX >= 0x70100
|
||||
usd.SetLinearTolerance(tolerance);
|
||||
usd.SetAngularTolerance(1.e-3);
|
||||
#endif
|
||||
usd.Build();
|
||||
return usd.Shape();
|
||||
}
|
||||
|
||||
bool ifcopenshell::geometry::util::boolean_subtraction_2d_using_builder(const TopoDS_Shape & a_input, const TopTools_ListOfShape & b_input, TopoDS_Shape & result, double eps) {
|
||||
ifcopenshell::geometry::impl::tree<int> edge_tree;
|
||||
|
||||
TopTools_ListOfShape ab_input = b_input;
|
||||
ab_input.Prepend(a_input);
|
||||
|
||||
TopTools_ListIteratorOfListOfShape it(ab_input);
|
||||
int shape_index = 0;
|
||||
int edge_index = 0;
|
||||
std::map<int, int> edge_index_to_shape_index;
|
||||
|
||||
std::vector<TopoDS_Shape> shapes;
|
||||
std::vector<std::pair<size_t, TopoDS_Edge>> edges;
|
||||
// First is the outer wire
|
||||
std::vector<TopoDS_Wire> wires;
|
||||
|
||||
for (; it.More(); it.Next(), ++shape_index) {
|
||||
if (it.Value().ShapeType() != TopAbs_FACE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const TopoDS_Face& f = TopoDS::Face(it.Value());
|
||||
TopoDS_Wire outer_wire;
|
||||
|
||||
if (shape_index == 0) {
|
||||
outer_wire = BRepTools::OuterWire(f);
|
||||
wires.push_back(outer_wire);
|
||||
}
|
||||
|
||||
size_t num_wires = 0;
|
||||
TopoDS_Iterator it2(it.Value());
|
||||
for (; it2.More(); it2.Next()) {
|
||||
++num_wires;
|
||||
|
||||
if (outer_wire.IsNull() || !it2.Value().IsSame(outer_wire)) {
|
||||
wires.push_back(TopoDS::Wire(it2.Value()));
|
||||
|
||||
if (shape_index == 0 && num_wires > 0) {
|
||||
// An inner wire on the first operand face: reverse, because
|
||||
// MakeFace expects inner boundaries to be added as bounded
|
||||
// areas.
|
||||
wires.back().Reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (num_wires > 1 && shape_index != 0) {
|
||||
// The first operand can have inner wires, but the others
|
||||
// can't because a inner wire would result in an additional
|
||||
// outer wire for the result.
|
||||
return false;
|
||||
}
|
||||
|
||||
shapes.push_back(it.Value());
|
||||
TopExp_Explorer exp(it.Value(), TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next(), ++edge_index) {
|
||||
edge_tree.add(edge_index, exp.Current());
|
||||
edge_index_to_shape_index[edge_index] = shape_index;
|
||||
edges.push_back({ shape_index, TopoDS::Edge(exp.Current()) });
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
TopoDS_Compound C;
|
||||
BRep_Builder BB;
|
||||
BB.MakeCompound(C);
|
||||
|
||||
for (auto& w : wires) {
|
||||
BB.Add(C, w);
|
||||
}
|
||||
|
||||
BRepTools::Write(C, "debug.brep");
|
||||
}
|
||||
|
||||
shape_index = 0;
|
||||
edge_index = 0;
|
||||
|
||||
it.Initialize(ab_input);
|
||||
for (; it.More(); it.Next(), ++shape_index) {
|
||||
TopExp_Explorer exp(it.Value(), TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next(), ++edge_index) {
|
||||
Bnd_Box b;
|
||||
BRepBndLib::Add(exp.Current(), b);
|
||||
b.Enlarge(eps);
|
||||
|
||||
for (auto& i : edge_tree.select_box(b)) {
|
||||
if (i == edge_index) {
|
||||
// Skip self-selection
|
||||
continue;
|
||||
}
|
||||
|
||||
if (edges[i].first == shape_index) {
|
||||
// Skip edges of the same operand
|
||||
continue;
|
||||
}
|
||||
|
||||
const TopoDS_Edge& e0 = TopoDS::Edge(exp.Current());
|
||||
const TopoDS_Edge& e1 = edges[i].second;
|
||||
|
||||
double u11, u12, u21, u22, U1, U2;
|
||||
|
||||
GeomAPI_ExtremaCurveCurve ecc(
|
||||
BRep_Tool::Curve(e0, u11, u12),
|
||||
BRep_Tool::Curve(e1, u21, u22)
|
||||
);
|
||||
|
||||
// @todo: extend this to work in case of multiple extrema and curved segments.
|
||||
const bool unbounded_intersects = (!ecc.Extrema().IsParallel() && ecc.NbExtrema() == 1 && ecc.Distance(1) < eps);
|
||||
if (unbounded_intersects) {
|
||||
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;
|
||||
|
||||
if (u11 < U1 && U1 < u12 && u21 < U2 && U2 < u22) {
|
||||
// Edge curves belonging to different operands intersect, don't process
|
||||
// using builder.
|
||||
Logger::Notice("Intersecting boundaries");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only inner wires are considered that are directly contained in the outer wire
|
||||
// Redundant subtractions are eliminated.
|
||||
|
||||
std::vector<bool> redundant(wires.size(), false);
|
||||
|
||||
std::vector<TopoDS_Face> wire_faces;
|
||||
wire_faces.reserve(wires.size());
|
||||
|
||||
std::vector<BRepTopAdaptor_FClass2d> wire_clss;
|
||||
wire_clss.reserve(wires.size());
|
||||
|
||||
std::vector<std::unique_ptr<ShapeAnalysis_Surface>> sass;
|
||||
sass.reserve(wires.size());
|
||||
|
||||
for (auto& w : wires) {
|
||||
wire_faces.push_back(BRepBuilderAPI_MakeFace(w).Face());
|
||||
wire_clss.emplace_back(wire_faces.back(), eps);
|
||||
sass.push_back(std::make_unique<ShapeAnalysis_Surface>(BRep_Tool::Surface(wire_faces.back())));
|
||||
}
|
||||
|
||||
// First check for containment in outer wire
|
||||
for (auto it = ++wires.begin(); it != wires.end(); ++it) {
|
||||
// Considering a single vertex is sufficient because we have already
|
||||
// guaranteed that the edges of different operands do not cross.
|
||||
TopoDS_Iterator it_ed(*it);
|
||||
auto& ed = it_ed.Value();
|
||||
|
||||
TopoDS_Iterator it_v(ed);
|
||||
auto& v = TopoDS::Vertex(it_v.Value());
|
||||
|
||||
auto pnt = BRep_Tool::Pnt(v);
|
||||
auto p2d = sass[0]->ValueOfUV(pnt, eps);
|
||||
if (wire_clss[0].Perform(p2d) != TopAbs_IN) {
|
||||
// A wire is not contained in the outer wire, it's a subtraction without
|
||||
// any effect and marked as redundant. Feeding it to the builder algo
|
||||
// will likely cause problems.
|
||||
redundant[std::distance(wires.begin(), it)] = true;
|
||||
Logger::Notice("Subtraction operand outside of outer bound");
|
||||
}
|
||||
}
|
||||
|
||||
// Now build a tree to find inner wires contained in other inner wires
|
||||
// NB first wire is *not* in this tree
|
||||
ifcopenshell::geometry::impl::tree<int> wire_tree;
|
||||
for (size_t wire_index = 1; wire_index < wires.size(); ++wire_index) {
|
||||
wire_tree.add(wire_index, wires[wire_index]);
|
||||
}
|
||||
|
||||
for (size_t wire_index = 1; wire_index < wires.size(); ++wire_index) {
|
||||
Bnd_Box b;
|
||||
BRepBndLib::Add(wires[wire_index], b);
|
||||
b.Enlarge(eps);
|
||||
|
||||
// We're only selecting operands completely within b because we
|
||||
// have already guaranteed they do not intersect. So they are
|
||||
// either fully in or out. Selecting with complete_within=true
|
||||
// will filter out some unnecessary cases. It also means we need
|
||||
// that due this asymmetry we need to process all pairs of wire
|
||||
// indices and not just the pairs where the first element is less
|
||||
// than the second element.
|
||||
for (auto& other_index : wire_tree.select_box(b, true)) {
|
||||
// other_index is fully contained in wire_index
|
||||
if (wire_index == other_index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TopoDS_Iterator it_ed(wires[other_index]);
|
||||
auto& ed = it_ed.Value();
|
||||
|
||||
TopoDS_Iterator it_v(ed);
|
||||
auto& v = TopoDS::Vertex(it_v.Value());
|
||||
|
||||
auto pnt = BRep_Tool::Pnt(v);
|
||||
auto p2d = sass[wire_index]->ValueOfUV(pnt, eps);
|
||||
if (wire_clss[wire_index].Perform(p2d) == TopAbs_IN) {
|
||||
// A wire is contained within another operand
|
||||
redundant[other_index] = true;
|
||||
Logger::Notice("Subtraction operand contained in other");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeFace mf(wire_faces[0]);
|
||||
for (size_t wire_index = 1; wire_index < wires.size(); ++wire_index) {
|
||||
if (!redundant[wire_index]) {
|
||||
mf.Add(TopoDS::Wire(wires[wire_index].Reversed()));
|
||||
}
|
||||
}
|
||||
result = mf.Face();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ifcopenshell::geometry::util::points_on_planar_face_generator::reset() {
|
||||
i = j = (int)inset_;
|
||||
}
|
||||
|
||||
bool ifcopenshell::geometry::util::points_on_planar_face_generator::operator()(gp_Pnt& p) {
|
||||
while (j < N) {
|
||||
double u = u0 + (u1 - u0) * i / N;
|
||||
double v = v0 + (v1 - v0) * j / N;
|
||||
|
||||
i++;
|
||||
if (i == N) {
|
||||
i = 0;
|
||||
j++;
|
||||
}
|
||||
|
||||
// Specifically does not consider ON
|
||||
if (cls_.Perform(gp_Pnt2d(u, v)) == TopAbs_IN) {
|
||||
plane_->D0(u, v, p);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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 BOOLEAN_UTILS_H
|
||||
#define BOOLEAN_UTILS_H
|
||||
|
||||
#include <TopoDS_Shape.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
#include <Geom_Surface.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <BRepTopAdaptor_FClass2d.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepTools.hxx>
|
||||
#include <TopTools_IndexedMapOfShape.hxx>
|
||||
|
||||
namespace ifcopenshell { namespace geometry {
|
||||
namespace util {
|
||||
|
||||
void copy_operand(const TopTools_ListOfShape& l, TopTools_ListOfShape& r);
|
||||
|
||||
TopoDS_Shape copy_operand(const TopoDS_Shape& s);
|
||||
|
||||
double min_edge_length(const TopoDS_Shape& a);
|
||||
|
||||
double min_vertex_edge_distance(const TopoDS_Shape& a, double min_search, double max_search);
|
||||
|
||||
class points_on_planar_face_generator {
|
||||
private:
|
||||
const TopoDS_Face& f_;
|
||||
Handle(Geom_Surface) plane_;
|
||||
BRepTopAdaptor_FClass2d cls_;
|
||||
double u0, u1, v0, v1;
|
||||
int i, j;
|
||||
bool inset_;
|
||||
static const int N = 10;
|
||||
|
||||
public:
|
||||
points_on_planar_face_generator(const TopoDS_Face& f, bool inset = false)
|
||||
: f_(f)
|
||||
, plane_(BRep_Tool::Surface(f_))
|
||||
, cls_(f_, BRep_Tool::Tolerance(f_))
|
||||
, i((int)inset), j((int)inset)
|
||||
, inset_(inset)
|
||||
{
|
||||
BRepTools::UVBounds(f_, u0, u1, v0, v1);
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
bool operator()(gp_Pnt& p);
|
||||
};
|
||||
|
||||
bool faces_overlap(const TopoDS_Face& f, const TopoDS_Face& g);
|
||||
|
||||
double min_face_face_distance(const TopoDS_Shape& a, double max_search);
|
||||
|
||||
int bounding_box_overlap(double p, const TopoDS_Shape& a, const TopTools_ListOfShape& b, TopTools_ListOfShape& c);
|
||||
|
||||
bool get_edge_axis(const TopoDS_Edge& e, gp_Ax1& ax);
|
||||
|
||||
bool is_subset(const TopTools_IndexedMapOfShape& lhs, const TopTools_IndexedMapOfShape& rhs);
|
||||
|
||||
bool is_extrusion(const gp_Vec& v, const TopoDS_Shape& s, TopoDS_Face& base, std::pair<double, double>& interval);
|
||||
|
||||
int eliminate_touching_operands(double prec, const TopoDS_Shape& a, const TopTools_ListOfShape& bs, TopTools_ListOfShape& c);
|
||||
|
||||
TopoDS_Shape unify(const TopoDS_Shape& s, double tolerance);
|
||||
|
||||
bool boolean_subtraction_2d_using_builder(const TopoDS_Shape& a_input, const TopTools_ListOfShape& b_input, TopoDS_Shape& result, double eps);
|
||||
|
||||
}
|
||||
|
||||
} }
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "OpenCascadeKernel.h"
|
||||
|
||||
#include <BRepPrimAPI_MakePrism.hxx>
|
||||
|
||||
using namespace ifcopenshell::geometry;
|
||||
using namespace ifcopenshell::geometry::kernels;
|
||||
|
||||
bool OpenCascadeKernel::convert(const taxonomy::extrusion* extrusion, TopoDS_Shape& shape) {
|
||||
const double& height = extrusion->depth;
|
||||
|
||||
if (height < precision_) {
|
||||
Logger::Error("Non-positive extrusion height encountered for:", extrusion->instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
TopoDS_Shape face;
|
||||
if (!convert(&extrusion->basis, face)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
// @todo we need to decide whether the matrix is kept on the taxonomy node or
|
||||
// move the TopoDS_Shape, but obviously not both.
|
||||
gp_GTrsf gtrsf;
|
||||
if (!convert(&extrusion->matrix, gtrsf)) {
|
||||
Logger::Error("Unable to move extrusion");
|
||||
}
|
||||
auto trsf = gtrsf.Trsf();
|
||||
*/
|
||||
|
||||
const auto& fs = extrusion->direction.ccomponents();
|
||||
gp_Dir dir(fs(0), fs(1), fs(2));
|
||||
|
||||
shape.Nullify();
|
||||
|
||||
if (face.ShapeType() == TopAbs_COMPOUND) {
|
||||
|
||||
// For compounds (most likely the result of a IfcCompositeProfileDef)
|
||||
// create a compound solid shape.
|
||||
|
||||
TopExp_Explorer exp(face, TopAbs_FACE);
|
||||
|
||||
TopoDS_CompSolid compound;
|
||||
BRep_Builder builder;
|
||||
builder.MakeCompSolid(compound);
|
||||
|
||||
int num_faces_extruded = 0;
|
||||
for (; exp.More(); exp.Next(), ++num_faces_extruded) {
|
||||
builder.Add(compound, BRepPrimAPI_MakePrism(exp.Current(), height*dir));
|
||||
}
|
||||
|
||||
if (num_faces_extruded) {
|
||||
shape = compound;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (shape.IsNull()) {
|
||||
shape = BRepPrimAPI_MakePrism(face, height*dir);
|
||||
}
|
||||
|
||||
/*
|
||||
if (!shape.IsNull()) {
|
||||
// IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D
|
||||
// and therefore has a unit scale factor
|
||||
shape.Move(trsf);
|
||||
}
|
||||
*/
|
||||
|
||||
return !shape.IsNull();
|
||||
}
|
||||
|
||||
bool OpenCascadeKernel::convert_impl(const taxonomy::extrusion* extrusion, ifcopenshell::geometry::ConversionResults& results) {
|
||||
TopoDS_Shape shape;
|
||||
if (!convert(extrusion, shape)) {
|
||||
return false;
|
||||
}
|
||||
results.emplace_back(ConversionResult(
|
||||
extrusion->instance->data().id(),
|
||||
extrusion->matrix,
|
||||
new OpenCascadeShape(shape),
|
||||
extrusion->surface_style
|
||||
));
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the Lesser GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3.0 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* IfcOpenShell is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* Lesser GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <gp_Vec.hxx>
|
||||
#include <gp_Dir.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
#include <Geom_Line.hxx>
|
||||
#include <Geom_Plane.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <TopoDS_Iterator.hxx>
|
||||
#include <ShapeFix_Shape.hxx>
|
||||
#include <ShapeFix_ShapeTolerance.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <TopTools_DataMapOfShapeInteger.hxx>
|
||||
#include <BRepLib_FindSurface.hxx>
|
||||
#include <ShapeExtend_MsgRegistrator.hxx>
|
||||
#include <Message_Msg.hxx>
|
||||
#include <ShapeFix_Edge.hxx>
|
||||
|
||||
#include "OpenCascadeKernel.h"
|
||||
#include "face_definition.h"
|
||||
|
||||
using namespace ifcopenshell::geometry;
|
||||
using namespace ifcopenshell::geometry::util;
|
||||
using namespace ifcopenshell::geometry::kernels;
|
||||
|
||||
bool OpenCascadeKernel::convert(const taxonomy::face* face, TopoDS_Shape& result) {
|
||||
auto bounds = face->children_as<taxonomy::loop>();
|
||||
|
||||
face_definition fd;
|
||||
|
||||
const bool is_face_surface = false; /* todo */
|
||||
|
||||
/*
|
||||
if (is_face_surface) {
|
||||
IfcSchema::IfcFaceSurface* fs = (IfcSchema::IfcFaceSurface*) l;
|
||||
fs->FaceSurface();
|
||||
// FIXME: Surfaces are interpreted as a TopoDS_Shape
|
||||
TopoDS_Shape surface_shape;
|
||||
if (!convert_shape(fs->FaceSurface(), surface_shape)) return false;
|
||||
|
||||
// FIXME: Assert this obtaines the only face
|
||||
TopExp_Explorer exp(surface_shape, TopAbs_FACE);
|
||||
if (!exp.More()) return false;
|
||||
|
||||
TopoDS_Face surface = TopoDS::Face(exp.Current());
|
||||
fd.surface() = BRep_Tool::Surface(surface);
|
||||
}
|
||||
*/
|
||||
|
||||
const int num_bounds = bounds.size();
|
||||
int num_outer_bounds = 0;
|
||||
|
||||
for (auto& bound : bounds) {
|
||||
if (bound->external.get_value_or(false)) {
|
||||
num_outer_bounds++;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of outer bounds should be one according to the schema. Also Open Cascade
|
||||
// expects this, but it is not strictly checked. Regardless, if the number is greater,
|
||||
// the face will still be processed as long as there are no holes. A compound of faces
|
||||
// is returned in that case.
|
||||
if (num_bounds > 1 && num_outer_bounds > 1 && num_bounds != num_outer_bounds) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Invalid configuration of boundaries for:", face->instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (num_outer_bounds > 1) {
|
||||
Logger::Message(Logger::LOG_WARNING, "Multiple outer boundaries for:", face->instance);
|
||||
fd.all_outer() = true;
|
||||
}
|
||||
|
||||
TopTools_DataMapOfShapeInteger wire_senses;
|
||||
|
||||
for (int process_interior = 0; process_interior <= 1; ++process_interior) {
|
||||
for (auto& bound : bounds) {
|
||||
bool same_sense = true; /* todo bound->Orientation(); */
|
||||
|
||||
const bool is_interior =
|
||||
!bound->external.get_value_or(false) &&
|
||||
(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_ && is_polyhedron(bound)) {
|
||||
if (!faceset_helper_->wire(bound, wire)) {
|
||||
Logger::Message(Logger::LOG_WARNING, "Face boundary loop not included", bound->instance);
|
||||
continue;
|
||||
}
|
||||
} else if (!convert(bound, wire)) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary loop", bound->instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!same_sense) {
|
||||
wire.Reverse();
|
||||
}
|
||||
|
||||
wire_senses.Bind(wire.Oriented(TopAbs_FORWARD), same_sense ? TopAbs_FORWARD : TopAbs_REVERSED);
|
||||
|
||||
fd.wires().emplace_back(wire);
|
||||
}
|
||||
}
|
||||
|
||||
if (fd.wires().empty()) {
|
||||
Logger::Warning("Face with no boundaries", face->instance);
|
||||
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 (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;
|
||||
fd.surface() = new Geom_Plane(c1->Position().Location(), n);
|
||||
}
|
||||
} else {
|
||||
gp_Pln pln;
|
||||
if (approximate_plane_through_wire(wire, pln)) {
|
||||
fd.surface() = new Geom_Plane(pln);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, 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", face->instance);
|
||||
|
||||
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& occ_face = TopoDS::Face(it.Value());
|
||||
for (TopExp_Explorer exp2(occ_face, TopAbs_EDGE); exp2.More(); exp2.Next()) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(exp2.Current());
|
||||
ShapeFix_Edge fix_edge;
|
||||
fix_edge.FixAddPCurve(edge, occ_face, false, precision_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
|
||||
const TopoDS_Face& occ_face = TopoDS::Face(it.Value());
|
||||
|
||||
ShapeFix_Face sfs(TopoDS::Face(occ_face));
|
||||
TopTools_DataMapOfShapeListOfShape wire_map;
|
||||
sfs.FixOrientation(wire_map);
|
||||
|
||||
TopoDS_Iterator jt(occ_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it.Value() = sfs.Face();
|
||||
}
|
||||
|
||||
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
|
||||
TopoDS_Face& occ_face = TopoDS::Face(it.Value());
|
||||
|
||||
bool all_reversed = true;
|
||||
TopoDS_Iterator jt(occ_face, false);
|
||||
for (; jt.More(); jt.Next()) {
|
||||
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
|
||||
if (!wire_senses.IsBound(w.Oriented(TopAbs_FORWARD)) || (w.Orientation() == wire_senses.Find(w.Oriented(TopAbs_FORWARD)))) {
|
||||
all_reversed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_reversed) {
|
||||
occ_face.Reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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& occ_face = TopoDS::Face(it.Value());
|
||||
builder.Add(compound, occ_face);
|
||||
}
|
||||
result = compound;
|
||||
} else {
|
||||
result = face_list.First();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "face_definition.h"
|
||||
|
||||
#include <TopoDS.hxx>
|
||||
#include <Geom_Line.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <TopoDS_Iterator.hxx>
|
||||
|
||||
/* Returns whether wire conforms to a polyhedron, i.e. only edges with linear curves*/
|
||||
bool IfcGeom::util::is_polyhedron(const TopoDS_Wire & wire) {
|
||||
double a, b;
|
||||
TopLoc_Location l;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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 FACE_DEFINITION_H
|
||||
#define FACE_DEFINITION_H
|
||||
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <Geom_Surface.hxx>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace IfcGeom {
|
||||
namespace util {
|
||||
|
||||
/* Returns whether wire conforms to a polyhedron, i.e. only edges with linear curves*/
|
||||
bool is_polyhedron(const TopoDS_Wire& wire);
|
||||
|
||||
/* 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() };
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,280 @@
|
||||
#include "OpenCascadeKernel.h"
|
||||
|
||||
#include "IfcGeomTree.h"
|
||||
#include "wire_utils.h"
|
||||
|
||||
using namespace ifcopenshell::geometry;
|
||||
using namespace ifcopenshell::geometry::kernels;
|
||||
|
||||
namespace {
|
||||
void find_neighbours(ifcopenshell::geometry::impl::tree<int>& tree, std::vector<std::unique_ptr<gp_Pnt>>& pnts, std::set<int>& visited, int p, double eps) {
|
||||
visited.insert(p);
|
||||
|
||||
Bnd_Box b;
|
||||
b.Set(*pnts[p].get());
|
||||
b.Enlarge(eps);
|
||||
|
||||
std::vector<int> js = tree.select_box(b, false);
|
||||
for (int j : js) {
|
||||
visited.insert(j);
|
||||
#ifdef FACESET_HELPER_RECURSIVE
|
||||
if (visited.find(j) == visited.end()) {
|
||||
// @todo, making this recursive removes the dependence on the initial ordering, but will
|
||||
// likely result in empty results when all vertices are within 1 eps from another point.
|
||||
find_neighbours(tree, pnts, visited, j, eps);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OpenCascadeKernel::faceset_helper::faceset_helper(OpenCascadeKernel* kernel, const taxonomy::shell* shell)
|
||||
: kernel_(kernel)
|
||||
, non_manifold_(false)
|
||||
{
|
||||
// @todo use pointers?
|
||||
std::vector<taxonomy::point3> points;
|
||||
std::vector<taxonomy::loop*> loops;
|
||||
|
||||
for (auto& f : shell->children_as<taxonomy::face>()) {
|
||||
for (auto& l : f->children_as<taxonomy::loop>()) {
|
||||
loops.push_back(l);
|
||||
for (auto& e : l->children_as<taxonomy::edge>()) {
|
||||
// @todo make sure only cartesian points are provided here
|
||||
points.push_back(boost::get<taxonomy::point3>(e->start));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<gp_Pnt>> pnts(points.size());
|
||||
std::vector<TopoDS_Vertex> vertices(pnts.size());
|
||||
|
||||
// @todo
|
||||
impl::tree<int> tree;
|
||||
|
||||
BRep_Builder B;
|
||||
|
||||
Bnd_Box box;
|
||||
for (size_t i = 0; i < points.size(); ++i) {
|
||||
gp_Pnt* p = new gp_Pnt(convert_xyz<gp_Pnt>(points[i]));
|
||||
pnts[i].reset(p);
|
||||
B.MakeVertex(vertices[i], *p, Precision::Confusion());
|
||||
tree.add(i, vertices[i]);
|
||||
box.Add(*p);
|
||||
}
|
||||
|
||||
// Use the bbox diagonal to influence local epsilon
|
||||
// double bdiff = std::sqrt(box.SquareExtent());
|
||||
|
||||
// @todo the bounding box diagonal is not used (see above)
|
||||
// because we're explicitly interested in the miminal
|
||||
// dimension of the element to limit the tolerance (for sheet-
|
||||
// like elements for example). But the way below is very
|
||||
// dependent on orientation due to the usage of the
|
||||
// axis-aligned bounding box. Use PCA to find three non-aligned
|
||||
// set of dimensions and use the one with the smallest eigenvalue.
|
||||
|
||||
// Find the minimal bounding box edge
|
||||
double bmin[3], bmax[3];
|
||||
box.Get(bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2]);
|
||||
double bdiff = std::numeric_limits<double>::infinity();
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
const double d = bmax[i] - bmin[i];
|
||||
if (d > kernel->settings_.getValue(ConversionSettings::GV_PRECISION) * 10. && d < bdiff) {
|
||||
bdiff = d;
|
||||
}
|
||||
}
|
||||
|
||||
eps_ = kernel->settings_.getValue(ConversionSettings::GV_PRECISION) * 10. * (std::min)(1.0, bdiff);
|
||||
|
||||
// @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.
|
||||
|
||||
size_t loops_removed, non_manifold, duplicate_faces;
|
||||
|
||||
std::map<std::pair<int, int>, int> edge_use;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
// Some times files, have large tolerance values specified collapsing too many vertices.
|
||||
// This case we detect below and re-run the loop with smaller epsilon. Normally
|
||||
// the body of this loop would only be executed once.
|
||||
|
||||
loops_removed = 0;
|
||||
non_manifold = 0;
|
||||
duplicate_faces = 0;
|
||||
|
||||
vertex_mapping_.clear();
|
||||
duplicates_.clear();
|
||||
|
||||
edge_use.clear();
|
||||
|
||||
if (eps_ < Precision::Confusion()) {
|
||||
// occt uses some hard coded precision values, don't go smaller than that.
|
||||
// @todo, can be reset though with BRepLib::Precision(double)
|
||||
eps_ = Precision::Confusion();
|
||||
}
|
||||
|
||||
for (int pnt_i = 0; pnt_i < (int)pnts.size(); ++pnt_i) {
|
||||
if (pnts[pnt_i]) {
|
||||
std::set<int> vs;
|
||||
find_neighbours(tree, pnts, vs, pnt_i, eps_);
|
||||
|
||||
for (int v : vs) {
|
||||
auto& pt = points[v];
|
||||
// NB: insert() ignores duplicate keys
|
||||
vertex_mapping_.insert({ pt.instance->data().id() , i });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::set<std::tuple<double, double, double>> unique;
|
||||
for (int pnt_i = 0; pnt_i < (int)pnts.size(); ++pnt_i) {
|
||||
if (pnts[pnt_i]) {
|
||||
unique.insert(std::make_tuple(
|
||||
(*pnts[pnt_i]).X(),
|
||||
(*pnts[pnt_i]).Y(),
|
||||
(*pnts[pnt_i]).Z()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (unique.size() != vertex_mapping_.size()) {
|
||||
Logger::Notice("Collapsed vertices from " + std::to_string(pnts.size()) + " (" + std::to_string(unique.size()) + " unique) to " + std::to_string(vertex_mapping_.size()));
|
||||
}
|
||||
|
||||
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) {
|
||||
std::vector<std::pair<int, int> > segments;
|
||||
edge_set_t segment_set;
|
||||
|
||||
loop_(loop, [&segments, &segment_set](int C, int D, bool) {
|
||||
segment_set.insert(edge_t{ C,D });
|
||||
segments.push_back(std::make_pair(C, D));
|
||||
});
|
||||
|
||||
if (edge_sets.find(segment_set) != edge_sets.end()) {
|
||||
duplicate_faces++;
|
||||
// @todo does this work with tesselated face sets, will they have an associated instance? Guess not.
|
||||
duplicates_.insert(loop->instance->data().id());
|
||||
continue;
|
||||
}
|
||||
edge_sets.insert(segment_set);
|
||||
|
||||
if (segments.size() >= 3) {
|
||||
for (auto& p : segments) {
|
||||
edge_use[p] ++;
|
||||
}
|
||||
} else {
|
||||
loops_removed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (edge_use.size() != 0) {
|
||||
break;
|
||||
} else {
|
||||
eps_ /= 10.;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& p : edge_use) {
|
||||
int a, b;
|
||||
std::tie(a, b) = p.first;
|
||||
edges_[p.first] = BRepBuilderAPI_MakeEdge(vertices[a], vertices[b]);
|
||||
|
||||
if (p.second != 2) {
|
||||
non_manifold += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (duplicates_.size() || loops_removed || (non_manifold && shell->closed.get_value_or(false))) {
|
||||
Logger::Warning(boost::lexical_cast<std::string>(duplicate_faces) + " duplicate faces removed, " + boost::lexical_cast<std::string>(loops_removed) + " degenerate loops eliminated and " + boost::lexical_cast<std::string>(non_manifold) + " non-manifold edges");
|
||||
}
|
||||
}
|
||||
|
||||
void OpenCascadeKernel::faceset_helper::loop_(const taxonomy::loop* ps, const std::function<void(int, int, bool)>& callback) {
|
||||
if (ps->children.size() < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto a = boost::get<taxonomy::point3>(((taxonomy::edge*) ps->children.back())->start).instance;
|
||||
auto A = a->data().id();
|
||||
for (auto& b : ps->children) {
|
||||
auto B = boost::get<taxonomy::point3>(((taxonomy::edge*) b)->start).instance->data().id();
|
||||
auto C = vertex_mapping_[A], D = vertex_mapping_[B];
|
||||
bool fwd = C < D;
|
||||
if (!fwd) {
|
||||
std::swap(C, D);
|
||||
}
|
||||
if (C != D) {
|
||||
callback(C, D, fwd);
|
||||
A = B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenCascadeKernel::faceset_helper::edge(int A, int B, TopoDS_Edge& e) {
|
||||
auto it = edges_.find({ A, B });
|
||||
if (it == edges_.end()) {
|
||||
return false;
|
||||
}
|
||||
e = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenCascadeKernel::faceset_helper::wire(const taxonomy::loop* loop, TopoDS_Wire& w) {
|
||||
TopTools_ListOfShape ws;
|
||||
if (!wires(loop, ws)) {
|
||||
return false;
|
||||
}
|
||||
util::select_largest(ws, w);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenCascadeKernel::faceset_helper::wires(const taxonomy::loop* loop, TopTools_ListOfShape& wires) {
|
||||
if (duplicates_.find(loop->instance->data().id()) != duplicates_.end()) {
|
||||
return false;
|
||||
}
|
||||
TopoDS_Wire wire;
|
||||
BRep_Builder builder;
|
||||
builder.MakeWire(wire);
|
||||
int count = 0;
|
||||
loop_(loop, [this, &builder, &wire, &count](int A, int B, bool fwd) {
|
||||
TopoDS_Edge e;
|
||||
if (edge(A, B, e)) {
|
||||
if (!fwd) {
|
||||
e.Reverse();
|
||||
}
|
||||
builder.Add(wire, e);
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
if (count >= 3) {
|
||||
wire.Closed(true);
|
||||
|
||||
TopTools_ListOfShape results;
|
||||
/* todo kernel_->getValue(GV_NO_WIRE_INTERSECTION_CHECK) < 0. && */
|
||||
/* todo kernel_->get_wire_intersection_tolerance(wire) */
|
||||
if (util::wire_intersections(wire, results, kernel_->settings_.getValue(ConversionSettings::GV_PRECISION), kernel_->settings_.getValue(ConversionSettings::GV_PRECISION))) {
|
||||
Logger::Warning("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected");
|
||||
non_manifold_ = true;
|
||||
wires = results;
|
||||
} else {
|
||||
wires.Append(wire);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
OpenCascadeKernel::faceset_helper::~faceset_helper() {
|
||||
// @todo this is super ugly, but how else can we be notified that the unique_ptr goes out of scope?
|
||||
// Perhaps just supply a custom std::deleter?
|
||||
kernel_->faceset_helper_ = nullptr;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
#include "OpenCascadeKernel.h"
|
||||
#include "wire_builder.h"
|
||||
|
||||
#include <Geom_Line.hxx>
|
||||
#include <Geom_Circle.hxx>
|
||||
#include <Geom_Ellipse.hxx>
|
||||
#include <BRepAdaptor_CompCurve.hxx>
|
||||
#include <BRepAdaptor_HCompCurve.hxx>
|
||||
#include <Approx_Curve3d.hxx>
|
||||
#include <ShapeFix_ShapeTolerance.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
|
||||
using namespace ifcopenshell::geometry;
|
||||
using namespace ifcopenshell::geometry::kernels;
|
||||
|
||||
using namespace IfcGeom::util;
|
||||
|
||||
namespace {
|
||||
typedef boost::variant<Handle(Geom_Curve), TopoDS_Wire> curve_creation_visitor_result_type;
|
||||
curve_creation_visitor_result_type convert_curve(OpenCascadeKernel* kernel, const taxonomy::item* curve);
|
||||
|
||||
struct curve_creation_visitor {
|
||||
OpenCascadeKernel* kernel;
|
||||
curve_creation_visitor_result_type result;
|
||||
|
||||
curve_creation_visitor_result_type operator()(const taxonomy::bspline_curve&) {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
curve_creation_visitor_result_type operator()(const taxonomy::line& l) {
|
||||
const auto& m = l.matrix.ccomponents();
|
||||
return result = Handle(Geom_Curve)(new Geom_Line(convert_xyz2<gp_Pnt>(m.col(3)), convert_xyz2<gp_Dir>(m.col(0))));
|
||||
}
|
||||
|
||||
curve_creation_visitor_result_type operator()(const taxonomy::circle& c) {
|
||||
const auto& m = c.matrix.ccomponents();
|
||||
return result = Handle(Geom_Curve)(new Geom_Circle(gp_Ax2(convert_xyz2<gp_Pnt>(m.col(3)), convert_xyz2<gp_Dir>(m.col(2)), convert_xyz2<gp_Dir>(m.col(0))), c.radius));
|
||||
}
|
||||
|
||||
curve_creation_visitor_result_type operator()(const taxonomy::ellipse& e) {
|
||||
const auto& m = e.matrix.ccomponents();
|
||||
return result = Handle(Geom_Curve)(new Geom_Ellipse(gp_Ax2(convert_xyz2<gp_Pnt>(m.col(3)), convert_xyz2<gp_Dir>(m.col(2)), convert_xyz2<gp_Dir>(m.col(0))), e.radius, e.radius2));
|
||||
}
|
||||
|
||||
curve_creation_visitor_result_type operator()(const taxonomy::loop& l) {
|
||||
TopoDS_Wire wire;
|
||||
kernel->convert(&l, wire);
|
||||
return result = wire;
|
||||
}
|
||||
|
||||
curve_creation_visitor_result_type operator()(const taxonomy::edge& e) {
|
||||
// @todo for polyloops/-lines we should probably construct edges based on correct oriented TopoDS_Vertex instead.
|
||||
|
||||
if (e.start.which() != e.end.which()) {
|
||||
throw std::runtime_error("Different trim types not supported");
|
||||
}
|
||||
|
||||
TopoDS_Edge E;
|
||||
if (e.basis) {
|
||||
auto crv_or_wire = convert_curve(kernel, e.basis);
|
||||
Handle(Geom_Curve) curve;
|
||||
if (crv_or_wire.which() == 0) {
|
||||
curve = boost::get<Handle(Geom_Curve)>(crv_or_wire);
|
||||
} else {
|
||||
// @todo
|
||||
const double precision_ = 1.e-5;
|
||||
Logger::Warning("Approximating BasisCurve due to possible discontinuities", e.instance);
|
||||
BRepAdaptor_CompCurve cc(boost::get<TopoDS_Wire>(crv_or_wire), true);
|
||||
Handle(Adaptor3d_HCurve) hcc = Handle(Adaptor3d_HCurve)(new BRepAdaptor_HCompCurve(cc));
|
||||
// @todo, arbitrary numbers here, note they cannot be too high as contiguous memory is allocated based on them.
|
||||
Approx_Curve3d approx(hcc, precision_, GeomAbs_C0, 10, 10);
|
||||
curve = approx.Curve();
|
||||
}
|
||||
|
||||
const bool reversed = !((taxonomy::geom_item*)e.basis)->orientation.get_value_or(true);
|
||||
const bool is_conic = e.basis->kind() == taxonomy::ELLIPSE || e.basis->kind() == taxonomy::CIRCLE;
|
||||
|
||||
// @todo, copy over logic from previous IfcTrimmedCurve handling
|
||||
if (e.start.which() == 0) {
|
||||
auto p1 = convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.start));
|
||||
auto p2 = convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.end));
|
||||
|
||||
if (reversed) {
|
||||
std::swap(p1, p2);
|
||||
}
|
||||
|
||||
E = BRepBuilderAPI_MakeEdge(curve, p1, p2).Edge();
|
||||
} else {
|
||||
auto v1 = boost::get<double>(e.start);
|
||||
auto v2 = boost::get<double>(e.end);
|
||||
|
||||
if (reversed) {
|
||||
std::swap(v1, v2);
|
||||
}
|
||||
|
||||
if (is_conic && ALMOST_THE_SAME(fmod(v2 - v1, M_PI*2.), 0.)) {
|
||||
E = BRepBuilderAPI_MakeEdge(curve).Edge();
|
||||
} else {
|
||||
E = BRepBuilderAPI_MakeEdge(curve, v1, v2).Edge();
|
||||
}
|
||||
}
|
||||
|
||||
if (reversed) {
|
||||
E.Reverse();
|
||||
}
|
||||
} else {
|
||||
if (e.start.which() != 0) {
|
||||
throw std::runtime_error("Non-cartesian trim on edge without curve");
|
||||
}
|
||||
auto p1 = convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.start));
|
||||
auto p2 = convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.end));
|
||||
|
||||
E = BRepBuilderAPI_MakeEdge(p1, p2).Edge();
|
||||
}
|
||||
BRep_Builder B;
|
||||
TopoDS_Wire W;
|
||||
B.MakeWire(W);
|
||||
B.Add(W, E);
|
||||
return result = W;
|
||||
}
|
||||
};
|
||||
|
||||
curve_creation_visitor_result_type convert_curve(OpenCascadeKernel* kernel, const taxonomy::item* curve) {
|
||||
curve_creation_visitor v{ kernel };
|
||||
if (dispatch_curve_creation<curve_creation_visitor, 0>::dispatch(curve, v)) {
|
||||
return v.result;
|
||||
} else {
|
||||
throw std::runtime_error("No curve created");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenCascadeKernel::convert(const taxonomy::loop* loop, TopoDS_Wire& wire) {
|
||||
auto segments = loop->children_as<taxonomy::edge>();
|
||||
|
||||
TopTools_ListOfShape converted_segments;
|
||||
|
||||
for (auto& segment : segments) {
|
||||
auto segment_wire = boost::get<TopoDS_Wire>(convert_curve(this, segment));
|
||||
|
||||
#ifdef IFOPSH_DEBUG
|
||||
std::ostringstream o;
|
||||
segment->print(o);
|
||||
TopoDS_Vertex v0, v1;
|
||||
TopExp::Vertices(segment_wire, v0, v1);
|
||||
gp_Pnt p0 = BRep_Tool::Pnt(v0);
|
||||
gp_Pnt p1 = BRep_Tool::Pnt(v1);
|
||||
o << "p0 " << p0.X() << " " << p0.Y() << " " << p0.Z() << std::endl;
|
||||
o << "p1 " << p1.X() << " " << p1.Y() << " " << p1.Z() << std::endl;
|
||||
auto o_str = o.str();
|
||||
std::wcout << o_str.c_str() << std::endl;
|
||||
#endif
|
||||
|
||||
if (!segment->orientation_2.get_value_or(true)) {
|
||||
segment_wire.Reverse();
|
||||
}
|
||||
|
||||
ShapeFix_ShapeTolerance FTol;
|
||||
FTol.SetTolerance(segment_wire, precision_, TopAbs_WIRE);
|
||||
|
||||
converted_segments.Append(segment_wire);
|
||||
}
|
||||
|
||||
if (converted_segments.Extent() == 0) {
|
||||
Logger::Message(Logger::LOG_ERROR, "No segment succesfully converted:", loop->instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
TopoDS_Vertex wire_first_vertex, wire_last_vertex, edge_first_vertex, edge_last_vertex;
|
||||
|
||||
TopTools_ListIteratorOfListOfShape it(converted_segments);
|
||||
|
||||
/*
|
||||
@todo
|
||||
IfcEntityList::ptr profile = l->data().getInverse(&IfcSchema::IfcProfileDef::Class(), -1);
|
||||
const bool force_close = profile && profile->size() > 0;
|
||||
*/
|
||||
const bool force_close = false;
|
||||
|
||||
wire_builder bld(precision_, loop->instance);
|
||||
shape_pair_enumerate(it, bld, force_close);
|
||||
wire = bld.wire();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "OpenCascadeKernel.h"
|
||||
|
||||
using namespace ifcopenshell::geometry;
|
||||
using namespace ifcopenshell::geometry::kernels;
|
||||
|
||||
bool OpenCascadeKernel::convert(const taxonomy::matrix4* matrix, gp_GTrsf& trsf) {
|
||||
// @todo check
|
||||
const auto& m = matrix->ccomponents();
|
||||
gp_Mat mat(
|
||||
m(0, 0), m(0, 1), m(0, 2),
|
||||
m(1, 0), m(1, 1), m(1, 2),
|
||||
m(2, 0), m(2, 1), m(2, 2)
|
||||
);
|
||||
|
||||
if (matrix->instance && matrix->instance->declaration().name() == "IfcCartesianTransformationOperator3DnonUniform") {
|
||||
std::wcout << "non uniform" << std::endl;
|
||||
}
|
||||
|
||||
// @nb SetVectorialPart() sets gp_GTrsf.scale to 0.0, causing an non-invertable
|
||||
// matrix later on which cannot be in TopLoc_Location.
|
||||
|
||||
std::array<double, 3> ms{ {
|
||||
mat.Column(1).Modulus(),
|
||||
mat.Column(2).Modulus(),
|
||||
mat.Column(3).Modulus()
|
||||
} };
|
||||
std::sort(ms.begin(), ms.end());
|
||||
|
||||
if (std::fabs(ms.front() - ms.back()) < 1.e-7) {
|
||||
gp_Trsf tr;
|
||||
tr.SetValues(
|
||||
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
|
||||
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
|
||||
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
|
||||
);
|
||||
trsf = tr;
|
||||
} else {
|
||||
trsf.SetVectorialPart(mat);
|
||||
trsf.SetTranslationPart(gp_XYZ(m(0, 3), m(1, 3), m(2, 3)));
|
||||
trsf.SetForm();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "OpenCascadeKernel.h"
|
||||
|
||||
using namespace ifcopenshell::geometry;
|
||||
using namespace ifcopenshell::geometry::kernels;
|
||||
|
||||
bool OpenCascadeKernel::convert(const taxonomy::shell* l, TopoDS_Shape& shape) {
|
||||
std::unique_ptr<faceset_helper> helper_scope;
|
||||
helper_scope.reset(new faceset_helper(this, l));
|
||||
|
||||
faceset_helper_ = helper_scope.get();
|
||||
|
||||
auto faces = l->children_as<taxonomy::face>();
|
||||
double minimal_face_area = precision_ * precision_ * 0.5;
|
||||
|
||||
double min_face_area = faceset_helper_
|
||||
? (faceset_helper_->epsilon() * faceset_helper_->epsilon() / 20.)
|
||||
: minimal_face_area;
|
||||
|
||||
TopTools_ListOfShape face_list;
|
||||
for (auto& face : faces) {
|
||||
bool success = false;
|
||||
TopoDS_Face occ_face;
|
||||
|
||||
try {
|
||||
success = convert(face, occ_face);
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Error(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Error("Unknown error creating face");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Error("Unknown error creating face");
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
Logger::Message(Logger::LOG_WARNING, "Failed to convert face:", face->instance);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (occ_face.ShapeType() == TopAbs_COMPOUND) {
|
||||
TopoDS_Iterator face_it(occ_face, false);
|
||||
for (; face_it.More(); face_it.Next()) {
|
||||
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) > min_face_area) {
|
||||
face_list.Append(triangle);
|
||||
} else {
|
||||
Logger::Message(Logger::LOG_WARNING, "Degenerate face:", face->instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (face_area(occ_face) > min_face_area) {
|
||||
face_list.Append(occ_face);
|
||||
} else {
|
||||
Logger::Message(Logger::LOG_WARNING, "Degenerate face:", face->instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (face_list.Extent() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @todo
|
||||
/* face_list.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || */
|
||||
|
||||
if (!create_solid_from_faces(face_list, shape)) {
|
||||
TopoDS_Compound compound;
|
||||
BRep_Builder builder;
|
||||
builder.MakeCompound(compound);
|
||||
|
||||
TopTools_ListIteratorOfListOfShape face_iterator;
|
||||
for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) {
|
||||
builder.Add(compound, face_iterator.Value());
|
||||
}
|
||||
shape = compound;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenCascadeKernel::convert_impl(const taxonomy::shell *shell, ifcopenshell::geometry::ConversionResults& results) {
|
||||
TopoDS_Shape shape;
|
||||
if (!convert(shell, shape)) {
|
||||
return false;
|
||||
}
|
||||
results.emplace_back(ConversionResult(
|
||||
shell->instance->data().id(),
|
||||
shell->matrix,
|
||||
new OpenCascadeShape(shape),
|
||||
shell->surface_style
|
||||
));
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
#include "sweep_utils.h"
|
||||
|
||||
#include "../ifcparse/IfcLogger.h"
|
||||
#include "../ifcgeom_schema_agnostic/Kernel.h"
|
||||
|
||||
#include <gp_Ax2.hxx>
|
||||
#include <Geom_Line.hxx>
|
||||
#include <Geom_Circle.hxx>
|
||||
|
||||
#include <TopExp.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Vertex.hxx>
|
||||
#include <TopoDS_Compound.hxx>
|
||||
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRep_Builder.hxx>
|
||||
|
||||
#include <BRepPrimAPI_MakePrism.hxx>
|
||||
#include <BRepPrimAPI_MakeRevol.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepOffsetAPI_MakePipeShell.hxx>
|
||||
|
||||
bool IfcGeom::util::wire_is_c1_continuous(const TopoDS_Wire & w, double tol) {
|
||||
// NB Note that c0 continuity is NOT checked!
|
||||
|
||||
TopTools_IndexedDataMapOfShapeListOfShape map;
|
||||
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, map);
|
||||
for (int i = 1; i <= map.Extent(); ++i) {
|
||||
const auto& li = map.FindFromIndex(i);
|
||||
if (li.Extent() == 2) {
|
||||
const TopoDS_Vertex& v = TopoDS::Vertex(map.FindKey(i));
|
||||
|
||||
const TopoDS_Edge& e0 = TopoDS::Edge(li.First());
|
||||
const TopoDS_Edge& e1 = TopoDS::Edge(li.Last());
|
||||
|
||||
double u0 = BRep_Tool::Parameter(v, e0);
|
||||
double u1 = BRep_Tool::Parameter(v, e1);
|
||||
|
||||
double _, __;
|
||||
Handle(Geom_Curve) c0 = BRep_Tool::Curve(e0, _, __);
|
||||
Handle(Geom_Curve) c1 = BRep_Tool::Curve(e1, _, __);
|
||||
|
||||
gp_Pnt p;
|
||||
gp_Vec v0, v1;
|
||||
c0->D1(u0, p, v0);
|
||||
c1->D1(u1, p, v1);
|
||||
|
||||
if (1. - std::abs(v0.Normalized().Dot(v1.Normalized())) > tol) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::util::wire_to_ax(const TopoDS_Wire & wire, gp_Ax2 & directrix) {
|
||||
gp_Pnt directrix_origin;
|
||||
gp_Vec directrix_tangent;
|
||||
|
||||
TopoDS_Edge edge;
|
||||
|
||||
// Find first edge
|
||||
TopoDS_Vertex v0, v1;
|
||||
TopExp::Vertices(wire, v0, v1);
|
||||
TopTools_IndexedDataMapOfShapeListOfShape map;
|
||||
TopExp::MapShapesAndAncestors(wire, TopAbs_VERTEX, TopAbs_EDGE, map);
|
||||
if (v0.IsSame(v1) && map.Contains(v0) && map.FindFromKey(v0).Extent() == 2) {
|
||||
// Closed wire, with more than 1 edges
|
||||
auto es = map.FindFromKey(v0);
|
||||
auto e1 = TopoDS::Edge(es.First());
|
||||
auto e2 = TopoDS::Edge(es.Last());
|
||||
|
||||
double u0, u1;
|
||||
|
||||
gp_Vec accum;
|
||||
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(e1, u0, u1);
|
||||
crv->D1(TopExp::FirstVertex(e1).IsSame(v0) ? u0 : u1, directrix_origin, directrix_tangent);
|
||||
|
||||
accum += directrix_tangent;
|
||||
|
||||
crv = BRep_Tool::Curve(e2, u0, u1);
|
||||
crv->D1(TopExp::FirstVertex(e2).IsSame(v0) ? u0 : u1, directrix_origin, directrix_tangent);
|
||||
|
||||
accum += directrix_tangent;
|
||||
|
||||
directrix_tangent = accum;
|
||||
|
||||
} else if (map.Contains(v0) && map.FindFromKey(v0).Extent() == 1) {
|
||||
edge = TopoDS::Edge(map.FindFromKey(v0).First());
|
||||
|
||||
double u0, u1;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u0, u1);
|
||||
crv->D1(u0, directrix_origin, directrix_tangent);
|
||||
} else {
|
||||
Logger::Error("Unable to locate first edge");
|
||||
return false;
|
||||
}
|
||||
|
||||
directrix = gp_Ax2(directrix_origin, directrix_tangent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::util::is_single_linear_edge(const TopoDS_Wire & wire) {
|
||||
TopExp_Explorer exp(wire, TopAbs_EDGE);
|
||||
if (!exp.More()) {
|
||||
return false;
|
||||
}
|
||||
TopoDS_Edge e = TopoDS::Edge(exp.Current());
|
||||
exp.Next();
|
||||
if (exp.More()) {
|
||||
return false;
|
||||
}
|
||||
double u, v;
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
|
||||
return crv->DynamicType() == STANDARD_TYPE(Geom_Line);
|
||||
}
|
||||
|
||||
bool IfcGeom::util::is_single_circular_edge(const TopoDS_Wire & wire) {
|
||||
TopExp_Explorer exp(wire, TopAbs_EDGE);
|
||||
if (!exp.More()) {
|
||||
return false;
|
||||
}
|
||||
TopoDS_Edge e = TopoDS::Edge(exp.Current());
|
||||
exp.Next();
|
||||
if (exp.More()) {
|
||||
return false;
|
||||
}
|
||||
double u, v;
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
|
||||
return crv->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
||||
}
|
||||
|
||||
void IfcGeom::util::process_sweep_as_extrusion(const TopoDS_Wire & wire, const TopoDS_Wire & section, TopoDS_Shape & result) {
|
||||
TopExp_Explorer exp(wire, TopAbs_EDGE);
|
||||
TopoDS_Edge e = TopoDS::Edge(exp.Current());
|
||||
double u, v;
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
|
||||
const auto& dir = Handle(Geom_Line)::DownCast(crv)->Position().Direction();
|
||||
// OCCT line is normalized so diff in parametric coords equals length
|
||||
const double depth = std::abs(u - v);
|
||||
// @todo we could be extruding the wire only when we know this is an intermediate edge.
|
||||
TopoDS_Face face = BRepBuilderAPI_MakeFace(section).Face();
|
||||
result = BRepPrimAPI_MakePrism(face, depth*dir).Shape();
|
||||
}
|
||||
|
||||
void IfcGeom::util::process_sweep_as_revolution(const TopoDS_Wire & wire, const TopoDS_Wire & section, TopoDS_Shape & result) {
|
||||
TopExp_Explorer exp(wire, TopAbs_EDGE);
|
||||
TopoDS_Edge e = TopoDS::Edge(exp.Current());
|
||||
double u, v;
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
|
||||
auto circ = Handle(Geom_Circle)::DownCast(crv);
|
||||
// @todo we could be extruding the wire only when we know this is an intermediate edge.
|
||||
const double depth = std::abs(u - v);
|
||||
TopoDS_Face face = BRepBuilderAPI_MakeFace(section).Face();
|
||||
result = BRepPrimAPI_MakeRevol(face, circ->Axis(), depth).Shape();
|
||||
}
|
||||
|
||||
void IfcGeom::util::process_sweep_as_pipe(const TopoDS_Wire & wire, const TopoDS_Wire & section, TopoDS_Shape & result, bool force_transformed) {
|
||||
// This tolerance is fairly high due to the linear edge substitution for small (or large radii) conical curves.
|
||||
const bool is_continuous = wire_is_c1_continuous(wire, 1.e-2);
|
||||
BRepOffsetAPI_MakePipeShell builder(wire);
|
||||
builder.Add(section);
|
||||
builder.SetTransitionMode(is_continuous || force_transformed ? BRepBuilderAPI_Transformed : BRepBuilderAPI_RightCorner);
|
||||
try {
|
||||
builder.Build();
|
||||
} catch (Standard_Failure& e) {
|
||||
// We fallback to BRepBuilderAPI_Transformed, but likely with visual artefacts.
|
||||
if (!(is_continuous || force_transformed)) {
|
||||
return process_sweep_as_pipe(wire, section, result, true);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
builder.MakeSolid();
|
||||
result = builder.Shape();
|
||||
}
|
||||
|
||||
void IfcGeom::util::sort_edges(const TopoDS_Wire & wire, std::vector<TopoDS_Edge>& sorted_edges) {
|
||||
TopTools_IndexedDataMapOfShapeListOfShape map;
|
||||
TopExp::MapShapesAndAncestors(wire, TopAbs_VERTEX, TopAbs_EDGE, map);
|
||||
|
||||
for (int i = 1; i <= map.Extent(); ++i) {
|
||||
if (map.FindFromIndex(i).Extent() > 2) {
|
||||
Logger::Warning("Self-intersecting Directrix");
|
||||
}
|
||||
}
|
||||
|
||||
std::set<TopoDS_TShape*> seen;
|
||||
|
||||
auto num_edges = IfcGeom::Kernel::count(wire, TopAbs_EDGE);
|
||||
|
||||
TopoDS_Vertex v0, v1;
|
||||
// @todo this creates the ancestor map twice
|
||||
TopExp::Vertices(wire, v0, v1);
|
||||
|
||||
bool ignore_first_equality_because_closed = v0.IsSame(v1);
|
||||
|
||||
// @todo this probably still does not work on a closed wire consisting of one (circular) edge.
|
||||
|
||||
while ((int)sorted_edges.size() < num_edges &&
|
||||
(!v0.IsSame(v1) || ignore_first_equality_because_closed)) {
|
||||
ignore_first_equality_because_closed = false;
|
||||
if (!map.Contains(v0)) {
|
||||
throw std::runtime_error("Disconnected vertex");
|
||||
}
|
||||
const TopTools_ListOfShape& es = map.FindFromKey(v0);
|
||||
TopoDS_Vertex ve0, ve1;
|
||||
TopTools_ListIteratorOfListOfShape it(es);
|
||||
bool added = false;
|
||||
for (; it.More(); it.Next()) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(it.Value());
|
||||
TopExp::Vertices(e, ve0, ve1, true);
|
||||
if (ve0.IsSame(v0) && seen.find(&*e.TShape()) == seen.end()) {
|
||||
sorted_edges.push_back(e);
|
||||
v0 = ve1;
|
||||
added = true;
|
||||
seen.insert(&*e.TShape());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
throw std::runtime_error("Disconnected edge");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #939: a closed loop causes failed triangulation in 7.3 and artefacts
|
||||
// in 7.4 so we break up a closed wire into two equal parts.
|
||||
void IfcGeom::util::break_closed(const TopoDS_Wire & wire, std::vector<TopoDS_Wire>& wires) {
|
||||
std::vector<TopoDS_Edge> sorted_edges;
|
||||
sort_edges(wire, sorted_edges);
|
||||
|
||||
if (sorted_edges.size() == 1) {
|
||||
wires.push_back(wire);
|
||||
return;
|
||||
}
|
||||
|
||||
BRep_Builder B;
|
||||
|
||||
wires.emplace_back();
|
||||
B.MakeWire(wires.back());
|
||||
|
||||
for (size_t i = 0; i < sorted_edges.size(); ++i) {
|
||||
if (i == sorted_edges.size() / 2) {
|
||||
wires.emplace_back();
|
||||
B.MakeWire(wires.back());
|
||||
}
|
||||
|
||||
const auto& e = sorted_edges[i];
|
||||
B.Add(wires.back(), e);
|
||||
}
|
||||
}
|
||||
|
||||
void IfcGeom::util::segment_adjacent_non_linear(const TopoDS_Wire & wire, std::vector<TopoDS_Wire>& wires) {
|
||||
std::vector<TopoDS_Edge> sorted_edges;
|
||||
sort_edges(wire, sorted_edges);
|
||||
|
||||
BRep_Builder B;
|
||||
double u, v;
|
||||
|
||||
wires.emplace_back();
|
||||
B.MakeWire(wires.back());
|
||||
|
||||
for (int i = 0; i < (int)sorted_edges.size() - 1; ++i) {
|
||||
const auto& e = sorted_edges[i];
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
|
||||
const bool is_linear = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
|
||||
|
||||
const auto& f = sorted_edges[i + 1];
|
||||
crv = BRep_Tool::Curve(f, u, v);
|
||||
const bool next_is_linear = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
|
||||
|
||||
B.Add(wires.back(), e);
|
||||
|
||||
if (!is_linear && !next_is_linear) {
|
||||
wires.emplace_back();
|
||||
B.MakeWire(wires.back());
|
||||
}
|
||||
}
|
||||
|
||||
if (!sorted_edges.empty()) {
|
||||
B.Add(wires.back(), sorted_edges.back());
|
||||
}
|
||||
}
|
||||
|
||||
// @todo make this generic for other sweeps not just swept disk
|
||||
void IfcGeom::util::process_sweep(const TopoDS_Wire & wire, double radius, TopoDS_Shape & result) {
|
||||
std::vector<TopoDS_Wire> wires, wires_tmp;
|
||||
segment_adjacent_non_linear(wire, wires_tmp);
|
||||
for (auto& w : wires_tmp) {
|
||||
break_closed(w, wires);
|
||||
}
|
||||
|
||||
TopoDS_Compound C;
|
||||
BRep_Builder B;
|
||||
if (wires.size() > 1) {
|
||||
B.MakeCompound(C);
|
||||
}
|
||||
|
||||
for (auto& w : wires) {
|
||||
TopoDS_Shape part;
|
||||
|
||||
gp_Ax2 directrix;
|
||||
if (!wire_to_ax(w, directrix)) {
|
||||
continue;
|
||||
}
|
||||
Handle(Geom_Circle) circle = new Geom_Circle(directrix, radius);
|
||||
TopoDS_Wire section = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(circle));
|
||||
|
||||
if (is_single_circular_edge(w)) {
|
||||
process_sweep_as_revolution(w, section, part);
|
||||
} else if (is_single_linear_edge(w)) {
|
||||
process_sweep_as_extrusion(w, section, part);
|
||||
} else {
|
||||
process_sweep_as_pipe(w, section, part);
|
||||
}
|
||||
if (wires.size() > 1) {
|
||||
B.Add(C, part);
|
||||
} else {
|
||||
result = part;
|
||||
}
|
||||
}
|
||||
|
||||
if (wires.size() > 1) {
|
||||
result = C;
|
||||
}
|
||||
|
||||
/*
|
||||
// Eliminate Swept Surfaces?
|
||||
result = ShapeCustom::SweptToElementary(result);
|
||||
|
||||
// Eliminate Trimmed Surfaces?
|
||||
ShapeBuild_ReShape sbrs;
|
||||
BRep_Builder b;
|
||||
TopExp_Explorer exp(result, TopAbs_FACE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
const TopoDS_Face& f = TopoDS::Face(exp.Current());
|
||||
auto S = BRep_Tool::Surface(f);
|
||||
if (S->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface))) {
|
||||
auto RTS = Handle(Geom_RectangularTrimmedSurface)::DownCast(S);
|
||||
auto B = RTS->BasisSurface();
|
||||
TopoDS_Shape newf = f.EmptyCopied();
|
||||
// @todo Is it ok to assume no location?
|
||||
b.MakeFace(TopoDS::Face(newf), B, BRep_Tool::Tolerance(f));
|
||||
sbrs.Replace(f, newf);
|
||||
}
|
||||
}
|
||||
result = sbrs.Apply(result);
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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 SWEEP_UTILS_H
|
||||
#define SWEEP_UTILS_H
|
||||
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <TopoDS_Edge.hxx>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace IfcGeom {
|
||||
namespace util {
|
||||
|
||||
bool wire_is_c1_continuous(const TopoDS_Wire& w, double tol);
|
||||
|
||||
bool wire_to_ax(const TopoDS_Wire& wire, gp_Ax2& directrix);
|
||||
|
||||
bool is_single_linear_edge(const TopoDS_Wire& wire);
|
||||
|
||||
bool is_single_circular_edge(const TopoDS_Wire& wire);
|
||||
|
||||
void process_sweep_as_extrusion(const TopoDS_Wire& wire, const TopoDS_Wire& section, TopoDS_Shape& result);
|
||||
|
||||
void process_sweep_as_revolution(const TopoDS_Wire& wire, const TopoDS_Wire& section, TopoDS_Shape& result);
|
||||
|
||||
void process_sweep_as_pipe(const TopoDS_Wire& wire, const TopoDS_Wire& section, TopoDS_Shape& result, bool force_transformed = false);
|
||||
|
||||
void sort_edges(const TopoDS_Wire& wire, std::vector<TopoDS_Edge>& sorted_edges);
|
||||
|
||||
|
||||
// #939: a closed loop causes failed triangulation in 7.3 and artefacts
|
||||
// in 7.4 so we break up a closed wire into two equal parts.
|
||||
void break_closed(const TopoDS_Wire& wire, std::vector<TopoDS_Wire>& wires);
|
||||
|
||||
void segment_adjacent_non_linear(const TopoDS_Wire& wire, std::vector<TopoDS_Wire>& wires);
|
||||
|
||||
// @todo make this generic for other sweeps not just swept disk
|
||||
void process_sweep(const TopoDS_Wire& wire, double radius, TopoDS_Shape& result);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,226 @@
|
||||
#include "wire_builder.h"
|
||||
|
||||
#include "../ifcparse/IfcLogger.h"
|
||||
#include "../ifcgeom_schema_agnostic/Kernel.h"
|
||||
|
||||
#include <TopExp.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <ShapeBuild_ReShape.hxx>
|
||||
#include <GC_MakeCircle.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <Geom_Line.hxx>
|
||||
#include <Geom_Circle.hxx>
|
||||
#include <GeomAdaptor_Curve.hxx>
|
||||
|
||||
// Returns the first edge of a wire
|
||||
TopoDS_Edge IfcGeom::util::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 IfcGeom::util::adjust(const TopoDS_Wire & w, const TopoDS_Vertex & v, const gp_Pnt & p) {
|
||||
TopTools_IndexedDataMapOfShapeListOfShape map;
|
||||
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, map);
|
||||
|
||||
bool all_linear = true, single_circle = false, first = true;
|
||||
|
||||
const TopTools_ListOfShape& edges = map.FindFromKey(v);
|
||||
TopTools_ListIteratorOfListOfShape it(edges);
|
||||
for (; it.More(); it.Next()) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(it.Value());
|
||||
double _, __;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(e, _, __);
|
||||
const bool is_line = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
|
||||
const bool is_circle = crv->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
||||
all_linear = all_linear && is_line;
|
||||
single_circle = first && is_circle;
|
||||
}
|
||||
|
||||
if (all_linear) {
|
||||
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));
|
||||
} else if (single_circle) {
|
||||
TopoDS_Vertex v1, v2;
|
||||
TopExp::Vertices(w, v1, v2);
|
||||
|
||||
gp_Pnt p1, p2, p3;
|
||||
p1 = v.IsEqual(v1) ? p : BRep_Tool::Pnt(v1);
|
||||
p3 = v.IsEqual(v2) ? p : BRep_Tool::Pnt(v2);
|
||||
|
||||
double a, b;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(edges.First()), a, b);
|
||||
crv->D0((a + b) / 2., p2);
|
||||
|
||||
GC_MakeCircle mc(p1, p2, p3);
|
||||
if (!mc.IsDone()) {
|
||||
throw IfcGeom::geometry_exception("Failed to adjust circle");
|
||||
}
|
||||
|
||||
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(mc.Value(), p1, p3).Edge();
|
||||
BRepBuilderAPI_MakeWire builder;
|
||||
builder.Add(edge);
|
||||
return builder.Wire();
|
||||
} else {
|
||||
throw IfcGeom::geometry_exception("Unexpected wire to adjust");
|
||||
}
|
||||
}
|
||||
|
||||
double IfcGeom::util::deflection_for_approximating_circle(double radius, double param) {
|
||||
return -radius * std::cos(1. / 2. * param) * std::cos(param) - radius * std::sin(1. / 2. * param) * std::sin(param) + radius;
|
||||
}
|
||||
|
||||
bool IfcGeom::util::create_edge_over_curve_with_log_messages(const Handle_Geom_Curve & crv, const double eps, const gp_Pnt & p1, const gp_Pnt & p2, TopoDS_Edge & result) {
|
||||
if (crv->IsClosed() && p1.Distance(p2) <= eps) {
|
||||
BRepBuilderAPI_MakeEdge me(crv);
|
||||
if (me.IsDone()) {
|
||||
result = me.Edge();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BRep_Builder builder;
|
||||
TopoDS_Vertex v1, v2;
|
||||
/// @todo project first and emit warnings accordingly
|
||||
builder.MakeVertex(v1, p1, eps);
|
||||
builder.MakeVertex(v2, p2, eps);
|
||||
|
||||
BRepBuilderAPI_MakeEdge me(crv, v1, v2);
|
||||
if (!me.IsDone()) {
|
||||
const double eps2 = eps * eps;
|
||||
if (me.Error() == BRepBuilderAPI_PointProjectionFailed) {
|
||||
GeomAdaptor_Curve GAC(crv);
|
||||
const gp_Pnt* ps[2] = { &p1, &p2 };
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Extrema_ExtPC extrema(*ps[i], GAC);
|
||||
if (extrema.IsDone()) {
|
||||
int n = extrema.NbExt();
|
||||
double dmin = std::numeric_limits<double>::infinity();
|
||||
for (int j = 1; j <= n; j++) {
|
||||
const double d = extrema.SquareDistance(j);
|
||||
if (d < dmin) {
|
||||
dmin = d;
|
||||
}
|
||||
}
|
||||
if (dmin == std::numeric_limits<double>::infinity()) {
|
||||
Logger::Error("No extrema for point");
|
||||
} else if (dmin > eps2) {
|
||||
Logger::Error("Distance of " + boost::lexical_cast<std::string>(std::sqrt(dmin)) + " exceeds tolerance");
|
||||
}
|
||||
} else {
|
||||
Logger::Error("Failed to calculate extrema for point");
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
result = me.Edge();
|
||||
return true;
|
||||
}
|
||||
|
||||
void IfcGeom::util::wire_builder::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 IfcGeom::util::wire_builder::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 tolerance, 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::Warning("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);
|
||||
|
||||
const bool is_circle1 = c1->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
||||
const bool is_circle2 = c2->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
||||
|
||||
// Preferably adjust the segment that is linear
|
||||
if (is_line1 || (is_circle1 && !is_line2)) {
|
||||
mw_.Add(adjust(w1, w12, p2));
|
||||
Logger::Notice("Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
|
||||
} else if ((is_line2 || is_circle2) && !last) {
|
||||
mw_.Add(w1);
|
||||
override_next_ = true;
|
||||
next_override_ = p1;
|
||||
Logger::Notice("Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
|
||||
} else {
|
||||
// In all other cases an edge is added
|
||||
mw_.Add(w1);
|
||||
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
|
||||
Logger::Warning("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_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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 WIRE_BUILDER_H
|
||||
#define WIRE_BUILDER_H
|
||||
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
|
||||
#include <Geom_Curve.hxx>
|
||||
|
||||
#include <TopoDS_Vertex.hxx>
|
||||
#include <TopoDS_Edge.hxx>
|
||||
#include <TopoDS_Wire.hxx>
|
||||
|
||||
#include <Extrema_ExtPC.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
|
||||
namespace IfcGeom {
|
||||
namespace util {
|
||||
// Returns the first edge of a wire
|
||||
TopoDS_Edge first_edge(const TopoDS_Wire& w);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
void operator()(const TopoDS_Shape& a, const TopoDS_Shape& b, bool last);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Below is code to deduce the formula below in SageMath
|
||||
|
||||
| R, b = var('R b')
|
||||
|
|
||||
| Bxy = R * cos(b), R * sin(b)
|
||||
| Cxy = R * cos(b/2), R * sin(b/2)
|
||||
|
|
||||
| def dot(v, w):
|
||||
| return v[0] * w[0] + v[1] * w[1]
|
||||
|
|
||||
| def norm(v):
|
||||
| l = sqrt(v[0]^2 + v[1]^2)
|
||||
| return v[0] / l, v[1] / l
|
||||
|
|
||||
| (R - R*dot(norm(Cxy), norm(Bxy))).full_simplify()
|
||||
*/
|
||||
|
||||
double deflection_for_approximating_circle(double radius, double param);
|
||||
|
||||
bool create_edge_over_curve_with_log_messages(const Handle_Geom_Curve& crv, const double eps, const gp_Pnt& p1, const gp_Pnt& p2, TopoDS_Edge& result);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,569 @@
|
||||
#include "wire_utils.h"
|
||||
|
||||
#include "../ifcparse/IfcLogger.h"
|
||||
#include "../ifcgeom_schema_agnostic/Kernel.h"
|
||||
#include "../ifcgeom_schema_agnostic/IfcGeomTree.h"
|
||||
|
||||
#include <TopExp.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Iterator.hxx>
|
||||
#include <ShapeFix_Wire.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepTools_WireExplorer.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepAlgo_NormalProjection.hxx>
|
||||
#include <BRepMesh_IncrementalMesh.hxx>
|
||||
#include <BRepBuilderAPI_MakePolygon.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
#include <ShapeExtend_WireData.hxx>
|
||||
#include <Standard_Version.hxx>
|
||||
#include <GeomAPI_ExtremaCurveCurve.hxx>
|
||||
|
||||
#include <boost/range/irange.hpp>
|
||||
#include <boost/range/algorithm_ext/push_back.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
bool IfcGeom::util::approximate_plane_through_wire(const TopoDS_Wire& wire, gp_Pln& plane, double eps_) {
|
||||
// Newell's Method is used for the normal calculation
|
||||
// as a simple edge cross product can give opposite results
|
||||
// for a concave face boundary.
|
||||
// Reference: Graphics Gems III p. 231
|
||||
|
||||
const double eps2 = eps_ * eps_;
|
||||
|
||||
double x = 0, y = 0, z = 0;
|
||||
gp_Pnt current, previous, first;
|
||||
gp_XYZ center;
|
||||
int n = 0;
|
||||
|
||||
BRepTools_WireExplorer exp(wire);
|
||||
|
||||
for (;; exp.Next()) {
|
||||
const bool has_more = exp.More() != 0;
|
||||
if (has_more) {
|
||||
const TopoDS_Vertex& v = exp.CurrentVertex();
|
||||
current = BRep_Tool::Pnt(v);
|
||||
center += current.XYZ();
|
||||
} else {
|
||||
current = first;
|
||||
}
|
||||
if (n) {
|
||||
const double& xn = previous.X();
|
||||
const double& yn = previous.Y();
|
||||
const double& zn = previous.Z();
|
||||
const double& xn1 = current.X();
|
||||
const double& yn1 = current.Y();
|
||||
const double& zn1 = current.Z();
|
||||
x += (yn - yn1)*(zn + zn1);
|
||||
y += (xn + xn1)*(zn - zn1);
|
||||
z += (xn - xn1)*(yn + yn1);
|
||||
} else {
|
||||
first = current;
|
||||
}
|
||||
if (!has_more) {
|
||||
break;
|
||||
}
|
||||
previous = current;
|
||||
++n;
|
||||
}
|
||||
|
||||
if (n < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gp_Vec v(x, y, z);
|
||||
if (v.SquareMagnitude() < eps_ * eps_) {
|
||||
Logger::Warning("Degenerate face boundary in normal estimation");
|
||||
return false;
|
||||
}
|
||||
|
||||
plane = gp_Pln(center / n, v);
|
||||
|
||||
exp.Init(wire);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
const TopoDS_Vertex& v = exp.CurrentVertex();
|
||||
current = BRep_Tool::Pnt(v);
|
||||
if (plane.SquareDistance(current) > eps2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::util::flatten_wire(TopoDS_Wire& wire, double eps) {
|
||||
gp_Pln pln;
|
||||
if (!approximate_plane_through_wire(wire, pln, eps)) {
|
||||
return false;
|
||||
}
|
||||
TopoDS_Face face = BRepBuilderAPI_MakeFace(pln).Face();
|
||||
BRepAlgo_NormalProjection proj(face);
|
||||
proj.Add(wire);
|
||||
proj.Build();
|
||||
if (!proj.IsDone()) {
|
||||
return false;
|
||||
}
|
||||
TopTools_ListOfShape list;
|
||||
proj.BuildWire(list);
|
||||
if (list.Extent() != 1) {
|
||||
return false;
|
||||
}
|
||||
wire = TopoDS::Wire(list.First());
|
||||
return true;
|
||||
}
|
||||
|
||||
IfcGeom::util::triangulate_wire_result IfcGeom::util::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
|
||||
// (due to the Bowyer-Watson super triangle perhaps?). Therefore
|
||||
// alternatively we use the regular OCCT incremental mesher on a new face
|
||||
// created from the UV coordinates of the original wire. Pray to our gods
|
||||
// that the vertex coordinates are unaffected by the meshing algorithm and
|
||||
// map them back to 3d coordinates when iterating over the mesh triangles.
|
||||
|
||||
// In addition, to maintain a manifold shell, we need to make sure that
|
||||
// every edge from the input wire is used exactly once in the list of
|
||||
// resulting faces. And that other internal edges are used twice.
|
||||
|
||||
typedef std::pair<double, double> uv_node;
|
||||
|
||||
gp_Pln pln;
|
||||
if (!approximate_plane_through_wire(wires.front(), pln, std::numeric_limits<double>::infinity())) {
|
||||
return TRIANGULATE_WIRE_FAIL;
|
||||
}
|
||||
|
||||
const gp_XYZ& udir = pln.Position().XDirection().XYZ();
|
||||
const gp_XYZ& vdir = pln.Position().YDirection().XYZ();
|
||||
const gp_XYZ& pnt = pln.Position().Location().XYZ();
|
||||
|
||||
std::map<uv_node, TopoDS_Vertex> mapping;
|
||||
std::map<std::pair<uv_node, uv_node>, TopoDS_Edge> existing_edges, new_edges;
|
||||
|
||||
std::unique_ptr<BRepBuilderAPI_MakeFace> mf;
|
||||
|
||||
for (auto it = wires.begin(); it != wires.end(); ++it) {
|
||||
const TopoDS_Wire& wire = *it;
|
||||
BRepTools_WireExplorer exp(wire);
|
||||
BRepBuilderAPI_MakePolygon mp;
|
||||
|
||||
// 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()));
|
||||
}
|
||||
}
|
||||
|
||||
const TopoDS_Face& face = mf->Face();
|
||||
|
||||
// Create a triangular mesh from the face
|
||||
BRepMesh_IncrementalMesh(face, Precision::Confusion());
|
||||
|
||||
int n123[3];
|
||||
TopLoc_Location loc;
|
||||
Handle_Poly_Triangulation tri = BRep_Tool::Triangulation(face, loc);
|
||||
|
||||
if (!tri.IsNull()) {
|
||||
|
||||
const Poly_Array1OfTriangle& triangles = tri->Triangles();
|
||||
for (int i = 1; i <= triangles.Length(); ++i) {
|
||||
if (face.Orientation() == TopAbs_REVERSED)
|
||||
triangles(i).Get(n123[2], n123[1], n123[0]);
|
||||
else triangles(i).Get(n123[0], n123[1], n123[2]);
|
||||
|
||||
// Create polygons from the mesh vertices
|
||||
BRepBuilderAPI_MakeWire mp2;
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
|
||||
uv_node uvnodes[2];
|
||||
TopoDS_Vertex vs[2];
|
||||
|
||||
for (int k = 0; k < 2; ++k) {
|
||||
const gp_Pnt& uv = tri->Node(n123[(j + k) % 3]);
|
||||
uvnodes[k] = std::make_pair(uv.X(), uv.Y());
|
||||
|
||||
auto it = mapping.find(uvnodes[k]);
|
||||
if (it == mapping.end()) {
|
||||
Logger::Error("Internal error: unable to unproject uv-mesh");
|
||||
return TRIANGULATE_WIRE_FAIL;
|
||||
}
|
||||
|
||||
vs[k] = it->second;
|
||||
}
|
||||
|
||||
auto it = existing_edges.find(std::make_pair(uvnodes[0], uvnodes[1]));
|
||||
if (it != existing_edges.end()) {
|
||||
// This is a boundary edge, reuse existing edge from wire
|
||||
mp2.Add(it->second);
|
||||
} else {
|
||||
auto jt = new_edges.find(std::make_pair(uvnodes[0], uvnodes[1]));
|
||||
if (jt != new_edges.end()) {
|
||||
// We have already added the reverse as part of another
|
||||
// triangle, reuse this edge.
|
||||
mp2.Add(TopoDS::Edge(jt->second));
|
||||
} else {
|
||||
// This is a new internal edge. Register the reverse
|
||||
// for reuse later. We need to be sure to reuse vertices
|
||||
// for the edge construction because otherwise the wire
|
||||
// builder will use geometrical proximity for vertex
|
||||
// connections in which case the edge will be copied
|
||||
// and no longer partner with other edges from the shell.
|
||||
TopoDS_Edge ne = BRepBuilderAPI_MakeEdge(vs[0], vs[1]);
|
||||
mp2.Add(ne);
|
||||
// Store the reverse to be picked up later.
|
||||
new_edges.insert(std::make_pair(std::make_pair(uvnodes[1], uvnodes[0]), TopoDS::Edge(ne.Reversed())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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() != wires.front().Orientation()) {
|
||||
triangle_face.Reverse();
|
||||
}
|
||||
}
|
||||
faces.Append(triangle_face);
|
||||
} else {
|
||||
Logger::Error("Internal error: missing face");
|
||||
return TRIANGULATE_WIRE_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TopTools_IndexedDataMapOfShapeListOfShape mape, mapn;
|
||||
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);
|
||||
}
|
||||
|
||||
// Validation
|
||||
bool non_manifold = false;
|
||||
|
||||
for (int i = 1; i <= mape.Extent(); ++i) {
|
||||
#if OCC_VERSION_HEX >= 0x70000
|
||||
TopTools_ListOfShape val;
|
||||
if (!mapn.FindFromKey(mape.FindKey(i), val)) {
|
||||
#else
|
||||
bool contains = false;
|
||||
try {
|
||||
TopTools_ListOfShape val = mapn.FindFromKey(mape.FindKey(i));
|
||||
contains = true;
|
||||
} catch (Standard_NoSuchObject&) {}
|
||||
if (!contains) {
|
||||
#endif
|
||||
// All existing edges need to exist in the new faces
|
||||
Logger::Error("Internal error, missing edge from triangulation");
|
||||
non_manifold = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i <= mapn.Extent(); ++i) {
|
||||
const TopoDS_Shape& v = mapn.FindKey(i);
|
||||
int n = mapn.FindFromIndex(i).Extent();
|
||||
// Existing edges are boundaries with use 1
|
||||
// New edges are internal with use 2
|
||||
if (n != (mape.Contains(v) ? 1 : 2)) {
|
||||
Logger::Error("Internal error, non-manifold result from triangulation");
|
||||
non_manifold = true;
|
||||
}
|
||||
}
|
||||
|
||||
return non_manifold ? TRIANGULATE_WIRE_NON_MANIFOLD : TRIANGULATE_WIRE_OK;
|
||||
}
|
||||
|
||||
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 = (int)n - 1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bounded_int& operator++() {
|
||||
++i;
|
||||
if (i == (int)n) {
|
||||
i = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator int() { return i; }
|
||||
};
|
||||
}
|
||||
|
||||
bool IfcGeom::util::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, double eps, double eps_real) {
|
||||
if (!wire.Closed()) {
|
||||
wires.Append(wire);
|
||||
return false;
|
||||
}
|
||||
|
||||
int n = IfcGeom::Kernel::count(wire, TopAbs_EDGE);
|
||||
if (n < 3) {
|
||||
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);
|
||||
IfcGeom::impl::tree<int> tree;
|
||||
|
||||
int edge_idx = 0;
|
||||
for (; exp.More(); exp.Next()) {
|
||||
wd->Add(exp.Current());
|
||||
if (n > 64) {
|
||||
// tfk: indices in tree are 0-based vd 1-based in wiredata
|
||||
tree.add(edge_idx++, exp.Current());
|
||||
}
|
||||
}
|
||||
|
||||
if (wd->NbEdges() != n) {
|
||||
// If the number of edges differs, BRepTools_WireExplorer did not
|
||||
// reach every edge, probably due to loops exactly at vertex locations.
|
||||
// This is not supported by this algorithm which only elimates loops
|
||||
// due to edge crossings.
|
||||
|
||||
throw geometry_exception("Invalid loop");
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
// @todo: should this start from 0 in case of n > 64?
|
||||
for (int i = 2; i < n; ++i) {
|
||||
|
||||
std::vector<int> js;
|
||||
if (n > 64) {
|
||||
Bnd_Box b;
|
||||
BRepBndLib::Add(wd->Edge(i + 1), b);
|
||||
b.Enlarge(eps);
|
||||
js = tree.select_box(b, false);
|
||||
} else {
|
||||
boost::push_back(js, boost::irange(0, i - 1));
|
||||
}
|
||||
|
||||
for (std::vector<int>::const_iterator it = js.begin(); it != js.end(); ++it) {
|
||||
int j = *it;
|
||||
|
||||
if (n > 64) {
|
||||
if (j > i) {
|
||||
continue;
|
||||
}
|
||||
if ((std::max)(i, j) - (std::min)(i, j) <= 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Only check non-consecutive edges
|
||||
if (i == n - 1 && j == 0) continue;
|
||||
|
||||
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)
|
||||
);
|
||||
|
||||
// @todo: extend this to work in case of multiple extrema and curved segments.
|
||||
const bool unbounded_intersects = (!ecc.Extrema().IsParallel() && ecc.NbExtrema() == 1 && ecc.Distance(1) < eps);
|
||||
if (unbounded_intersects) {
|
||||
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 (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, true);
|
||||
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) > eps_real * 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;
|
||||
}
|
||||
}
|
||||
|
||||
ShapeFix_Wire sfw;
|
||||
sfw.Load(mw.Wire());
|
||||
sfw.Perform();
|
||||
|
||||
// Recursively process both cuts
|
||||
|
||||
// @todo this is a change in behaviour with eps precomputed from the kernel
|
||||
// instead of adaptively calculated for the successive iterations.
|
||||
wire_intersections(sfw.Wire(), wires, eps, eps_real);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No intersections found, append original wire
|
||||
if (!intersected) {
|
||||
wires.Append(wire);
|
||||
}
|
||||
|
||||
return intersected;
|
||||
}
|
||||
|
||||
void IfcGeom::util::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]);
|
||||
|
||||
// @todo hard coded precision.
|
||||
// @todo this is a really strange measure for wire size. Why not use newell's
|
||||
// method to project to plane and then calculate size of the 2d bbox?
|
||||
const double eps = 1.e-5;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <gp_Pln.hxx>
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ifcopenshell { namespace geometry {
|
||||
namespace util {
|
||||
bool approximate_plane_through_wire(const TopoDS_Wire& wire, gp_Pln& plane, double eps);
|
||||
|
||||
bool flatten_wire(TopoDS_Wire& wire, double eps);
|
||||
|
||||
enum triangulate_wire_result {
|
||||
TRIANGULATE_WIRE_FAIL,
|
||||
TRIANGULATE_WIRE_OK,
|
||||
TRIANGULATE_WIRE_NON_MANIFOLD,
|
||||
};
|
||||
|
||||
/// Triangulate the set of wires. The firstmost wire is assumed to be the outer wire.
|
||||
triangulate_wire_result triangulate_wire(const std::vector<TopoDS_Wire>& wires, TopTools_ListOfShape& faces);
|
||||
|
||||
// eps: tolerance added to wire intersection checks, can be zero
|
||||
// eps_real: tolerance used to construct new edge geometry around intersection points, cannot be zero
|
||||
bool wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, double eps, double eps_real);
|
||||
|
||||
void select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest);
|
||||
}
|
||||
} }
|
||||
@@ -0,0 +1,122 @@
|
||||
#include "profile_helper.h"
|
||||
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::loop* ifcopenshell::geometry::polygon_from_points(const std::vector<taxonomy::point3>& ps, bool external) {
|
||||
auto loop = new taxonomy::loop();
|
||||
loop->external = external;
|
||||
boost::optional<taxonomy::point3> previous;
|
||||
for (auto& p : ps) {
|
||||
if (previous) {
|
||||
auto e = new taxonomy::edge;
|
||||
e->start = *previous;
|
||||
e->end = p;
|
||||
loop->children.push_back(e);
|
||||
}
|
||||
previous = p;
|
||||
}
|
||||
return loop;
|
||||
}
|
||||
|
||||
taxonomy::loop* ifcopenshell::geometry::profile_helper(Eigen::Matrix4d& m4, const std::vector<profile_point>& points) {
|
||||
|
||||
/* TopoDS_Vertex* vertices = new TopoDS_Vertex[numVerts];
|
||||
|
||||
for (int i = 0; i < numVerts; i++) {
|
||||
gp_XY xy(verts[2 * i], verts[2 * i + 1]);
|
||||
trsf.Transforms(xy);
|
||||
vertices[i] = BRepBuilderAPI_MakeVertex(gp_Pnt(xy.X(), xy.Y(), 0.0f));
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
for (int i = 0; i < numVerts; i++)
|
||||
w.Add(BRepBuilderAPI_MakeEdge(vertices[i], vertices[(i + 1) % numVerts]));
|
||||
|
||||
TopoDS_Face face;
|
||||
convert_wire_to_face(w.Wire(), face);
|
||||
|
||||
if (numFillets && *std::max_element(filletRadii, filletRadii + numFillets) > ALMOST_ZERO) {
|
||||
BRepFilletAPI_MakeFillet2d fillet(face);
|
||||
for (int i = 0; i < numFillets; i++) {
|
||||
const double radius = filletRadii[i];
|
||||
if (radius <= ALMOST_ZERO) continue;
|
||||
fillet.AddFillet(vertices[filletIndices[i]], radius);
|
||||
}
|
||||
fillet.Build();
|
||||
if (fillet.IsDone()) {
|
||||
face = TopoDS::Face(fillet.Shape());
|
||||
} else {
|
||||
Logger::Error("Failed to process profile fillets");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
const bool has_position = !m4.isIdentity();
|
||||
|
||||
// @todo precision
|
||||
|
||||
std::vector<taxonomy::point3> ps;
|
||||
ps.reserve(points.size() + 1);
|
||||
std::transform(points.begin(), points.end(), std::back_inserter(ps), [&has_position, &m4](const profile_point& p) {
|
||||
if (has_position) {
|
||||
Eigen::Vector4d v(p.xy[0], p.xy[1], 0., 1.);
|
||||
v = m4 * v;
|
||||
return taxonomy::point3(v(0), v(1), 0.);
|
||||
} else {
|
||||
return taxonomy::point3(p.xy[0], p.xy[1], 0.);
|
||||
}
|
||||
});
|
||||
ps.push_back(ps.front());
|
||||
|
||||
auto loop = polygon_from_points(ps);
|
||||
|
||||
std::vector<profile_point_with_edges> pps(points.size());
|
||||
for (int b = 0; b < points.size(); ++b) {
|
||||
int c = (b - 1) % points.size();
|
||||
pps[b] = { Eigen::Vector2d(points[b].xy[0], points[b].xy[1]), points[b].radius, (taxonomy::edge*) loop->children[c], (taxonomy::edge*) loop->children[b] };
|
||||
}
|
||||
|
||||
size_t i = pps.size();
|
||||
while (i--) {
|
||||
const auto& p = pps[i];
|
||||
if (p.radius && *p.radius > 0.) {
|
||||
// Position is a IfcAxis2Placement2D, so should remain 2d points
|
||||
auto p0 = boost::get<taxonomy::point3>(p.previous->start).components_->head<2>();
|
||||
auto p1a = boost::get<taxonomy::point3>(p.previous->end).components_->head<2>();
|
||||
auto p2 = boost::get<taxonomy::point3>(p.next->end).components_->head<2>();
|
||||
auto p1b = boost::get<taxonomy::point3>(p.next->start).components_->head<2>();
|
||||
|
||||
auto ba_ = p0 - p1a;
|
||||
auto bc_ = p2 - p1b;
|
||||
|
||||
auto ba = ba_.normalized();
|
||||
auto bc = bc_.normalized();
|
||||
|
||||
const double angle = std::acos(ba.dot(bc));
|
||||
const double inset = *p.radius / std::tan(angle / 2.);
|
||||
|
||||
boost::get<taxonomy::point3>(p.previous->end).components_->head<2>() += ba * inset;
|
||||
boost::get<taxonomy::point3>(p.next->start).components_->head<2>() += bc * inset;
|
||||
|
||||
auto e = new taxonomy::edge;
|
||||
e->start = p.previous->end;
|
||||
e->end = p.next->start;
|
||||
|
||||
auto ab = Eigen::Vector3d(-ba(1), +ba(0), 0.);
|
||||
|
||||
double sign = ab.head<2>().dot(bc) > 0 ? 1. : -1.;
|
||||
|
||||
auto O = boost::get<taxonomy::point3>(p.previous->end).ccomponents().head<3>() + ab * *p.radius * sign;
|
||||
|
||||
auto c = new taxonomy::circle;
|
||||
c->matrix.components_ = new Eigen::Matrix4d(Eigen::Affine3d(Eigen::Translation3d(O)).matrix());
|
||||
c->radius = *p.radius;
|
||||
e->basis = c;
|
||||
c->orientation.reset(sign == -1.);
|
||||
|
||||
loop->children.insert(std::find(loop->children.begin(), loop->children.end(), p.next), e);
|
||||
}
|
||||
};
|
||||
|
||||
return loop;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "taxonomy.h"
|
||||
|
||||
namespace ifcopenshell {
|
||||
|
||||
namespace geometry {
|
||||
|
||||
struct profile_point {
|
||||
std::array<double, 2> xy;
|
||||
boost::optional<double> radius;
|
||||
};
|
||||
|
||||
struct profile_point_with_edges {
|
||||
Eigen::Vector2d xy;
|
||||
boost::optional<double> radius;
|
||||
taxonomy::edge *previous, *next;
|
||||
};
|
||||
|
||||
taxonomy::loop* polygon_from_points(const std::vector<taxonomy::point3>& ps, bool external = true);
|
||||
|
||||
taxonomy::loop* profile_helper(Eigen::Matrix4d& m4, const std::vector<profile_point>& points);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcArbitraryClosedProfileDef* inst) {
|
||||
auto loop = map(inst->OuterCurve());
|
||||
if (loop) {
|
||||
auto face = new taxonomy::face;
|
||||
((taxonomy::loop*)loop)->external = true;
|
||||
face->children = { loop };
|
||||
if (inst->as<IfcSchema::IfcArbitraryProfileDefWithVoids>()) {
|
||||
auto with_voids = inst->as<IfcSchema::IfcArbitraryProfileDefWithVoids>();
|
||||
auto voids = with_voids->InnerCurves();
|
||||
for (auto& v : *voids) {
|
||||
auto inner_loop = map(v);
|
||||
if (inner_loop) {
|
||||
((taxonomy::loop*)inner_loop)->external = false;
|
||||
face->children.push_back(inner_loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
return face;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcAxis2Placement2D* inst) {
|
||||
Eigen::Vector3d P, axis(0, 0, 1), V(1, 0, 0);
|
||||
{
|
||||
taxonomy::point3 v = as<taxonomy::point3>(map(inst->Location()));
|
||||
P = *v.components_;
|
||||
}
|
||||
const bool hasRef = inst->hasRefDirection();
|
||||
if (hasRef) {
|
||||
taxonomy::direction3 v = as<taxonomy::direction3>(map(inst->RefDirection()));
|
||||
V = *v.components_;
|
||||
}
|
||||
return new taxonomy::matrix4(P, axis, V);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcAxis2Placement3D* inst) {
|
||||
Eigen::Vector3d o, axis(0, 0, 1), refDirection, X(1, 0, 0);
|
||||
{
|
||||
taxonomy::point3 v = as<taxonomy::point3>(map(inst->Location()));
|
||||
o = *v.components_;
|
||||
}
|
||||
const bool hasAxis = inst->hasAxis();
|
||||
const bool hasRef = inst->hasRefDirection();
|
||||
|
||||
if (hasAxis != hasRef) {
|
||||
Logger::Warning("Axis and RefDirection should be specified together", inst);
|
||||
}
|
||||
|
||||
if (hasAxis) {
|
||||
taxonomy::direction3 v = as<taxonomy::direction3>(map(inst->Axis()));
|
||||
axis = *v.components_;
|
||||
}
|
||||
|
||||
if (hasRef) {
|
||||
taxonomy::direction3 v = as<taxonomy::direction3>(map(inst->RefDirection()));
|
||||
refDirection = *v.components_;
|
||||
} else {
|
||||
if (acos(axis.dot(X)) > 1.e-5) {
|
||||
refDirection = { 1., 0., 0. };
|
||||
} else {
|
||||
refDirection = { 0., 0., 1. };
|
||||
}
|
||||
auto Xvec = axis.dot(refDirection) * axis;
|
||||
auto Xaxis = refDirection - Xvec;
|
||||
refDirection = Xaxis;
|
||||
}
|
||||
return new taxonomy::matrix4(o, axis, refDirection);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
namespace {
|
||||
taxonomy::boolean_result::operation_t boolean_op_type(IfcSchema::IfcBooleanOperator::Value op) {
|
||||
if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) {
|
||||
return taxonomy::boolean_result::SUBTRACTION;
|
||||
} else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION) {
|
||||
return taxonomy::boolean_result::INTERSECTION;
|
||||
} else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION) {
|
||||
return taxonomy::boolean_result::UNION;
|
||||
} else {
|
||||
throw taxonomy::topology_error("Unknown boolean operation");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcBooleanResult* inst) {
|
||||
IfcSchema::IfcBooleanOperand* operand1 = inst->FirstOperand();
|
||||
IfcSchema::IfcBooleanOperand* operand2 = inst->SecondOperand();
|
||||
|
||||
std::vector<IfcUtil::IfcBaseClass*> operands = { operand2 };
|
||||
|
||||
auto op = boolean_op_type(inst->Operator());
|
||||
|
||||
bool process_as_list = true;
|
||||
while (true) {
|
||||
auto res1 = operand1->as<IfcSchema::IfcBooleanResult>();
|
||||
if (res1) {
|
||||
if (boolean_op_type(res1->Operator()) == op) {
|
||||
operand1 = res1->FirstOperand();
|
||||
operands.push_back(res1->SecondOperand());
|
||||
} else {
|
||||
process_as_list = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
operands.push_back(operand1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (process_as_list) {
|
||||
std::reverse(operands.begin(), operands.end());
|
||||
} else {
|
||||
operand1 = inst->FirstOperand();
|
||||
operands.clear();
|
||||
operands.push_back(operand1);
|
||||
operands.push_back(operand2);
|
||||
}
|
||||
|
||||
auto br = map_to_collection<taxonomy::boolean_result>(this, &operands);
|
||||
if (br) {
|
||||
br->operation = op;
|
||||
}
|
||||
return br;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCartesianPoint* inst) {
|
||||
auto coords = inst->Coordinates();
|
||||
return new taxonomy::point3(
|
||||
coords.size() >= 1 ? coords[0] * length_unit_ : 0.,
|
||||
coords.size() >= 2 ? coords[1] * length_unit_ : 0.,
|
||||
coords.size() >= 3 ? coords[2] * length_unit_ : 0.
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCartesianTransformationOperator2D* inst) {
|
||||
auto m = new taxonomy::matrix4;
|
||||
|
||||
Eigen::Vector4d origin, axis1(1.0, 0.0, 0.0, 0.0), axis2(0.0, 1.0, 0.0, 0.0), axis3(0.0, 0.0, 1.0, 0.0);
|
||||
|
||||
taxonomy::point3 O = as<taxonomy::point3>(map(inst->LocalOrigin()));
|
||||
origin << *O.components_, 1.0;
|
||||
|
||||
if (inst->hasAxis1()) {
|
||||
taxonomy::direction3 ax1 = as<taxonomy::direction3>(map(inst->Axis1()));
|
||||
axis1 << *ax1.components_, 0.0;
|
||||
}
|
||||
if (inst->hasAxis2()) {
|
||||
taxonomy::direction3 ax2 = as<taxonomy::direction3>(map(inst->Axis1()));
|
||||
axis2 << *ax2.components_, 0.0;
|
||||
}
|
||||
|
||||
double scale1, scale2;
|
||||
scale1 = scale2 = 1.0;
|
||||
|
||||
if (inst->hasScale()) {
|
||||
scale1 = inst->Scale();
|
||||
}
|
||||
if (inst->as<IfcSchema::IfcCartesianTransformationOperator2DnonUniform>()) {
|
||||
auto nu = inst->as<IfcSchema::IfcCartesianTransformationOperator2DnonUniform>();
|
||||
scale2 = nu->hasScale2() ? nu->Scale2() : scale1;
|
||||
}
|
||||
|
||||
m->components() <<
|
||||
axis1 * scale1,
|
||||
axis2 * scale2,
|
||||
axis3,
|
||||
origin;
|
||||
|
||||
m->components().transposeInPlace();
|
||||
|
||||
return m;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCartesianTransformationOperator3D* inst) {
|
||||
auto m = new taxonomy::matrix4;
|
||||
|
||||
Eigen::Vector4d origin;
|
||||
Eigen::Vector4d axis1(1., 0., 0., 0.);
|
||||
Eigen::Vector4d axis2(0., 1., 0., 0.);
|
||||
Eigen::Vector4d axis3(0., 0., 1., 0.);
|
||||
|
||||
taxonomy::point3 O = as<taxonomy::point3>(map(inst->LocalOrigin()));
|
||||
origin << *O.components_, 1.0;
|
||||
|
||||
if (inst->hasAxis1()) {
|
||||
taxonomy::direction3 ax1 = as<taxonomy::direction3>(map(inst->Axis1()));
|
||||
axis1 << *ax1.components_, 0.0;
|
||||
}
|
||||
if (inst->hasAxis2()) {
|
||||
taxonomy::direction3 ax2 = as<taxonomy::direction3>(map(inst->Axis2()));
|
||||
axis2 << *ax2.components_, 0.0;
|
||||
}
|
||||
if (inst->hasAxis3()) {
|
||||
taxonomy::direction3 ax3 = as<taxonomy::direction3>(map(inst->Axis3()));
|
||||
axis3 << *ax3.components_, 0.0;
|
||||
}
|
||||
|
||||
double scale1, scale2, scale3;
|
||||
scale1 = scale2 = scale3 = 1.;
|
||||
|
||||
if (inst->hasScale()) {
|
||||
scale1 = inst->Scale();
|
||||
}
|
||||
if (inst->as<IfcSchema::IfcCartesianTransformationOperator3DnonUniform>()) {
|
||||
auto nu = inst->as<IfcSchema::IfcCartesianTransformationOperator3DnonUniform>();
|
||||
scale2 = nu->hasScale2() ? nu->Scale2() : scale1;
|
||||
scale3 = nu->hasScale3() ? nu->Scale3() : scale1;
|
||||
}
|
||||
|
||||
Eigen::Matrix4d tmp;
|
||||
tmp <<
|
||||
axis1 * scale1,
|
||||
axis2 * scale2,
|
||||
axis3 * scale3,
|
||||
origin;
|
||||
|
||||
m->components() = tmp.inverse();
|
||||
m->components().transposeInPlace();
|
||||
|
||||
// @todo tag identity?
|
||||
|
||||
return m;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCircle* inst) {
|
||||
auto c = new taxonomy::circle;
|
||||
c->matrix = as<taxonomy::matrix4>(map(inst->Position()));
|
||||
c->radius = inst->Radius() * length_unit_;
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCircleProfileDef* inst) {
|
||||
std::vector<double> radii = { inst->Radius() * length_unit_ };
|
||||
|
||||
if (inst->as<IfcSchema::IfcCircleHollowProfileDef>()) {
|
||||
double t = inst->as<IfcSchema::IfcCircleHollowProfileDef>()->WallThickness() * length_unit_;
|
||||
radii.push_back(radii.front() - t);
|
||||
}
|
||||
|
||||
auto f = new taxonomy::face;
|
||||
|
||||
for (auto it = radii.begin(); it != radii.end(); ++it) {
|
||||
const double r = *it;
|
||||
const bool exterior = it == radii.begin();
|
||||
|
||||
auto c = new taxonomy::circle;
|
||||
c->radius = r;
|
||||
|
||||
bool has_position = true;
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = inst->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
taxonomy::matrix4 m = as<taxonomy::matrix4>(map(inst->Position()));
|
||||
if (m.components_) {
|
||||
c->matrix = *m.components_;
|
||||
}
|
||||
}
|
||||
|
||||
auto e = new taxonomy::edge;
|
||||
e->basis = c;
|
||||
e->start = 0.;
|
||||
e->end = 2 * boost::math::constants::pi<double>();
|
||||
|
||||
auto l = new taxonomy::loop;
|
||||
l->children = { e };
|
||||
l->external = exterior;
|
||||
|
||||
f->children.push_back(l);
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) {
|
||||
auto loop = new taxonomy::loop;
|
||||
auto segments = inst->Segments();
|
||||
for (auto& segment : *segments) {
|
||||
auto crv = map(segment->ParentCurve());
|
||||
if (crv) {
|
||||
if (crv->kind() == taxonomy::EDGE) {
|
||||
((taxonomy::edge*)crv)->orientation_2.reset(segment->SameSense());
|
||||
loop->children.push_back(crv);
|
||||
} else if (crv->kind() == taxonomy::LOOP) {
|
||||
if (!segment->SameSense()) {
|
||||
crv->reverse();
|
||||
}
|
||||
auto curve_segments = ((taxonomy::loop*)crv)->children_as<taxonomy::edge>();
|
||||
for (auto& s : curve_segments) {
|
||||
loop->children.push_back(s);
|
||||
}
|
||||
// @todo delete crv without children
|
||||
}
|
||||
}
|
||||
}
|
||||
IfcEntityList::ptr profile = inst->data().getInverse(&IfcSchema::IfcProfileDef::Class(), -1);
|
||||
const bool force_close = profile && profile->size() > 0;
|
||||
loop->closed = force_close;
|
||||
return loop;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcConnectedFaceSet* inst) {
|
||||
auto shell = map_to_collection<taxonomy::shell>(this, inst->CfsFaces());
|
||||
if (shell == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
shell->closed = inst->declaration().is(IfcSchema::IfcClosedShell::Class());
|
||||
return shell;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcDirection* inst) {
|
||||
auto coords = inst->DirectionRatios();
|
||||
return new taxonomy::direction3(
|
||||
coords.size() >= 1 ? coords[0] : 0.,
|
||||
coords.size() >= 2 ? coords[1] : 0.,
|
||||
coords.size() >= 3 ? coords[2] : 0.
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcExtrudedAreaSolid* inst) {
|
||||
return new taxonomy::extrusion(
|
||||
as<taxonomy::matrix4>(map(inst->Position())),
|
||||
as<taxonomy::face>(map(inst->SweptArea())),
|
||||
as<taxonomy::direction3>(map(inst->ExtrudedDirection())),
|
||||
inst->Depth() * length_unit_
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcFace* inst) {
|
||||
taxonomy::face* face = new taxonomy::face;
|
||||
auto bounds = inst->Bounds();
|
||||
for (auto& bound : *bounds) {
|
||||
if (auto r = map(bound->Bound())) {
|
||||
if (!bound->Orientation()) {
|
||||
r->reverse();
|
||||
}
|
||||
if (bound->declaration().is(IfcSchema::IfcFaceOuterBound::Class())) {
|
||||
((taxonomy::loop*)r)->external = true;
|
||||
/*
|
||||
// Make a copy in case we need immutability later for e.g. caching
|
||||
auto s = r->clone();
|
||||
((taxonomy::loop*)s)->external = true;
|
||||
delete r;
|
||||
r = s;
|
||||
*/
|
||||
}
|
||||
face->children.push_back(r);
|
||||
}
|
||||
}
|
||||
if (face->children.empty()) {
|
||||
delete face;
|
||||
return nullptr;
|
||||
}
|
||||
return face;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcFaceBasedSurfaceModel* inst) {
|
||||
return map_to_collection(this, inst->FbsmFaces());
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcGeometricSet* inst) {
|
||||
return map_to_collection(this, inst->Elements());
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcHalfSpaceSolid* inst) {
|
||||
IfcSchema::IfcSurface* surface = inst->BaseSurface();
|
||||
if (!surface->declaration().is(IfcSchema::IfcPlane::Class())) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Unsupported BaseSurface:", surface);
|
||||
return nullptr;
|
||||
}
|
||||
auto p = new taxonomy::plane;
|
||||
p->matrix = as<taxonomy::matrix4>(map(((IfcSchema::IfcPlane*)surface)->Position()));
|
||||
p->orientation.reset(!inst->AgreementFlag());
|
||||
auto f = new taxonomy::face;
|
||||
f->basis = p;
|
||||
return f;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
#include "../profile_helper.h"
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcIShapeProfileDef* inst) {
|
||||
const double x1 = inst->OverallWidth() / 2.0f * length_unit_;
|
||||
const double y = inst->OverallDepth() / 2.0f * length_unit_;
|
||||
const double d1 = inst->WebThickness() / 2.0f * length_unit_;
|
||||
const double dy1 = inst->FlangeThickness() * length_unit_;
|
||||
|
||||
bool doFillet1 = inst->hasFilletRadius();
|
||||
double f1 = 0.;
|
||||
if (doFillet1) {
|
||||
f1 = inst->FilletRadius() * length_unit_;
|
||||
}
|
||||
|
||||
bool doFillet2 = doFillet1;
|
||||
double x2 = x1, dy2 = dy1, f2 = f1;
|
||||
|
||||
if (inst->declaration().is(IfcSchema::IfcAsymmetricIShapeProfileDef::Class())) {
|
||||
IfcSchema::IfcAsymmetricIShapeProfileDef* assym = (IfcSchema::IfcAsymmetricIShapeProfileDef*) inst;
|
||||
x2 = assym->TopFlangeWidth() / 2. * length_unit_;
|
||||
doFillet2 = assym->hasTopFlangeFilletRadius();
|
||||
if (doFillet2) {
|
||||
f2 = assym->TopFlangeFilletRadius() * length_unit_;
|
||||
}
|
||||
if (assym->hasTopFlangeThickness()) {
|
||||
dy2 = assym->TopFlangeThickness() * length_unit_;
|
||||
}
|
||||
}
|
||||
|
||||
// @todo
|
||||
const double precision_ = 1.e-5;
|
||||
|
||||
if (x1 < precision_ || x2 < precision_ || y < precision_ || d1 < precision_ || dy1 < precision_ || dy2 < precision_) {
|
||||
Logger::Message(Logger::LOG_NOTICE, "Skipping zero sized profile:", inst);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Eigen::Matrix4d m4;
|
||||
bool has_position = true;
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = inst->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
taxonomy::matrix4 m = as<taxonomy::matrix4>(map(inst->Position()));
|
||||
m4 = m.ccomponents();
|
||||
}
|
||||
|
||||
return profile_helper(m4, {
|
||||
{{-x1,-y}},
|
||||
{{x1,-y}},
|
||||
{{x1,-y + dy1}},
|
||||
{{d1,-y + dy1}, f1},
|
||||
{{d1,y - dy2}, f2},
|
||||
{{x2,y - dy2}},
|
||||
{{x2,y}},
|
||||
{{-x2,y}},
|
||||
{{-x2,y - dy2}},
|
||||
{{-d1,y - dy2}, f2},
|
||||
{{-d1,-y + dy1}, f1},
|
||||
{{-x1,-y + dy1}}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcLocalPlacement* inst) {
|
||||
IfcSchema::IfcLocalPlacement* current = (IfcSchema::IfcLocalPlacement*)inst;
|
||||
auto m4 = new taxonomy::matrix4;
|
||||
for (;;) {
|
||||
IfcSchema::IfcAxis2Placement* relplacement = current->RelativePlacement();
|
||||
if (relplacement->declaration().is(IfcSchema::IfcAxis2Placement3D::Class())) {
|
||||
taxonomy::matrix4 trsf2 = as<taxonomy::matrix4>(map(relplacement));
|
||||
// @todo check
|
||||
m4->components() = trsf2.ccomponents() * m4->ccomponents();
|
||||
}
|
||||
if (current->hasPlacementRelTo()) {
|
||||
IfcSchema::IfcObjectPlacement* parent = current->PlacementRelTo();
|
||||
IfcSchema::IfcProduct::list::ptr parentPlaces = parent->PlacesObject();
|
||||
bool parentPlacesType = false;
|
||||
for (IfcSchema::IfcProduct::list::it iter = parentPlaces->begin();
|
||||
iter != parentPlaces->end(); ++iter) {
|
||||
if ((*iter)->declaration().is(*placement_rel_to_)) {
|
||||
parentPlacesType = true;
|
||||
}
|
||||
}
|
||||
if (parentPlacesType) {
|
||||
break;
|
||||
} else if (parent->declaration().is(IfcSchema::IfcLocalPlacement::Class())) {
|
||||
current = (IfcSchema::IfcLocalPlacement*)current->PlacementRelTo();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return m4;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcManifoldSolidBrep* inst) {
|
||||
// @todo voids
|
||||
return map(inst->Outer());
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcMappedItem* inst) {
|
||||
IfcSchema::IfcCartesianTransformationOperator* transform = inst->MappingTarget();
|
||||
taxonomy::matrix4 gtrsf = as<taxonomy::matrix4>(map(transform));
|
||||
IfcSchema::IfcRepresentationMap* rmap = inst->MappingSource();
|
||||
IfcSchema::IfcAxis2Placement* placement = rmap->MappingOrigin();
|
||||
taxonomy::matrix4 trsf2 = as<taxonomy::matrix4>(map(placement));
|
||||
// Cannot be nullptr here
|
||||
*gtrsf.components_ = *gtrsf.components_ * *trsf2.components_;
|
||||
|
||||
// @todo immutable for caching?
|
||||
// @todo allow for multiple levels of matrix?
|
||||
auto shapes = map(rmap->MappedRepresentation());
|
||||
if (shapes == nullptr) {
|
||||
return shapes;
|
||||
}
|
||||
|
||||
auto collection = new taxonomy::collection;
|
||||
collection->children.push_back(shapes);
|
||||
collection->matrix = *gtrsf.components_;
|
||||
|
||||
if (shapes != nullptr) {
|
||||
for (auto& c : ((taxonomy::collection*)shapes)->children) {
|
||||
// @todo previously style was also copied.
|
||||
}
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcPolyLoop* inst) {
|
||||
taxonomy::loop* loop = new taxonomy::loop;
|
||||
|
||||
taxonomy::point3 first, previous;
|
||||
bool is_first = true;
|
||||
|
||||
auto points = inst->Polygon();
|
||||
for (auto& point : *points) {
|
||||
auto p = as<taxonomy::point3>(map(point));
|
||||
if (is_first) {
|
||||
previous = first = p;
|
||||
is_first = false;
|
||||
} else {
|
||||
auto edge = new taxonomy::edge;
|
||||
edge->start = previous;
|
||||
edge->end = p;
|
||||
loop->children.push_back(edge);
|
||||
previous = p;
|
||||
}
|
||||
}
|
||||
|
||||
auto edge = new taxonomy::edge;
|
||||
edge->start = previous;
|
||||
edge->end = first;
|
||||
loop->children.push_back(edge);
|
||||
|
||||
if (loop->children.size() < 3) {
|
||||
Logger::Warning("Not enough edges for", inst);
|
||||
delete loop;
|
||||
return nullptr;
|
||||
}
|
||||
return loop;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcPolygonalBoundedHalfSpace* inst) {
|
||||
auto f = map_impl((IfcSchema::IfcHalfSpaceSolid*) inst);
|
||||
((taxonomy::face*)f)->children = { map(inst->PolygonalBoundary()) };
|
||||
((taxonomy::face*)f)->matrix = as<taxonomy::matrix4>(map(inst->Position()));
|
||||
return f;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
#include "../profile_helper.h"
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcPolyline* inst) {
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points = inst->Points();
|
||||
|
||||
// @todo
|
||||
const double precision_ = 1.e-5;
|
||||
|
||||
// Parse and store the points in a sequence
|
||||
std::vector<taxonomy::point3> polygon;
|
||||
polygon.reserve(points->size());
|
||||
std::transform(points->begin(), points->end(), std::back_inserter(polygon), [this](const IfcSchema::IfcCartesianPoint* p) {
|
||||
return as<taxonomy::point3>(map(p));
|
||||
});
|
||||
|
||||
const double eps = precision_ * 10;
|
||||
const bool closed_by_proximity = polygon.size() >= 3 && (*polygon.front().components_ - *polygon.back().components_).norm() < eps;
|
||||
|
||||
// @todo this removes the end point, since it's identical to the beginning.
|
||||
if (closed_by_proximity) {
|
||||
// polygon.resize(polygon.size() - 1);
|
||||
}
|
||||
|
||||
// Remove points that are too close to one another
|
||||
// remove_duplicate_points_from_loop(polygon, closed_by_proximity, eps);
|
||||
|
||||
if (polygon.size() < 2) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return polygon_from_points(polygon);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcProduct* inst) {
|
||||
const bool use_body = !this->settings_.get(ifcopenshell::geometry::settings::INCLUDE_CURVES);
|
||||
|
||||
auto openings = find_openings(inst);
|
||||
// @todo const cast
|
||||
auto reps = inst->data().file->traverse((IfcSchema::IfcProduct*) inst, 2)->as<IfcSchema::IfcRepresentation>();
|
||||
IfcSchema::IfcRepresentation* body = nullptr;
|
||||
for (auto& rep : *reps) {
|
||||
if ((rep->RepresentationIdentifier() == "Body") == use_body) {
|
||||
body = rep;
|
||||
}
|
||||
}
|
||||
if (!body) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto c = new taxonomy::collection;
|
||||
c->matrix = as<taxonomy::matrix4>(map(inst->ObjectPlacement()));
|
||||
|
||||
const IfcSchema::IfcMaterial* single_material = get_single_material_association(inst);
|
||||
if (single_material) {
|
||||
auto material_style = map(single_material);
|
||||
if (material_style) {
|
||||
c->surface_style = (taxonomy::style*) material_style;
|
||||
}
|
||||
}
|
||||
|
||||
if (openings->size() && !settings_.get(settings::DISABLE_OPENING_SUBTRACTIONS) && use_body) {
|
||||
|
||||
Eigen::Matrix4d ci;
|
||||
if (c->matrix.components_) {
|
||||
ci = c->matrix.components_->inverse();
|
||||
} else {
|
||||
ci.setIdentity();
|
||||
}
|
||||
|
||||
IfcEntityList::ptr operands(new IfcEntityList);
|
||||
operands->push(body);
|
||||
operands->push(openings);
|
||||
auto n = map_to_collection<taxonomy::boolean_result>(this, operands);
|
||||
std::for_each(n->children.begin() + 1, n->children.end(), [&ci](taxonomy::item* i) {
|
||||
((taxonomy::geom_item*)i)->matrix.components() = ci * ((taxonomy::geom_item*)i)->matrix.ccomponents();
|
||||
});
|
||||
n->operation = taxonomy::boolean_result::SUBTRACTION;
|
||||
// @todo one indirection too many
|
||||
n->instance = inst;
|
||||
c->children = { n };
|
||||
} else {
|
||||
auto child = map(body);
|
||||
if (child) {
|
||||
c->children = { child };
|
||||
} else {
|
||||
delete c;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
#include "../profile_helper.h"
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcRectangleHollowProfileDef* inst) {
|
||||
const double x = inst->XDim() / 2.0f * length_unit_;
|
||||
const double y = inst->YDim() / 2.0f * length_unit_;
|
||||
const double d = inst->WallThickness() * length_unit_;
|
||||
|
||||
boost::optional<double> radius1, radius2;
|
||||
if (inst->hasOuterFilletRadius()) {
|
||||
radius1 = inst->OuterFilletRadius() * length_unit_;
|
||||
}
|
||||
if (inst->hasInnerFilletRadius()) {
|
||||
radius2 = inst->InnerFilletRadius() * length_unit_;
|
||||
}
|
||||
|
||||
// @todo
|
||||
const double precision_ = 1.e-5;
|
||||
|
||||
if (x < precision_ || y < precision_) {
|
||||
Logger::Message(Logger::LOG_NOTICE, "Skipping zero sized profile:", inst);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Eigen::Matrix4d m4;
|
||||
bool has_position = true;
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = inst->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
taxonomy::matrix4 m = as<taxonomy::matrix4>(map(inst->Position()));
|
||||
m4 = m.ccomponents();
|
||||
}
|
||||
|
||||
auto outer_loop = profile_helper(m4, {
|
||||
{{-x, -y}, radius1},
|
||||
{{+x, -y}, radius1},
|
||||
{{+x, +y}, radius1},
|
||||
{{-x, +y}, radius1},
|
||||
});
|
||||
outer_loop->external = true;
|
||||
|
||||
auto inner_loop = profile_helper(m4, {
|
||||
{{-x + d, -y + d}, radius2},
|
||||
{{+x - d, -y + d}, radius2},
|
||||
{{+x - d, +y - d}, radius2},
|
||||
{{-x + d, +y - d}, radius2},
|
||||
});
|
||||
inner_loop->reverse();
|
||||
inner_loop->external = false;
|
||||
|
||||
auto face = new taxonomy::face;
|
||||
face->children = { outer_loop, inner_loop };
|
||||
|
||||
// @todo is this necessary;
|
||||
std::swap(outer_loop->matrix, face->matrix);
|
||||
inner_loop->matrix = outer_loop->matrix;
|
||||
|
||||
return face;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
#include "../profile_helper.h"
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcRectangleProfileDef* inst) {
|
||||
const double x = inst->XDim() / 2.0f * length_unit_;
|
||||
const double y = inst->YDim() / 2.0f * length_unit_;
|
||||
boost::optional<double> radius;
|
||||
if (inst->as<IfcSchema::IfcRoundedRectangleProfileDef>()) {
|
||||
radius = inst->as<IfcSchema::IfcRoundedRectangleProfileDef>()->RoundingRadius() * length_unit_;
|
||||
}
|
||||
|
||||
// @todo
|
||||
const double precision_ = 1.e-5;
|
||||
|
||||
if (x < precision_ || y < precision_) {
|
||||
Logger::Message(Logger::LOG_NOTICE, "Skipping zero sized profile:", inst);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Eigen::Matrix4d m4;
|
||||
bool has_position = true;
|
||||
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
|
||||
has_position = inst->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
taxonomy::matrix4 m = as<taxonomy::matrix4>(map(inst->Position()));
|
||||
m4 = m.ccomponents();
|
||||
}
|
||||
|
||||
return profile_helper(m4, {
|
||||
{{-x, -y}, radius},
|
||||
{{+x, -y}, radius},
|
||||
{{+x, +y}, radius},
|
||||
{{-x, +y}, radius},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcRepresentation* inst) {
|
||||
const bool use_body = !this->settings_.get(ifcopenshell::geometry::settings::INCLUDE_CURVES);
|
||||
|
||||
auto items = map_to_collection(this, inst->Items());
|
||||
if (items == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
// Don't blindly flatten, as we're culling away IfcMappedItem transformations
|
||||
|
||||
auto flat = flatten(items);
|
||||
if (flat == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
*/
|
||||
|
||||
return filter_in_place(items, [&use_body](taxonomy::item* i) {
|
||||
// @todo just filter loops for now.
|
||||
return (i->kind() != taxonomy::LOOP) == use_body;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcShellBasedSurfaceModel* inst) {
|
||||
return map_to_collection(this, inst->SbsmBoundary());
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
|
||||
taxonomy::item* mapping::map_impl(const IfcSchema::IfcTrimmedCurve* inst) {
|
||||
IfcSchema::IfcCurve* basis_curve = inst->BasisCurve();
|
||||
bool isConic = basis_curve->declaration().is(IfcSchema::IfcConic::Class());
|
||||
double parameterFactor = isConic ? angle_unit_ : length_unit_;
|
||||
|
||||
auto tc = new taxonomy::edge;
|
||||
tc->basis = map(inst->BasisCurve());
|
||||
|
||||
bool trim_cartesian = inst->MasterRepresentation() != IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER;
|
||||
IfcEntityList::ptr trims1 = inst->Trim1();
|
||||
IfcEntityList::ptr trims2 = inst->Trim2();
|
||||
|
||||
// reversed orientation handling happens in geometry kernel
|
||||
unsigned sense_agreement = 0; // inst->SenseAgreement() ? 0 : 1;
|
||||
double flts[2];
|
||||
taxonomy::point3 pnts[2];
|
||||
bool has_flts[2] = { false,false };
|
||||
bool has_pnts[2] = { false,false };
|
||||
|
||||
tc->orientation = inst->SenseAgreement();
|
||||
|
||||
for (IfcEntityList::it it = trims1->begin(); it != trims1->end(); it++) {
|
||||
IfcUtil::IfcBaseClass* i = *it;
|
||||
if (i->declaration().is(IfcSchema::IfcCartesianPoint::Class())) {
|
||||
pnts[sense_agreement] = as<taxonomy::point3>(map(i));
|
||||
has_pnts[sense_agreement] = true;
|
||||
} else if (i->declaration().is(IfcSchema::IfcParameterValue::Class())) {
|
||||
const double value = *((IfcSchema::IfcParameterValue*)i);
|
||||
flts[sense_agreement] = value * parameterFactor;
|
||||
has_flts[sense_agreement] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (IfcEntityList::it it = trims2->begin(); it != trims2->end(); it++) {
|
||||
IfcUtil::IfcBaseClass* i = *it;
|
||||
if (i->declaration().is(IfcSchema::IfcCartesianPoint::Class())) {
|
||||
pnts[1 - sense_agreement] = as<taxonomy::point3>(map(i));
|
||||
has_pnts[1 - sense_agreement] = true;
|
||||
} else if (i->declaration().is(IfcSchema::IfcParameterValue::Class())) {
|
||||
const double value = *((IfcSchema::IfcParameterValue*)i);
|
||||
flts[1 - sense_agreement] = value * parameterFactor;
|
||||
has_flts[1 - sense_agreement] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// @todo
|
||||
const double precision_ = 1.e-5;
|
||||
|
||||
trim_cartesian &= has_pnts[0] && has_pnts[1];
|
||||
if (trim_cartesian) {
|
||||
if ((*pnts[0].components_ - *pnts[1].components_).norm() < (2 * precision_)) {
|
||||
Logger::Message(Logger::LOG_WARNING, "Skipping segment with length below tolerance level:", inst);
|
||||
return nullptr;
|
||||
}
|
||||
tc->start = pnts[0];
|
||||
tc->end = pnts[1];
|
||||
} else if (has_flts[0] && has_flts[1]) {
|
||||
// The Geom_Line is constructed from a gp_Pnt and gp_Dir, whereas the IfcLine
|
||||
// is defined by an IfcCartesianPoint and an IfcVector with Magnitude. Because
|
||||
// the vector is normalised when passed to Geom_Line constructor the magnitude
|
||||
// needs to be factored in with the IfcParameterValue here.
|
||||
if (basis_curve->declaration().is(IfcSchema::IfcLine::Class())) {
|
||||
IfcSchema::IfcLine* line = static_cast<IfcSchema::IfcLine*>(basis_curve);
|
||||
const double magnitude = line->Dir()->Magnitude();
|
||||
flts[0] *= magnitude; flts[1] *= magnitude;
|
||||
}
|
||||
if (basis_curve->declaration().is(IfcSchema::IfcEllipse::Class())) {
|
||||
IfcSchema::IfcEllipse* ellipse = static_cast<IfcSchema::IfcEllipse*>(basis_curve);
|
||||
double x = ellipse->SemiAxis1() * length_unit_;
|
||||
double y = ellipse->SemiAxis2() * length_unit_;
|
||||
// @todo the need for this rotation is OCCT-specific
|
||||
const bool rotated = y > x;
|
||||
if (rotated) {
|
||||
flts[0] -= boost::math::constants::pi<double>() / 2.;
|
||||
flts[1] -= boost::math::constants::pi<double>() / 2.;
|
||||
}
|
||||
}
|
||||
tc->start = flts[0];
|
||||
tc->end = flts[1];
|
||||
}
|
||||
|
||||
/*
|
||||
// @todo
|
||||
if (isConic) {
|
||||
// Tiny circle segnments can cause issues later on, for example
|
||||
// when the comp curve is used as the sweeping directrix.
|
||||
double a, b;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(e, a, b);
|
||||
double radius = -1.;
|
||||
if (crv->DynamicType() == STANDARD_TYPE(Geom_Circle)) {
|
||||
radius = Handle(Geom_Circle)::DownCast(crv)->Radius();
|
||||
} else if (crv->DynamicType() == STANDARD_TYPE(Geom_Ellipse)) {
|
||||
// The formula above is for circles, but probably good enough
|
||||
radius = Handle(Geom_Ellipse)::DownCast(crv)->MajorRadius();
|
||||
}
|
||||
if (radius > 0. && deflection_for_approximating_circle(radius, b - a) < getValue(GV_PRECISION)) {
|
||||
TopoDS_Vertex v0, v1;
|
||||
TopExp::Vertices(e, v0, v1);
|
||||
e = TopoDS::Edge(BRepBuilderAPI_MakeEdge(v0, v1).Edge().Oriented(e.Orientation()));
|
||||
Logger::Warning("Subsituted edge with linear approximation", l);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return tc;
|
||||
}
|
||||
@@ -9,10 +9,13 @@
|
||||
if (item != nullptr) { \
|
||||
item->instance = l; \
|
||||
try { \
|
||||
if (l->as<IfcSchema::IfcRepresentationItem>() && !l->as<IfcSchema::IfcStyledItem>()) { \
|
||||
if (l->as<IfcSchema::IfcRepresentationItem>() && !l->as<IfcSchema::IfcStyledItem>() && \
|
||||
/* @todo */ \
|
||||
(item->kind() == taxonomy::SHELL || item->kind() == taxonomy::COLLECTION || item->kind() == taxonomy::EXTRUSION) \
|
||||
) { \
|
||||
auto style = find_style(l->as<IfcSchema::IfcRepresentationItem>()); \
|
||||
if (style) { \
|
||||
((taxonomy::geom_item*)item)->surface_style = as<taxonomy::style>(map(style)); \
|
||||
((taxonomy::geom_item*)item)->surface_style = (taxonomy::style*) map(style); \
|
||||
} \
|
||||
} \
|
||||
} catch (const std::exception& e) { \
|
||||
|
||||
+25
-1166
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,107 @@ namespace geometry {
|
||||
|
||||
#include "bind_convert_decl.i"
|
||||
};
|
||||
|
||||
// Hacks around not wanting to use if constexpr
|
||||
template <typename T>
|
||||
class loop_to_face_upgrade {
|
||||
public:
|
||||
loop_to_face_upgrade(taxonomy::item*) {}
|
||||
|
||||
operator bool() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
operator taxonomy::face() const {
|
||||
throw taxonomy::topology_error();
|
||||
}
|
||||
|
||||
operator T() const {
|
||||
throw taxonomy::topology_error();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class loop_to_face_upgrade<taxonomy::face> {
|
||||
private:
|
||||
boost::optional<taxonomy::face> face_;
|
||||
public:
|
||||
loop_to_face_upgrade(taxonomy::item* item) {
|
||||
taxonomy::loop* loop = dynamic_cast<taxonomy::loop*>(item);
|
||||
if (loop) {
|
||||
loop->external = true;
|
||||
|
||||
face_.emplace();
|
||||
face_->instance = loop->instance;
|
||||
face_->matrix = loop->matrix;
|
||||
face_->children = { loop->clone() };
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return face_.is_initialized();
|
||||
}
|
||||
|
||||
operator taxonomy::face() const {
|
||||
return *face_;
|
||||
}
|
||||
};
|
||||
|
||||
// A RAII-based mechanism to cast the conversion results
|
||||
// from map() into the right type expected by the higher
|
||||
// level typology items. An exception is thrown if the
|
||||
// types do not match or the result was nullptr. A copy
|
||||
// will be assigned to the higher level topology member
|
||||
// and the original pointer will be deleted.
|
||||
|
||||
// This class is also able to uplift some topology items
|
||||
// to higher level types, such as a loop to a face, which
|
||||
// is why the cast operator does not return a reference.
|
||||
template <typename T>
|
||||
class as {
|
||||
private:
|
||||
taxonomy::item* item_;
|
||||
|
||||
public:
|
||||
as(taxonomy::item* item) : item_(item) {}
|
||||
operator T() const {
|
||||
if (!item_) {
|
||||
throw taxonomy::topology_error("item was nullptr");
|
||||
}
|
||||
T* t = dynamic_cast<T*>(item_);
|
||||
if (t) {
|
||||
return T(*t);
|
||||
} else {
|
||||
{
|
||||
loop_to_face_upgrade<T> upgrade(item_);
|
||||
if (upgrade) {
|
||||
return upgrade;
|
||||
}
|
||||
}
|
||||
throw taxonomy::topology_error("item does not match type");
|
||||
}
|
||||
}
|
||||
~as() {
|
||||
delete item_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename U = taxonomy::collection, typename T>
|
||||
U* map_to_collection(POSTFIX_SCHEMA(mapping)* m, const T& ts) {
|
||||
auto c = new U;
|
||||
if (ts->size()) {
|
||||
for (auto it = ts->begin(); it != ts->end(); ++it) {
|
||||
if (auto r = m->map(*it)) {
|
||||
c->children.push_back(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c->children.empty()) {
|
||||
delete c;
|
||||
return nullptr;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,18 +24,20 @@
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
// @todo bring back return value for reduced casting, casting of vector is expensive?
|
||||
|
||||
BIND(IfcProduct);
|
||||
|
||||
BIND(IfcShellBasedSurfaceModel);
|
||||
BIND(IfcFaceBasedSurfaceModel);
|
||||
BIND(IfcRepresentation);
|
||||
BIND(IfcMappedItem);
|
||||
BIND(IfcShellBasedSurfaceModel); // -> collection
|
||||
BIND(IfcFaceBasedSurfaceModel); // -> collection
|
||||
BIND(IfcRepresentation); // -> collection
|
||||
BIND(IfcMappedItem); // -> collection
|
||||
// IfcFacetedBrep included
|
||||
// IfcAdvancedBrep included
|
||||
// IfcFacetedBrepWithVoids included
|
||||
// IfcAdvancedBrepWithVoids included
|
||||
BIND(IfcManifoldSolidBrep);
|
||||
BIND(IfcGeometricSet);
|
||||
BIND(IfcManifoldSolidBrep); // -> shell
|
||||
BIND(IfcGeometricSet); // -> collection
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcCylindricalSurface
|
||||
// BIND(IfcCylindricalSurface);
|
||||
@@ -53,12 +55,12 @@ BIND(IfcGeometricSet);
|
||||
#ifdef SCHEMA_HAS_IfcExtrudedAreaSolidTapered
|
||||
// BIND(IfcExtrudedAreaSolidTapered);
|
||||
#endif
|
||||
BIND(IfcExtrudedAreaSolid);
|
||||
BIND(IfcExtrudedAreaSolid); // -> extrusion
|
||||
// BIND(IfcRevolvedAreaSolid);
|
||||
BIND(IfcConnectedFaceSet);
|
||||
BIND(IfcBooleanResult);
|
||||
BIND(IfcPolygonalBoundedHalfSpace);
|
||||
BIND(IfcHalfSpaceSolid);
|
||||
BIND(IfcConnectedFaceSet); // -> shell
|
||||
BIND(IfcBooleanResult); // -> boolean_result
|
||||
BIND(IfcPolygonalBoundedHalfSpace); // -> face
|
||||
BIND(IfcHalfSpaceSolid); // -> face
|
||||
// BIND(IfcSurfaceOfLinearExtrusion);
|
||||
// BIND(IfcSurfaceOfRevolution);
|
||||
// BIND(IfcBlock);
|
||||
@@ -73,43 +75,43 @@ BIND(IfcHalfSpaceSolid);
|
||||
// BIND(IfcSweptDiskSolid);
|
||||
|
||||
// IfcArbitraryProfileDefWithVoids included
|
||||
BIND(IfcArbitraryClosedProfileDef);
|
||||
BIND(IfcRectangleHollowProfileDef);
|
||||
BIND(IfcArbitraryClosedProfileDef); // -> face
|
||||
BIND(IfcRectangleHollowProfileDef); // -> face
|
||||
// IfcRoundedRectangleProfileDef included
|
||||
BIND(IfcRectangleProfileDef);
|
||||
BIND(IfcRectangleProfileDef); // -> face
|
||||
// BIND(IfcTrapeziumProfileDef)
|
||||
// BIND(IfcCShapeProfileDef);
|
||||
// IfcAsymmetricIShapeProfileDef included
|
||||
BIND(IfcIShapeProfileDef);
|
||||
BIND(IfcIShapeProfileDef); // -> face
|
||||
// BIND(IfcLShapeProfileDef);
|
||||
// BIND(IfcTShapeProfileDef);
|
||||
// BIND(IfcUShapeProfileDef);
|
||||
// BIND(IfcZShapeProfileDef);
|
||||
// IfcCircleHollowProfileDef included
|
||||
BIND(IfcCircleProfileDef);
|
||||
BIND(IfcCircleProfileDef); // -> face
|
||||
// BIND(IfcEllipseProfileDef);
|
||||
// BIND(IfcCenterLineProfileDef);
|
||||
// BIND(IfcCompositeProfileDef);
|
||||
// BIND(IfcDerivedProfileDef);
|
||||
// IfcFaceSurface included
|
||||
// IfcAdvancedFace included in case of IFC4
|
||||
BIND(IfcFace);
|
||||
BIND(IfcFace); // -> face
|
||||
|
||||
// BIND(IfcEdgeCurve);
|
||||
// BIND(IfcSubedge);
|
||||
// BIND(IfcOrientedEdge);
|
||||
// BIND(IfcEdge);
|
||||
// BIND(IfcEdgeLoop);
|
||||
BIND(IfcPolyline);
|
||||
BIND(IfcPolyLoop);
|
||||
BIND(IfcCompositeCurve);
|
||||
BIND(IfcTrimmedCurve);
|
||||
BIND(IfcPolyline); // -> loop
|
||||
BIND(IfcPolyLoop); // -> loop
|
||||
BIND(IfcCompositeCurve); // -> loop
|
||||
BIND(IfcTrimmedCurve); // -> edge
|
||||
// BIND(IfcArbitraryOpenProfileDef);
|
||||
#ifdef SCHEMA_HAS_IfcIndexedPolyCurve
|
||||
// BIND(IfcIndexedPolyCurve)
|
||||
#endif
|
||||
|
||||
BIND(IfcCircle);
|
||||
BIND(IfcCircle); // -> circle
|
||||
// BIND(IfcEllipse);
|
||||
// BIND(IfcLine);
|
||||
#ifdef SCHEMA_HAS_IfcBSplineCurveWithKnots
|
||||
@@ -117,19 +119,19 @@ BIND(IfcCircle);
|
||||
// BIND(IfcBSplineCurveWithKnots);
|
||||
#endif
|
||||
|
||||
BIND(IfcCartesianPoint);
|
||||
BIND(IfcDirection);
|
||||
BIND(IfcAxis2Placement2D);
|
||||
BIND(IfcAxis2Placement3D);
|
||||
BIND(IfcCartesianPoint); // -> point3
|
||||
BIND(IfcDirection); // -> direction3
|
||||
BIND(IfcAxis2Placement2D); // -> matrix4
|
||||
BIND(IfcAxis2Placement3D); // -> matrix4
|
||||
// BIND(IfcAxis1Placement);
|
||||
// IfcCartesianTransformationOperator2DnonUniform included
|
||||
BIND(IfcCartesianTransformationOperator2D);
|
||||
BIND(IfcCartesianTransformationOperator2D); // -> matrix4
|
||||
// IfcCartesianTransformationOperator3DnonUniform included
|
||||
BIND(IfcCartesianTransformationOperator3D);
|
||||
BIND(IfcLocalPlacement);
|
||||
BIND(IfcCartesianTransformationOperator3D); // -> matrix4
|
||||
BIND(IfcLocalPlacement); // -> matrix4
|
||||
// BIND(IfcVector);
|
||||
// BIND(IfcPlane);
|
||||
|
||||
// BIND(IfcColourRgb);
|
||||
BIND(IfcMaterial);
|
||||
BIND(IfcStyledItem);
|
||||
BIND(IfcMaterial); // -> style
|
||||
BIND(IfcStyledItem); // -> style
|
||||
|
||||
@@ -55,6 +55,10 @@ namespace ifcopenshell { namespace geometry {
|
||||
virtual ConversionResultShape* clone() const = 0;
|
||||
virtual int surface_genus() const = 0;
|
||||
virtual bool is_manifold() const = 0;
|
||||
// @todo this must be something with a virtual dtor so that we can delete it.
|
||||
virtual double bounding_box(void*& b) const = 0;
|
||||
virtual int num_vertices() const = 0;
|
||||
virtual void set_box(void* b) = 0;
|
||||
virtual ~ConversionResultShape() {}
|
||||
};
|
||||
|
||||
@@ -63,30 +67,31 @@ namespace ifcopenshell { namespace geometry {
|
||||
int id;
|
||||
ifcopenshell::geometry::taxonomy::matrix4 placement;
|
||||
ConversionResultShape* shape;
|
||||
ifcopenshell::geometry::taxonomy::style style;
|
||||
const ifcopenshell::geometry::taxonomy::style* style;
|
||||
public:
|
||||
ConversionResult(int id, const ifcopenshell::geometry::taxonomy::matrix4& placement, const ConversionResultShape* shape, const ifcopenshell::geometry::taxonomy::style& style)
|
||||
ConversionResult(int id, const ifcopenshell::geometry::taxonomy::matrix4& placement, const ConversionResultShape* shape, const ifcopenshell::geometry::taxonomy::style* style)
|
||||
: id(id), placement(placement), shape(shape->clone()), style(style) {}
|
||||
ConversionResult(int id, const ifcopenshell::geometry::taxonomy::matrix4& placement, const ConversionResultShape* shape)
|
||||
: id(id), placement(placement), shape(shape->clone()) {}
|
||||
ConversionResult(int id, const ConversionResultShape* shape, const ifcopenshell::geometry::taxonomy::style& style)
|
||||
ConversionResult(int id, const ConversionResultShape* shape, const ifcopenshell::geometry::taxonomy::style* style)
|
||||
: id(id), shape(shape->clone()), style(style) {}
|
||||
ConversionResult(int id, const ConversionResultShape* shape)
|
||||
: id(id), shape(shape->clone()) {}
|
||||
void append(const ifcopenshell::geometry::taxonomy::matrix4& trsf) {
|
||||
// @todo verify order
|
||||
*placement.components = *placement.components * *trsf.components;
|
||||
placement.components() = placement.ccomponents() * trsf.ccomponents();
|
||||
}
|
||||
void prepend(const ifcopenshell::geometry::taxonomy::matrix4& trsf) {
|
||||
// @todo verify order
|
||||
*placement.components = *trsf.components * *placement.components;
|
||||
placement.components() = trsf.ccomponents() * placement.ccomponents();
|
||||
}
|
||||
const ConversionResultShape* Shape() const { return shape; }
|
||||
ConversionResultShape* Shape() { return shape; }
|
||||
const ifcopenshell::geometry::taxonomy::matrix4& Placement() const { return placement; }
|
||||
// @todo
|
||||
bool hasStyle() const { return style.diffuse.is_initialized(); }
|
||||
const ifcopenshell::geometry::taxonomy::style& Style() const { return style; }
|
||||
void setStyle(const ifcopenshell::geometry::taxonomy::style& newStyle) { style = newStyle; }
|
||||
bool hasStyle() const { return style != nullptr; }
|
||||
const ifcopenshell::geometry::taxonomy::style& Style() const { return *style; }
|
||||
void setStyle(const ifcopenshell::geometry::taxonomy::style* newStyle) { style = newStyle; }
|
||||
int ItemId() const { return id; }
|
||||
};
|
||||
|
||||
|
||||
@@ -3,17 +3,37 @@
|
||||
#include "../../ifcgeom/schema_agnostic/IfcGeomElement.h"
|
||||
|
||||
ifcopenshell::geometry::Converter::Converter(const std::string& geometry_library, IfcParse::IfcFile* file, settings& s)
|
||||
: settings_(s)
|
||||
: geometry_library_(boost::to_lower_copy(geometry_library))
|
||||
, settings_(s)
|
||||
{
|
||||
kernel_ = kernels::construct(geometry_library, file);
|
||||
mapping_ = impl::mapping_implementations().construct(file, settings_);
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
void substitute_with_box_based_on_density(ifcopenshell::geometry::ConversionResults& items, double& density) {
|
||||
int nv = 0;
|
||||
void* box = nullptr;
|
||||
double volume = 0.;
|
||||
for (auto& i : items) {
|
||||
nv += i.Shape()->num_vertices();
|
||||
volume = i.Shape()->bounding_box(box);
|
||||
}
|
||||
density = nv / volume;
|
||||
if (density > 1e5) {
|
||||
items[0].Shape()->set_box(box);
|
||||
items.erase(items.begin() + 1, items.end());
|
||||
Logger::Notice("Substituted element with " + boost::lexical_cast<std::string>(density) + " vertices / m3 with a bounding box");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create_brep_for_representation_and_product(taxonomy::item* product_node, const taxonomy::matrix4& place) {
|
||||
auto product = (IfcUtil::IfcBaseEntity*) product_node->instance;
|
||||
const std::string product_type = product->declaration().name();
|
||||
// @todo
|
||||
element_settings s(settings_, 1.0 /*getValue(GV_LENGTH_UNIT) */, product_type);
|
||||
element_settings s(settings_, settings.getValue(ConversionSettings::GV_LENGTH_UNIT), product_type);
|
||||
|
||||
std::stringstream representation_id_builder;
|
||||
|
||||
@@ -46,6 +66,11 @@ ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create
|
||||
|
||||
total_geom_time += (geom_end - geom_start) / (double)CLOCKS_PER_SEC;
|
||||
|
||||
double d;
|
||||
if (geometry_library_ == "cgal") {
|
||||
substitute_with_box_based_on_density(shapes, d);
|
||||
}
|
||||
|
||||
shape = brep_ptr(new ifcopenshell::geometry::Representation::BRep(s, representation_id_builder.str(), shapes));
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../../ifcgeom/settings.h"
|
||||
#include "../../ifcgeom/schema_agnostic/ConversionResult.h"
|
||||
#include "../../ifcgeom/abstract_mapping.h"
|
||||
#include "../../ifcgeom/ConversionSettings.h"
|
||||
#include "../../ifcgeom/kernel_agnostic/AbstractKernel.h"
|
||||
|
||||
#include <boost/function.hpp>
|
||||
@@ -17,43 +18,16 @@ namespace ifcopenshell { namespace geometry {
|
||||
public:
|
||||
typedef boost::shared_ptr<ifcopenshell::geometry::Representation::BRep> brep_ptr;
|
||||
private:
|
||||
std::string geometry_library_;
|
||||
abstract_mapping* mapping_;
|
||||
kernels::AbstractKernel* kernel_;
|
||||
ifcopenshell::geometry::settings settings_;
|
||||
std::map<ifcopenshell::geometry::taxonomy::item*, brep_ptr, ifcopenshell::geometry::taxonomy::less_functor> cache_;
|
||||
|
||||
public:
|
||||
kernels::AbstractKernel* kernel() { return kernel_; }
|
||||
ConversionSettings settings;
|
||||
|
||||
// Tolerances and settings for various geometrical operations:
|
||||
enum GeomValue {
|
||||
// Specifies the deflection of the mesher
|
||||
// Default: 0.001m / 1mm
|
||||
GV_DEFLECTION_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
|
||||
// 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
|
||||
GV_LENGTH_UNIT,
|
||||
// The plane angle unit used for the creation of TopoDS_Shapes, primarily affects
|
||||
// the interpretation of IfcParamaterValues of IfcTrimmedCurves
|
||||
// Default: -1.0 (= not set, fist try degrees, then radians)
|
||||
GV_PLANEANGLE_UNIT,
|
||||
// The precision used in boolean operations, setting this value too low results
|
||||
// in artefacts and potentially modelling failures
|
||||
// Default: 0.00001 (obtained from IfcGeometricRepresentationContext if available)
|
||||
GV_PRECISION,
|
||||
// Whether to process shapes of type Face or higher (1) Wire or lower (-1) or all (0)
|
||||
GV_DIMENSIONALITY
|
||||
};
|
||||
kernels::AbstractKernel* kernel() { return kernel_; }
|
||||
|
||||
Converter(const std::string& geometry_library, IfcParse::IfcFile* file, ifcopenshell::geometry::settings& settings);
|
||||
|
||||
@@ -61,16 +35,6 @@ namespace ifcopenshell { namespace geometry {
|
||||
|
||||
abstract_mapping* mapping() const { return mapping_; }
|
||||
|
||||
/*
|
||||
virtual void setValue(GeomValue var, double value) {
|
||||
implementation_->setValue(var, value);
|
||||
}
|
||||
|
||||
virtual double getValue(GeomValue var) const {
|
||||
return implementation_->getValue(var);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
virtual NativeElement<double, double>* convert(
|
||||
const IteratorSettings& settings, IfcUtil::IfcBaseClass* representation,
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace ifcopenshell { namespace geometry {
|
||||
// internally in IfcOpenShell everything is measured in meters.
|
||||
if (settings.get(settings::CONVERT_BACK_UNITS)) {
|
||||
for (int i = 0; i <= 2; ++i) {
|
||||
(*matrix_.components)(3, i) /= settings.unit_magnitude();
|
||||
matrix_.components()(3, i) /= settings.unit_magnitude();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,9 +234,12 @@ namespace ifcopenshell { namespace geometry {
|
||||
std::transform(products.begin(), products.end(), std::back_inserter(items), [this, &placements](IfcUtil::IfcBaseClass* p) {
|
||||
auto item = converter_->mapping()->map(p);
|
||||
// Product placements do not affect item reuse and should temporarily be swapped to identity
|
||||
std::swap(placements[item], ((taxonomy::geom_item*)item)->matrix);
|
||||
if (item) {
|
||||
std::swap(placements[item], ((taxonomy::geom_item*)item)->matrix);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
items.erase(std::remove(items.begin(), items.end(), nullptr), items.end());
|
||||
std::sort(items.begin(), items.end(), taxonomy::less);
|
||||
auto it = items.begin();
|
||||
while (it < items.end()) {
|
||||
@@ -430,6 +433,7 @@ namespace ifcopenshell { namespace geometry {
|
||||
geometry_conversion_result* task = nullptr;
|
||||
while (task_iterator_ != tasks_.end()) {
|
||||
task = &*task_iterator_++;
|
||||
done++;
|
||||
create_element(converter_, settings_, task);
|
||||
if (task->elements.empty()) {
|
||||
task = nullptr;
|
||||
@@ -671,6 +675,10 @@ namespace ifcopenshell { namespace geometry {
|
||||
for (auto& p : all_processed_elements_) {
|
||||
delete p;
|
||||
}
|
||||
|
||||
for (auto& t : tasks_) {
|
||||
delete t.item;
|
||||
}
|
||||
}
|
||||
};
|
||||
}}
|
||||
|
||||
@@ -39,18 +39,18 @@ ifcopenshell::geometry::Representation::Serialization::Serialization(const BRep&
|
||||
delete shape;
|
||||
|
||||
for (ifcopenshell::geometry::ConversionResults::const_iterator it = brep.begin(); it != brep.end(); ++ it) {
|
||||
if (it->hasStyle() && it->Style().diffuse) {
|
||||
auto clr = it->Style().diffuse.get().components;
|
||||
surface_styles_.push_back(clr[0]);
|
||||
surface_styles_.push_back(clr[1]);
|
||||
surface_styles_.push_back(clr[2]);
|
||||
if (it->hasStyle()) {
|
||||
const auto& clr = it->Style().diffuse.ccomponents();
|
||||
surface_styles_.push_back(clr(0));
|
||||
surface_styles_.push_back(clr(1));
|
||||
surface_styles_.push_back(clr(2));
|
||||
} else {
|
||||
surface_styles_.push_back(-1.);
|
||||
surface_styles_.push_back(-1.);
|
||||
surface_styles_.push_back(-1.);
|
||||
}
|
||||
if (it->hasStyle() && it->Style().transparency) {
|
||||
surface_styles_.push_back(1. - *it->Style().transparency);
|
||||
if (it->hasStyle() && it->Style().has_transparency()) {
|
||||
surface_styles_.push_back(1. - it->Style().transparency);
|
||||
} else {
|
||||
surface_styles_.push_back(1.);
|
||||
}
|
||||
@@ -95,14 +95,16 @@ ifcopenshell::geometry::ConversionResultShape* ifcopenshell::geometry::Represent
|
||||
|
||||
// @todo, check
|
||||
gp_GTrsf trsf;
|
||||
gp_Trsf tr;
|
||||
const auto& m = it->Placement().components;
|
||||
tr.SetValues(
|
||||
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
|
||||
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
|
||||
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
|
||||
);
|
||||
trsf = tr;
|
||||
if (it->Placement().components_) {
|
||||
gp_Trsf tr;
|
||||
const auto& m = it->Placement().ccomponents();
|
||||
tr.SetValues(
|
||||
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
|
||||
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
|
||||
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
|
||||
);
|
||||
trsf = tr;
|
||||
}
|
||||
|
||||
if (!force_meters && settings().get(ifcopenshell::geometry::settings::CONVERT_BACK_UNITS)) {
|
||||
gp_Trsf scale;
|
||||
@@ -243,14 +245,17 @@ bool ifcopenshell::geometry::Representation::BRep::calculate_projected_surface_a
|
||||
try {
|
||||
// @todo check
|
||||
gp_GTrsf trsf;
|
||||
gp_Trsf tr;
|
||||
const auto& m = place.components;
|
||||
tr.SetValues(
|
||||
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
|
||||
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
|
||||
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
|
||||
);
|
||||
trsf = tr;
|
||||
|
||||
if (place.components_) {
|
||||
gp_Trsf tr;
|
||||
const auto& m = place.ccomponents();
|
||||
tr.SetValues(
|
||||
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
|
||||
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
|
||||
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
|
||||
);
|
||||
trsf = tr;
|
||||
}
|
||||
|
||||
gp_Mat mat = trsf.Trsf().HVectorialPart();
|
||||
gp_Ax3 ax(trsf.TranslationPart(), mat.Column(3), mat.Column(1));
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ifcopenshell { namespace geometry {
|
||||
|
||||
namespace Representation {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
/*
|
||||
namespace IfcGeom {
|
||||
extern IfcUtil::IfcBaseClass* tesselate_Ifc2x3(const TopoDS_Shape& shape, double deflection);
|
||||
extern IfcUtil::IfcBaseClass* tesselate_Ifc4(const TopoDS_Shape& shape, double deflection);
|
||||
@@ -20,11 +21,14 @@ IfcUtil::IfcBaseClass* execute_based_on_schema(Fn fn1, Fn fn2, const std::string
|
||||
throw IfcParse::IfcException("No geometry serialization available for " + schema_name);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
IfcUtil::IfcBaseClass* IfcGeom::tesselate(const std::string& schema_name, const TopoDS_Shape& shape, double deflection) {
|
||||
return execute_based_on_schema(IfcGeom::tesselate_Ifc2x3, IfcGeom::tesselate_Ifc4, schema_name, shape, deflection);
|
||||
return nullptr;
|
||||
// return execute_based_on_schema(IfcGeom::tesselate_Ifc2x3, IfcGeom::tesselate_Ifc4, schema_name, shape, deflection);
|
||||
}
|
||||
|
||||
IfcUtil::IfcBaseClass* IfcGeom::serialise(const std::string& schema_name, const TopoDS_Shape& shape, bool advanced) {
|
||||
return execute_based_on_schema(IfcGeom::serialise_Ifc2x3, IfcGeom::serialise_Ifc4, schema_name, shape, advanced);
|
||||
return nullptr;
|
||||
// return execute_based_on_schema(IfcGeom::serialise_Ifc2x3, IfcGeom::serialise_Ifc4, schema_name, shape, advanced);
|
||||
}
|
||||
@@ -13,45 +13,46 @@ static bool default_materials_initialized = false;
|
||||
|
||||
void InitDefaultMaterials() {
|
||||
default_materials.insert(std::make_pair("IfcSite", ifcopenshell::geometry::taxonomy::style("IfcSite")));
|
||||
default_materials["IfcSite"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.75, 0.8, 0.65));
|
||||
default_materials["IfcSite"].diffuse = ifcopenshell::geometry::taxonomy::colour(0.75, 0.8, 0.65);
|
||||
|
||||
default_materials.insert(std::make_pair("IfcSlab", ifcopenshell::geometry::taxonomy::style("IfcSlab")));
|
||||
default_materials["IfcSlab"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.4, 0.4, 0.4));
|
||||
default_materials["IfcSlab"].diffuse = ifcopenshell::geometry::taxonomy::colour(0.4, 0.4, 0.4);
|
||||
|
||||
default_materials.insert(std::make_pair("IfcWallStandardCase", ifcopenshell::geometry::taxonomy::style("IfcWallStandardCase")));
|
||||
default_materials["IfcWallStandardCase"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.9, 0.9, 0.9));
|
||||
default_materials["IfcWallStandardCase"].diffuse = ifcopenshell::geometry::taxonomy::colour(0.9, 0.9, 0.9);
|
||||
|
||||
default_materials.insert(std::make_pair("IfcWall", ifcopenshell::geometry::taxonomy::style("IfcWall")));
|
||||
default_materials["IfcWall"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.9, 0.9, 0.9));
|
||||
default_materials["IfcWall"].diffuse = ifcopenshell::geometry::taxonomy::colour(0.9, 0.9, 0.9);
|
||||
|
||||
default_materials.insert(std::make_pair("IfcWindow", ifcopenshell::geometry::taxonomy::style("IfcWindow")));
|
||||
default_materials["IfcWindow"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.75, 0.8, 0.75));
|
||||
default_materials["IfcWindow"].transparency.reset(0.3);
|
||||
default_materials["IfcWindow"].diffuse = ifcopenshell::geometry::taxonomy::colour(0.75, 0.8, 0.75);
|
||||
default_materials["IfcWindow"].transparency = 0.3;
|
||||
|
||||
default_materials.insert(std::make_pair("IfcDoor", ifcopenshell::geometry::taxonomy::style("IfcDoor")));
|
||||
default_materials["IfcDoor"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.55, 0.3, 0.15));
|
||||
default_materials["IfcDoor"].diffuse = ifcopenshell::geometry::taxonomy::colour(0.55, 0.3, 0.15);
|
||||
|
||||
default_materials.insert(std::make_pair("IfcBeam", ifcopenshell::geometry::taxonomy::style("IfcBeam")));
|
||||
default_materials["IfcBeam"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.75, 0.7, 0.7));
|
||||
default_materials["IfcBeam"].diffuse = ifcopenshell::geometry::taxonomy::colour(0.75, 0.7, 0.7);
|
||||
|
||||
default_materials.insert(std::make_pair("IfcRailing", ifcopenshell::geometry::taxonomy::style("IfcRailing")));
|
||||
default_materials["IfcRailing"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.65, 0.6, 0.6));
|
||||
default_materials["IfcRailing"].diffuse = ifcopenshell::geometry::taxonomy::colour(0.65, 0.6, 0.6);
|
||||
|
||||
default_materials.insert(std::make_pair("IfcMember", ifcopenshell::geometry::taxonomy::style("IfcMember")));
|
||||
default_materials["IfcMember"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.65, 0.6, 0.6));
|
||||
default_materials["IfcMember"].diffuse = ifcopenshell::geometry::taxonomy::colour(0.65, 0.6, 0.6);
|
||||
|
||||
default_materials.insert(std::make_pair("IfcPlate", ifcopenshell::geometry::taxonomy::style("IfcPlate")));
|
||||
default_materials["IfcPlate"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.8, 0.8, 0.8));
|
||||
default_materials["IfcPlate"].diffuse = ifcopenshell::geometry::taxonomy::colour(0.8, 0.8, 0.8);
|
||||
|
||||
default_material = ifcopenshell::geometry::taxonomy::style("DefaultMaterial");
|
||||
default_material.diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.7, 0.7, 0.7));
|
||||
default_material.diffuse = ifcopenshell::geometry::taxonomy::colour(0.7, 0.7, 0.7);
|
||||
|
||||
default_materials_initialized = true;
|
||||
}
|
||||
|
||||
boost::optional<ifcopenshell::geometry::taxonomy::colour> read_colour_component(const boost::optional<pt::ptree&> list) {
|
||||
ifcopenshell::geometry::taxonomy::colour read_colour_component(const boost::optional<pt::ptree&> list) {
|
||||
ifcopenshell::geometry::taxonomy::colour clr;
|
||||
if (!list) {
|
||||
return boost::none;
|
||||
return clr;
|
||||
}
|
||||
double rgb[3];
|
||||
int i = 0;
|
||||
@@ -65,7 +66,8 @@ boost::optional<ifcopenshell::geometry::taxonomy::colour> read_colour_component(
|
||||
if (i != 3) {
|
||||
throw std::runtime_error("rgb array less than 3 elements large (was " + std::to_string(i) + ")");
|
||||
}
|
||||
return ifcopenshell::geometry::taxonomy::colour(rgb[0], rgb[1], rgb[2]);
|
||||
clr.components() << rgb[0], rgb[1], rgb[2];
|
||||
return clr;
|
||||
}
|
||||
|
||||
void IfcGeom::set_default_style_file(const std::string& json_file) {
|
||||
@@ -88,7 +90,7 @@ void IfcGeom::set_default_style_file(const std::string& json_file) {
|
||||
default_materials[name].specular = read_colour_component(specular);
|
||||
|
||||
if (material.get_child_optional("specular-roughness")) {
|
||||
default_materials[name].specularity.reset(1.0 / material.get<double>("specular-roughness"));
|
||||
default_materials[name].specularity = 1.0 / material.get<double>("specular-roughness");
|
||||
}
|
||||
if (material.get_child_optional("transparency")) {
|
||||
default_materials[name].transparency = material.get<double>("transparency");
|
||||
|
||||
+57
-18
@@ -3,13 +3,25 @@
|
||||
using namespace ifcopenshell::geometry::taxonomy;
|
||||
|
||||
namespace {
|
||||
bool compare(const trimmed_curve& a, const trimmed_curve& b);
|
||||
|
||||
bool compare(const collection& a, const collection& b);
|
||||
|
||||
template <typename T>
|
||||
bool compare(const eigen_base<T>& t, const eigen_base<T>& u) {
|
||||
auto t_begin = t.components->data();
|
||||
auto t_end = t.components->data() + t.components->size();
|
||||
if (t.components_ == nullptr && u.components_ == nullptr) {
|
||||
return false;
|
||||
} else if (t.components_ == nullptr && u.components_ != nullptr) {
|
||||
return true;
|
||||
} else if (t.components_ != nullptr && u.components_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto u_begin = u.components->data();
|
||||
auto u_end = u.components->data() + u.components->size();
|
||||
auto t_begin = t.components_->data();
|
||||
auto t_end = t.components_->data() + t.components_->size();
|
||||
|
||||
auto u_begin = u.components_->data();
|
||||
auto u_end = u.components_->data() + u.components_->size();
|
||||
|
||||
return std::lexicographical_compare(t_begin, t_end, u_begin, u_end);
|
||||
}
|
||||
@@ -84,30 +96,35 @@ namespace {
|
||||
-1 : (!b_lt_a ? 0 : 1);
|
||||
}
|
||||
|
||||
bool compare(const trimmed_curve& a, const trimmed_curve& b);
|
||||
|
||||
bool compare(const collection& a, const collection& b);
|
||||
|
||||
bool compare(const extrusion& a, const extrusion& b) {
|
||||
// @todo extrusions can also have non-identity matrices right? perhaps it's time
|
||||
// for a dedicated transform node and not on the abstract geom_item.
|
||||
const int order[3] = {
|
||||
less_to_order(a.basis, b.basis),
|
||||
less_to_order(a.direction, b.direction),
|
||||
a.depth < b.depth ? -1 : (a.depth == b.depth ? 0 : 1)
|
||||
};
|
||||
// find the first non-zero integer.
|
||||
auto it = std::find_if(std::begin(order), std::end(order), [](int x) { return x; });
|
||||
if (it == std::end(order)) return false;
|
||||
if (it == std::end(order)) {
|
||||
// extrusions can have non-identity matrices
|
||||
//
|
||||
// @todo perhaps it's time for a dedicated transform
|
||||
// node and not on the abstract geom_item.
|
||||
return compare(a.matrix, b.matrix);
|
||||
}
|
||||
return *it == -1;
|
||||
}
|
||||
|
||||
bool compare(const node&, const node&) {
|
||||
throw std::runtime_error("not implemented");
|
||||
}
|
||||
|
||||
bool compare(const style& a, const style& b) {
|
||||
const int order[5] = {
|
||||
less_to_order_optional(a.name, b.name),
|
||||
less_to_order_optional(a.diffuse, b.diffuse),
|
||||
less_to_order_optional(a.specular, b.specular),
|
||||
less_to_order_optional(a.specularity, b.specularity),
|
||||
less_to_order_optional(a.transparency, b.transparency)
|
||||
less_to_order(a.name, b.name),
|
||||
less_to_order(a.diffuse, b.diffuse),
|
||||
less_to_order(a.specular, b.specular),
|
||||
less_to_order(a.specularity, b.specularity),
|
||||
less_to_order(a.transparency, b.transparency)
|
||||
};
|
||||
auto it = std::find_if(std::begin(order), std::end(order), [](int x) { return x; });
|
||||
if (it == std::end(order)) return false;
|
||||
@@ -214,9 +231,31 @@ namespace {
|
||||
return a_lt_b;
|
||||
}
|
||||
// Vectors equal, compare matrix (in case of mapped items).
|
||||
return compare(a.matrix, b.matrix);
|
||||
int matrix_order = less_to_order(a.matrix, b.matrix);
|
||||
if (matrix_order == 0) {
|
||||
|
||||
if (a.surface_style == nullptr && b.surface_style == nullptr) {
|
||||
return false;
|
||||
} else if (a.surface_style == nullptr && b.surface_style != nullptr) {
|
||||
return true;
|
||||
} else if (a.surface_style != nullptr && b.surface_style == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return compare(*a.surface_style, *b.surface_style);
|
||||
} else {
|
||||
return matrix_order == -1;
|
||||
}
|
||||
} else {
|
||||
return a.children.size() < b.children.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ifcopenshell::geometry::taxonomy::collection * ifcopenshell::geometry::flatten(const taxonomy::collection * deep) {
|
||||
auto flat = new taxonomy::collection;
|
||||
visit(deep, [&flat](taxonomy::item* i) {
|
||||
flat->children.push_back(i);
|
||||
});
|
||||
return flat;
|
||||
}
|
||||
|
||||
+195
-52
@@ -11,7 +11,8 @@
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <exception>
|
||||
#include <cstdalign>
|
||||
|
||||
// @todo don't do std::less but use hashing and cache hash values.
|
||||
|
||||
namespace ifcopenshell {
|
||||
|
||||
@@ -29,12 +30,17 @@ enum kinds { MATRIX4, POINT3, DIRECTION3, LINE, CIRCLE, ELLIPSE, BSPLINE_CURVE,
|
||||
|
||||
struct item {
|
||||
const IfcUtil::IfcBaseClass* instance;
|
||||
|
||||
boost::optional<bool> orientation;
|
||||
|
||||
virtual item* clone() const = 0;
|
||||
virtual kinds kind() const = 0;
|
||||
virtual void print(std::ostream&, int indent=0) const = 0;
|
||||
virtual void reverse() { throw taxonomy::topology_error(); }
|
||||
|
||||
item(const IfcUtil::IfcBaseClass* instance = nullptr) : instance(instance) {}
|
||||
|
||||
virtual ~item() {}
|
||||
};
|
||||
|
||||
bool less(const item*, const item*);
|
||||
@@ -45,40 +51,76 @@ struct less_functor {
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
const T& eigen_defaults();
|
||||
|
||||
template <>
|
||||
const Eigen::Vector3d& eigen_defaults<Eigen::Vector3d>() {
|
||||
static Eigen::Vector3d identity = Eigen::Vector3d::Zero();
|
||||
return identity;
|
||||
}
|
||||
|
||||
template <>
|
||||
const Eigen::Matrix4d& eigen_defaults<Eigen::Matrix4d>() {
|
||||
static Eigen::Matrix4d identity = Eigen::Matrix4d::Identity();
|
||||
return identity;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct eigen_base {
|
||||
T* components;
|
||||
T* components_;
|
||||
|
||||
eigen_base() {
|
||||
components = new T;
|
||||
components_ = nullptr;
|
||||
}
|
||||
|
||||
eigen_base(const eigen_base& other) {
|
||||
this->components = new T(*other.components);
|
||||
this->components_ = other.components_ ? new T(*other.components_) : nullptr;
|
||||
}
|
||||
|
||||
eigen_base(const T& other) {
|
||||
this->components = new T(other);
|
||||
this->components_ = new T(other);
|
||||
}
|
||||
|
||||
eigen_base& operator=(const eigen_base& other) {
|
||||
if (this != &other) {
|
||||
this->components = new T(*other.components);
|
||||
this->components_ = other.components_ ? new T(*other.components_) : nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void print_impl(std::ostream& o, const std::string& class_name, int indent = 0) const {
|
||||
o << std::string(indent, ' ') << class_name;
|
||||
int n = T::RowsAtCompileTime * T::ColsAtCompileTime;
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
o << " " << (*components)(i);
|
||||
if (this->components_) {
|
||||
int n = T::RowsAtCompileTime * T::ColsAtCompileTime;
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
o << " " << (*components_)(i);
|
||||
}
|
||||
}
|
||||
o << std::endl;
|
||||
}
|
||||
|
||||
~eigen_base() {
|
||||
delete this->components;
|
||||
virtual ~eigen_base() {
|
||||
delete this->components_;
|
||||
}
|
||||
|
||||
const T& ccomponents() const {
|
||||
if (this->components_) {
|
||||
return *this->components_;
|
||||
} else {
|
||||
return eigen_defaults<T>();
|
||||
}
|
||||
}
|
||||
|
||||
T& components() {
|
||||
if (!this->components_) {
|
||||
this->components_ = new T;
|
||||
}
|
||||
return *this->components_;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -88,20 +130,24 @@ struct matrix4 : public item, public eigen_base<Eigen::Matrix4d> {
|
||||
};
|
||||
tag_t tag;
|
||||
|
||||
matrix4() : eigen_base(Eigen::Matrix4d::Identity()), tag(IDENTITY) {}
|
||||
matrix4() : eigen_base(), tag(IDENTITY) {}
|
||||
matrix4(const Eigen::Matrix4d& c) : eigen_base(c), tag(OTHER) {}
|
||||
matrix4(const Eigen::Vector3d& o, const Eigen::Vector3d& z, const Eigen::Vector3d& x) : tag(AFFINE_WO_SCALE) {
|
||||
auto X = x.normalized();
|
||||
auto Y = z.cross(x).normalized();
|
||||
auto Z = z.normalized();
|
||||
components = new Eigen::Matrix4d;
|
||||
(*components) <<
|
||||
components_ = new Eigen::Matrix4d;
|
||||
(*components_) <<
|
||||
X(0), Y(0), Z(0), o(0),
|
||||
X(1), Y(1), Z(1), o(1),
|
||||
X(2), Y(2), Z(2), o(2),
|
||||
0, 0, 0, 1.;
|
||||
}
|
||||
|
||||
bool is_identity() const {
|
||||
return !components_ || components_->isIdentity();
|
||||
}
|
||||
|
||||
void print(std::ostream& o, int indent = 0) const {
|
||||
print_impl(o, "matrix4", indent);
|
||||
}
|
||||
@@ -118,33 +164,30 @@ struct colour : public item, public eigen_base<Eigen::Vector3d> {
|
||||
virtual item* clone() const { return new colour(*this); }
|
||||
virtual kinds kind() const { return COLOUR; }
|
||||
|
||||
colour() : eigen_base(Eigen::Vector3d::Zero()) {}
|
||||
colour(double r, double g, double b) { (*components) << r, g, b; }
|
||||
colour() : eigen_base() {}
|
||||
colour(double r, double g, double b) { components() << r, g, b; }
|
||||
|
||||
const double& r() const { return (*components)[0]; }
|
||||
const double& g() const { return (*components)[1]; }
|
||||
const double& b() const { return (*components)[2]; }
|
||||
const double& r() const { return ccomponents()[0]; }
|
||||
const double& g() const { return ccomponents()[1]; }
|
||||
const double& b() const { return ccomponents()[2]; }
|
||||
};
|
||||
|
||||
struct style : public item {
|
||||
// @todo this is not very efficient wrt alignment
|
||||
boost::optional<std::string> name;
|
||||
boost::optional<colour> diffuse;
|
||||
boost::optional<colour> specular;
|
||||
boost::optional<double> specularity, transparency;
|
||||
std::string name;
|
||||
colour diffuse;
|
||||
colour specular;
|
||||
double specularity, transparency;
|
||||
|
||||
void print(std::ostream& o, int indent = 0) const {
|
||||
o << std::string(indent, ' ') << "style" << std::endl;
|
||||
if (name) {
|
||||
o << std::string(indent, ' ') << " " << "name" << (*name) << std::endl;
|
||||
o << std::string(indent, ' ') << " " << "name" << (name) << std::endl;
|
||||
if (diffuse.components_) {
|
||||
o << std::string(indent, ' ') << " " << "diffuse" << (name) << std::endl;
|
||||
diffuse.print(o, indent + 5 + 7);
|
||||
}
|
||||
if (diffuse) {
|
||||
o << std::string(indent, ' ') << " " << "diffuse" << (*name) << std::endl;
|
||||
diffuse->print(o, indent + 5 + 7);
|
||||
}
|
||||
if (diffuse) {
|
||||
o << std::string(indent, ' ') << " " << "specular" << (*name) << std::endl;
|
||||
diffuse->print(o, indent + 5 + 8);
|
||||
if (specular.components_) {
|
||||
o << std::string(indent, ' ') << " " << "specular" << (name) << std::endl;
|
||||
specular.print(o, indent + 5 + 8);
|
||||
}
|
||||
// @todo
|
||||
}
|
||||
@@ -155,23 +198,31 @@ struct style : public item {
|
||||
// @todo equality implementation based on values?
|
||||
bool operator==(const style& other) const { return instance == other.instance; }
|
||||
|
||||
style() {}
|
||||
style(const std::string& name) : name(name) {}
|
||||
style() : specularity(std::numeric_limits<double>::quiet_NaN()), transparency(std::numeric_limits<double>::quiet_NaN()) {}
|
||||
style(const std::string& name) : name(name), specularity(std::numeric_limits<double>::quiet_NaN()), transparency(std::numeric_limits<double>::quiet_NaN()) {}
|
||||
|
||||
bool has_specularity() const {
|
||||
return !std::isnan(specularity);
|
||||
}
|
||||
|
||||
bool has_transparency() const {
|
||||
return !std::isnan(transparency);
|
||||
}
|
||||
};
|
||||
|
||||
struct geom_item : public item {
|
||||
style surface_style;
|
||||
style* surface_style;
|
||||
matrix4 matrix;
|
||||
boost::optional<bool> orientation;
|
||||
|
||||
geom_item(const IfcUtil::IfcBaseClass* instance = nullptr) : item(instance) {}
|
||||
geom_item(const IfcUtil::IfcBaseClass* instance, matrix4 m) : item(instance), matrix(m) {}
|
||||
geom_item(matrix4 m) : matrix(m) {}
|
||||
geom_item(const IfcUtil::IfcBaseClass* instance = nullptr) : item(instance), surface_style(nullptr) {}
|
||||
geom_item(const IfcUtil::IfcBaseClass* instance, matrix4 m) : item(instance), surface_style(nullptr), matrix(m) {}
|
||||
geom_item(matrix4 m) : surface_style(nullptr), matrix(m) {}
|
||||
};
|
||||
|
||||
// @todo make 4d for easier multiplication
|
||||
template <size_t N>
|
||||
struct cartesian_base : public geom_item, public eigen_base<Eigen::Vector3d> {
|
||||
cartesian_base() : eigen_base(Eigen::Vector3d::Zero()) {}
|
||||
struct cartesian_base : public item, public eigen_base<Eigen::Vector3d> {
|
||||
cartesian_base() : eigen_base() {}
|
||||
cartesian_base(double x, double y, double z = 0.) : eigen_base(Eigen::Vector3d(x, y, z)) {}
|
||||
};
|
||||
|
||||
@@ -183,7 +234,8 @@ struct point3 : public cartesian_base<3> {
|
||||
print_impl(o, "point3", indent);
|
||||
}
|
||||
|
||||
point3(double x = 0., double y = 0., double z = 0.) : cartesian_base(x, y, z) {}
|
||||
point3() : cartesian_base() {}
|
||||
point3(double x, double y, double z = 0.) : cartesian_base(x, y, z) {}
|
||||
};
|
||||
|
||||
struct direction3 : public cartesian_base<3> {
|
||||
@@ -194,7 +246,8 @@ struct direction3 : public cartesian_base<3> {
|
||||
print_impl(o, "direction3", indent);
|
||||
}
|
||||
|
||||
direction3(double x = 0., double y = 0., double z = 0.) : cartesian_base(x, y, z) {}
|
||||
direction3() : cartesian_base() {}
|
||||
direction3(double x, double y, double z = 0.) : cartesian_base(x, y, z) {}
|
||||
};
|
||||
|
||||
struct curve : public geom_item {
|
||||
@@ -244,16 +297,18 @@ struct bspline_curve : public curve {
|
||||
}
|
||||
};
|
||||
|
||||
struct trimmed_curve : public curve {
|
||||
struct trimmed_curve : public item {
|
||||
// @todo The copy constructor of point3 within the variant fails on the avx instruction
|
||||
// on the default gcc in Ubuntu 18.04 and a recent AMD Ryzen. Probably due to allignment.
|
||||
boost::variant<point3, double> start, end;
|
||||
|
||||
// @todo somehow account for the fact that curve in IFC can be trimmed curve, polyline and composite curve as well.
|
||||
item* basis;
|
||||
bool orientation;
|
||||
|
||||
trimmed_curve() : basis(nullptr), orientation(true) {}
|
||||
// @todo does this make sense? this is to accomodate for the fact that orientation is defined on both TrimmedCurve as well CompCurveSegment
|
||||
boost::optional<bool> orientation_2;
|
||||
|
||||
trimmed_curve() : basis(nullptr), orientation_2(true) {}
|
||||
|
||||
virtual void reverse() {
|
||||
// std::swap(start, end);
|
||||
@@ -288,9 +343,15 @@ struct edge : public trimmed_curve {
|
||||
virtual kinds kind() const { return EDGE; }
|
||||
};
|
||||
|
||||
// template <typename T=item>
|
||||
struct collection : public geom_item {
|
||||
std::vector<item*> children;
|
||||
|
||||
collection() {}
|
||||
collection(const collection& other) {
|
||||
std::transform(other.children.begin(), other.children.end(), std::back_inserter(children), std::mem_fn(&item::clone));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T*> children_as() const {
|
||||
std::vector<T*> ts;
|
||||
@@ -315,16 +376,22 @@ struct collection : public geom_item {
|
||||
|
||||
void print(std::ostream& o, int indent = 0) const {
|
||||
o << std::string(indent, ' ') << "collection" << std::endl;
|
||||
if (!matrix.components->isIdentity()) {
|
||||
if (!matrix.components_->isIdentity()) {
|
||||
matrix.print(o, indent + 4);
|
||||
}
|
||||
for (auto& c : children) {
|
||||
c->print(o, indent + 4);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~collection() {
|
||||
for (auto& c : children) {
|
||||
delete c;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct shell : public collection {
|
||||
struct shell : public collection /*<face>*/ {
|
||||
boost::optional<bool> closed;
|
||||
|
||||
virtual item* clone() const { return new shell(*this); }
|
||||
@@ -342,14 +409,14 @@ struct plane : public surface {
|
||||
}
|
||||
};
|
||||
|
||||
struct face : public collection {
|
||||
struct face : public collection /*<loop>*/ {
|
||||
item* basis;
|
||||
|
||||
virtual item* clone() const { return new face(*this); }
|
||||
virtual kinds kind() const { return FACE; }
|
||||
};
|
||||
|
||||
struct loop : public collection {
|
||||
struct loop : public collection /*<edge>*/ {
|
||||
boost::optional<bool> external, closed;
|
||||
|
||||
virtual item* clone() const { return new loop(*this); }
|
||||
@@ -379,11 +446,13 @@ struct extrusion : public sweep {
|
||||
}
|
||||
};
|
||||
|
||||
struct node : public collection {
|
||||
struct node : public item {
|
||||
std::map<std::string, geom_item*> representations;
|
||||
|
||||
virtual item* clone() const { return new node(*this); }
|
||||
virtual kinds kind() const { return NODE; }
|
||||
|
||||
void print(std::ostream&, int = 0) const {}
|
||||
};
|
||||
|
||||
struct boolean_result : public collection {
|
||||
@@ -418,7 +487,81 @@ struct curves {
|
||||
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void visit(const taxonomy::collection* deep, Fn fn) {
|
||||
for (auto& c : deep->children) {
|
||||
if (c->kind() == taxonomy::COLLECTION) {
|
||||
visit((taxonomy::collection*)c, fn);
|
||||
} else {
|
||||
fn(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Fn>
|
||||
void visit_2(const taxonomy::collection* c, const Fn& fn) {
|
||||
static_assert(std::is_same<T, taxonomy::point3>::value, "@todo Only implemented for point3");
|
||||
for (auto& i : c->children) {
|
||||
if (dynamic_cast<const taxonomy::collection*>(i)) {
|
||||
visit_2<T>(dynamic_cast<const taxonomy::collection*>(i), fn);
|
||||
} else if (i->kind() == taxonomy::POINT3) {
|
||||
fn((const taxonomy::point3*) i);
|
||||
} else if (i->kind() == taxonomy::EDGE) {
|
||||
// @todo maybe make edge a collection then as well?
|
||||
auto l = (const taxonomy::edge *) i;
|
||||
if (l->start.which() == 0) {
|
||||
fn(&boost::get<taxonomy::point3>(l->start));
|
||||
}
|
||||
if (l->end.which() == 0) {
|
||||
fn(&boost::get<taxonomy::point3>(l->end));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
taxonomy::collection* flatten(const taxonomy::collection* deep);
|
||||
|
||||
template <typename Fn>
|
||||
bool apply_predicate_to_collection(taxonomy::item* i, Fn fn) {
|
||||
if (i->kind() == taxonomy::COLLECTION) {
|
||||
auto c = (taxonomy::collection*) i;
|
||||
for (auto& child : c->children) {
|
||||
if (apply_predicate_to_collection(child, fn)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fn(i);
|
||||
}
|
||||
}
|
||||
|
||||
// @nb traverses nested collections
|
||||
template <typename Fn>
|
||||
taxonomy::collection* filter(taxonomy::collection* collection, Fn fn) {
|
||||
auto filtered = new taxonomy::collection;
|
||||
for (auto& child : collection->children) {
|
||||
if (apply_predicate_to_collection(child, fn)) {
|
||||
filtered->children.push_back(child->clone());
|
||||
}
|
||||
}
|
||||
if (filtered->children.empty()) {
|
||||
delete filtered;
|
||||
return nullptr;
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
// @nb traverses nested collections
|
||||
template <typename Fn>
|
||||
taxonomy::collection* filter_in_place(taxonomy::collection* collection, Fn fn) {
|
||||
for (auto it = --collection->children.end(); it >= collection->children.begin(); --it) {
|
||||
if (!apply_predicate_to_collection(*it, fn)) {
|
||||
delete *it;
|
||||
collection->children.erase(it);
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "ifc_parse_api.h"
|
||||
|
||||
#include "../ifcparse/IfcEntityList.h"
|
||||
#include "../ifcparse/ArgumentType.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
@@ -37,9 +36,11 @@ class Argument;
|
||||
class IfcEntityList;
|
||||
class IfcEntityListList;
|
||||
class IfcEntityInstanceData;
|
||||
|
||||
namespace IfcParse {
|
||||
class IfcFile;
|
||||
}
|
||||
|
||||
namespace IfcUtil {
|
||||
class IfcBaseClass;
|
||||
|
||||
@@ -61,11 +62,11 @@ public:
|
||||
virtual operator std::vector<double>() const;
|
||||
virtual operator std::vector<std::string>() const;
|
||||
virtual operator std::vector<boost::dynamic_bitset<> >() const;
|
||||
virtual operator IfcEntityList::ptr() const;
|
||||
virtual operator boost::shared_ptr<IfcEntityList>() const;
|
||||
|
||||
virtual operator std::vector< std::vector<int> >() const;
|
||||
virtual operator std::vector< std::vector<double> >() const;
|
||||
virtual operator IfcEntityListList::ptr() const;
|
||||
virtual operator boost::shared_ptr<IfcEntityListList>() const;
|
||||
|
||||
virtual bool isNull() const = 0;
|
||||
virtual unsigned int size() const = 0;
|
||||
|
||||
@@ -23,10 +23,9 @@
|
||||
#include "ifc_parse_api.h"
|
||||
|
||||
#include "../ifcparse/IfcEntityInstanceData.h"
|
||||
#include "../ifcparse/Argument.h"
|
||||
#include "../ifcparse/IfcSchema.h"
|
||||
|
||||
class Argument;
|
||||
|
||||
namespace IfcUtil {
|
||||
|
||||
class IFC_PARSE_API IfcBaseClass {
|
||||
|
||||
@@ -46,11 +46,14 @@
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
#include "../ifcparse/IfcLogger.h"
|
||||
#include "../ifcparse/Argument.h"
|
||||
#include "../ifcparse/IfcEntityList.h"
|
||||
|
||||
#include "../ifcparse/IfcSpfStream.h"
|
||||
|
||||
#if defined(__llvm__)
|
||||
# define my_thread_local thread_local
|
||||
#elif defined(__GNUC__)
|
||||
/* gcc doesn't know _Thread_local from C11 yet */
|
||||
#ifdef __GNUC__
|
||||
# define my_thread_local __thread
|
||||
#elif __STDC_VERSION__ >= 201112L
|
||||
# define my_thread_local _Thread_local
|
||||
|
||||
@@ -243,7 +243,7 @@ namespace IfcParse {
|
||||
|
||||
std::vector<const inverse_attribute*> inverse_attributes_;
|
||||
|
||||
class attribute_by_name_cmp : public std::unary_function<const attribute*, bool> {
|
||||
class attribute_by_name_cmp {
|
||||
private:
|
||||
std::string name_;
|
||||
public:
|
||||
@@ -399,14 +399,14 @@ namespace IfcParse {
|
||||
std::vector<const enumeration_type*> enumeration_types_;
|
||||
std::vector<const entity*> entities_;
|
||||
|
||||
class declaration_by_name_cmp : public std::binary_function<const declaration*, const std::string&, bool> {
|
||||
class declaration_by_name_cmp {
|
||||
public:
|
||||
bool operator()(const declaration* decl, const std::string& name) {
|
||||
return decl->name_lc() < name;
|
||||
}
|
||||
};
|
||||
|
||||
class declaration_by_index_sort : public std::binary_function<const declaration*, const declaration*, bool> {
|
||||
class declaration_by_index_sort {
|
||||
public:
|
||||
bool operator()(const declaration* a, const declaration* b) {
|
||||
return a->index_in_schema() < b->index_in_schema();
|
||||
|
||||
@@ -197,20 +197,20 @@ void ColladaSerializer::ColladaExporter::ColladaScene::add(
|
||||
// If this is not the first parent, get the relative placement
|
||||
if (parentNodes.size() > 0)
|
||||
{
|
||||
auto m4 = ifcopenshell::geometry::taxonomy::matrix4(*matrixStack.top().data().components * *transformation.data().components);
|
||||
auto m4 = ifcopenshell::geometry::taxonomy::matrix4(matrixStack.top().data().ccomponents() * transformation.data().ccomponents());
|
||||
relative_trsf = new ifcopenshell::geometry::Transformation(transformation.settings(), m4);
|
||||
transformation_towrite = relative_trsf;
|
||||
}
|
||||
|
||||
// @todo verify
|
||||
|
||||
const double* m = transformation_towrite->data().components->data();
|
||||
const auto& m = transformation_towrite->data().ccomponents();
|
||||
|
||||
double matrix_array[4][4] = {
|
||||
{ m[0], m[4], m[8], m[12] },
|
||||
{ m[1], m[5], m[9], m[13] },
|
||||
{ m[2], m[6], m[10], m[14] },
|
||||
{ m[3], m[7], m[11], m[15] }
|
||||
{ m(0), m(4), m(8), m(12) },
|
||||
{ m(1), m(5), m(9), m(13) },
|
||||
{ m(2), m(6), m(10), m(14) },
|
||||
{ m(3), m(7), m(11), m(15) }
|
||||
};
|
||||
|
||||
/// @todo: TFK: Rather than applying this offset to all leafs (which might be undesirable) should this offset be applied to a node higher up in the hierarchy?
|
||||
@@ -251,19 +251,19 @@ void ColladaSerializer::ColladaExporter::ColladaScene::addParent(const ifcopensh
|
||||
// If this is not the first parent, get the relative placement
|
||||
if (parentNodes.size() > 0)
|
||||
{
|
||||
auto m4 = ifcopenshell::geometry::taxonomy::matrix4(*matrixStack.top().data().components * *parent_trsf.data().components);
|
||||
auto m4 = ifcopenshell::geometry::taxonomy::matrix4(matrixStack.top().data().ccomponents() * parent_trsf.data().ccomponents());
|
||||
relative_trsf = new ifcopenshell::geometry::Transformation(parent_trsf.settings(), m4);
|
||||
transformation_towrite = relative_trsf;
|
||||
}
|
||||
|
||||
// @todo verify
|
||||
|
||||
const double* parentMatrix = transformation_towrite->data().components->data();
|
||||
const auto& parentMatrix = transformation_towrite->data().ccomponents();
|
||||
|
||||
double matrix_array[4][4] = {
|
||||
{ (double)parentMatrix[0], (double)parentMatrix[3], (double)parentMatrix[6], (double)parentMatrix[9] },
|
||||
{ (double)parentMatrix[1], (double)parentMatrix[4], (double)parentMatrix[7], (double)parentMatrix[10] },
|
||||
{ (double)parentMatrix[2], (double)parentMatrix[5], (double)parentMatrix[8], (double)parentMatrix[11] },
|
||||
{ (double)parentMatrix(0), (double)parentMatrix(3), (double)parentMatrix(6), (double)parentMatrix(9) },
|
||||
{ (double)parentMatrix(1), (double)parentMatrix(4), (double)parentMatrix(7), (double)parentMatrix(10) },
|
||||
{ (double)parentMatrix(2), (double)parentMatrix(5), (double)parentMatrix(8), (double)parentMatrix(11) },
|
||||
{ 0, 0, 0, 1 }
|
||||
};
|
||||
|
||||
@@ -280,7 +280,7 @@ void ColladaSerializer::ColladaExporter::ColladaScene::addParent(const ifcopensh
|
||||
current_node->addMatrix(matrix_array);
|
||||
|
||||
// Add the node to the parent stack
|
||||
matrixStack.push(ifcopenshell::geometry::Transformation(parent_trsf.settings(), ifcopenshell::geometry::taxonomy::matrix4(parent_trsf.data().components->inverse())));
|
||||
matrixStack.push(ifcopenshell::geometry::Transformation(parent_trsf.settings(), ifcopenshell::geometry::taxonomy::matrix4(parent_trsf.data().ccomponents().inverse())));
|
||||
parentNodes.push(current_node);
|
||||
serializer->parentStackId.push(parent.id());
|
||||
}
|
||||
@@ -319,19 +319,17 @@ void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::write
|
||||
openEffect(material_uri + "-fx");
|
||||
COLLADASW::EffectProfile effect(mSW);
|
||||
effect.setShaderType(COLLADASW::EffectProfile::LAMBERT);
|
||||
if (material.diffuse) {
|
||||
const auto& diffuse = *material.diffuse.get().components;
|
||||
effect.setDiffuse(COLLADASW::ColorOrTexture(COLLADASW::Color(diffuse[0],diffuse[1],diffuse[2])));
|
||||
}
|
||||
if (material.specular) {
|
||||
const auto& specular = *material.specular.get().components;
|
||||
const auto& diffuse = material.diffuse.ccomponents();
|
||||
effect.setDiffuse(COLLADASW::ColorOrTexture(COLLADASW::Color(diffuse[0],diffuse[1],diffuse[2])));
|
||||
if (material.specular.components_) {
|
||||
const auto& specular = material.specular.ccomponents();
|
||||
effect.setSpecular(COLLADASW::ColorOrTexture(COLLADASW::Color(specular[0],specular[1],specular[2])));
|
||||
}
|
||||
if (material.specularity) {
|
||||
effect.setShininess(*material.specularity);
|
||||
if (material.has_specularity()) {
|
||||
effect.setShininess(material.specularity);
|
||||
}
|
||||
if (material.transparency) {
|
||||
const double transparency = *material.transparency;
|
||||
if (material.has_transparency()) {
|
||||
const double transparency = material.transparency;
|
||||
if (transparency > 0) {
|
||||
// The default opacity mode for Collada is A_ONE, which apparently indicates a
|
||||
// transparency value of 1 to be fully opaque. Hence transparency is inverted.
|
||||
@@ -352,10 +350,10 @@ void ColladaSerializer::ColladaExporter::ColladaMaterials::add(const ifcopenshel
|
||||
|
||||
// @todo apparently material.name is unitialized in some cases now.
|
||||
|
||||
std::string material_name = material.name.get_value_or("missing-material");
|
||||
std::string material_name = material.name;
|
||||
|
||||
if (material_name.empty()) {
|
||||
material_name = "missing-material-" + *material.name;
|
||||
material_name = "missing-material-" + material.name;
|
||||
}
|
||||
|
||||
collada_id(material_name);
|
||||
|
||||
@@ -80,7 +80,7 @@ void GltfSerializer::writeHeader() {
|
||||
|
||||
int GltfSerializer::writeMaterial(const ifcopenshell::geometry::taxonomy::style& style) {
|
||||
// @todo is it safe to always dereference this optional?
|
||||
const std::string& name = *style.name;
|
||||
const std::string& name = style.name;
|
||||
|
||||
auto it = materials_.find(name);
|
||||
if (it != materials_.end()) {
|
||||
@@ -92,18 +92,16 @@ int GltfSerializer::writeMaterial(const ifcopenshell::geometry::taxonomy::style&
|
||||
|
||||
std::array<double, 4> base;
|
||||
base.fill(1.0);
|
||||
if (style.diffuse) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
base[i] = (*style.diffuse->components)[i];
|
||||
}
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
base[i] = style.diffuse.ccomponents()(i);
|
||||
}
|
||||
if (style.transparency) {
|
||||
base[3] = 1. - *style.transparency;
|
||||
if (style.has_transparency()) {
|
||||
base[3] = 1. - style.transparency;
|
||||
}
|
||||
|
||||
json_["materials"].push_back({ {"pbrMetallicRoughness", {{"baseColorFactor", base}, {"metallicFactor", 0}}} });
|
||||
|
||||
if (style.transparency && *style.transparency > 1.e-9) {
|
||||
if (style.has_transparency() && style.transparency > 1.e-9) {
|
||||
json_["materials"].back()["alphaMode"] = "BLEND";
|
||||
}
|
||||
|
||||
@@ -167,14 +165,14 @@ void GltfSerializer::write(const ifcopenshell::geometry::TriangulationElement* o
|
||||
|
||||
node_array_.push_back(json_["nodes"].size());
|
||||
|
||||
const double* m = o->transformation().data().components->data();
|
||||
const auto& m = o->transformation().data().ccomponents();
|
||||
|
||||
// nb: note that this applies the Y-UP transform.
|
||||
const std::array<double, 16> matrix_flat = {
|
||||
m[ 0], m[ 2], -m[ 1], m[ 3],
|
||||
m[ 4], m[ 6], -m[ 5], m[ 7],
|
||||
m[ 8], m[10], -m[ 9], m[11],
|
||||
m[12], m[14], -m[13], m[15]
|
||||
m( 0), m( 2), -m( 1), m( 3),
|
||||
m( 4), m( 6), -m( 5), m( 7),
|
||||
m( 8), m(10), -m( 9), m(11),
|
||||
m(12), m(14), -m(13), m(15)
|
||||
};
|
||||
|
||||
static const std::array<double, 16> identity_matrix = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
|
||||
|
||||
@@ -490,7 +490,7 @@ void SvgSerializer::setFile(IfcParse::IfcFile* f) {
|
||||
auto item = mapping_->map(*product->get("ObjectPlacement"));
|
||||
if (item) {
|
||||
auto matrix = (ifcopenshell::geometry::taxonomy::matrix4*) item;
|
||||
const double& Z = (*matrix->components)(3, 2);
|
||||
const double& Z = matrix->ccomponents()(3, 2);
|
||||
setSectionHeight(Z + 1.);
|
||||
Logger::Warning("No building storeys encountered, used for reference:", product);
|
||||
return;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user