This commit is contained in:
Thomas Krijnen
2024-01-30 09:04:13 +01:00
6 changed files with 234 additions and 67 deletions
+41 -33
View File
@@ -37,6 +37,46 @@ double to_radian(double deg) { return PI * deg / 180; }
#define Schema Ifc4x3_add2
// performs basic project setup including created the IfcProject object
// and initializing the project units to FEET
Schema::IfcProject* setup_project(IfcHierarchyHelper<Schema>& file) {
std::vector<std::string> file_description;
file_description.push_back("ViewDefinition[Alignment-basedReferenceView]");
file.header().file_description().description(file_description);
auto project = file.addProject();
project->setName(std::string("FHWA Bridge Geometry Manual Example Alignment"));
// set up project units for feet
// the call to file.addProject() sets up length units as millimeter.
auto units_in_context = project->UnitsInContext();
auto units = units_in_context->Units();
auto begin = units->begin();
auto iter = begin;
auto end = units->end();
for (; iter != end; iter++) {
auto unit = *iter;
if (unit->as<Schema::IfcSIUnit>() && unit->as<Schema::IfcSIUnit>()->UnitType() == Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT) {
auto dimensions = new Schema::IfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0);
file.addEntity(dimensions);
auto conversion_factor = new Schema::IfcMeasureWithUnit(new Schema::IfcLengthMeasure(304.80), unit->as<Schema::IfcSIUnit>());
file.addEntity(conversion_factor);
auto conversion_based_unit = new Schema::IfcConversionBasedUnit(dimensions, Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT, "FEET", conversion_factor);
file.addEntity(conversion_based_unit);
units->remove(unit); // remove the millimeter unit
units->push(conversion_based_unit); // add the feet unit
units_in_context->setUnits(units); // update the UnitsInContext
break; // Done!, the length unit was found, so break out of the loop
}
}
return project;
}
// creates geometry and business logic segments for horizontal alignment tangent runs
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_tangent(typename Schema::IfcCartesianPoint* p, double dir, double length) {
// geometry
@@ -161,39 +201,7 @@ void create_segment_representations(IfcHierarchyHelper<Schema>& file, Schema::If
int main() {
IfcHierarchyHelper<Schema> file;
std::vector<std::string> file_description;
file_description.push_back("ViewDefinition[Alignment-basedReferenceView]");
file.header().file_description().description(file_description);
auto project = file.addProject();
project->setName(std::string("FHWA Bridge Geometry Manual Example Alignment"));
// set up project units for feet
// the call to file.addProject() sets up length units as millimeter.
auto units_in_context = project->UnitsInContext();
auto units = units_in_context->Units();
auto begin = units->begin();
auto iter = begin;
auto end = units->end();
for (; iter != end; iter++) {
auto unit = *iter;
if (unit->as<Schema::IfcSIUnit>() && unit->as<Schema::IfcSIUnit>()->UnitType() == Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT) {
auto dimensions = new Schema::IfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0);
file.addEntity(dimensions);
auto conversion_factor = new Schema::IfcMeasureWithUnit(new Schema::IfcLengthMeasure(304.80), unit->as<Schema::IfcSIUnit>());
file.addEntity(conversion_factor);
auto conversion_based_unit = new Schema::IfcConversionBasedUnit(dimensions, Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT, "FEET", conversion_factor);
file.addEntity(conversion_based_unit);
units->remove(unit); // remove the millimeter unit
units->push(conversion_based_unit); // add the feet unit
units_in_context->setUnits(units); // update the UnitsInContext
break; // Done!, the length unit was found, so break out of the loop
}
}
auto project = setup_project(file);
auto geometric_representation_context = file.getRepresentationContext(std::string("Model")); // creates the representation context if it doesn't already exist
+70 -7
View File
@@ -312,15 +312,22 @@ class cant_adjuster : public segment_geometry_adjuster {
const Eigen::Matrix4d& get_start_of_segment() const { return transformation_matrix_; }
};
// vector of parent curve types that are supported for IfcCurveSegment.ParentCurve
typedef boost::mpl::vector<
IfcSchema::IfcLine
#ifdef SCHEMA_HAS_IfcClothoid
, IfcSchema::IfcClothoid
#endif
#if defined SCHEMA_HAS_IfcSecondOrderPolynomialSpiral
//, IfcSchema::IfcSecondOrderPolynomialSpiral // this isn't implemented yet, just some stubbed out dummy code
, IfcSchema::IfcSecondOrderPolynomialSpiral
#endif
, IfcSchema::IfcPolyline
#if defined SCHEMA_HAS_IfcThirdOrderPolynomialSpiral
, IfcSchema::IfcThirdOrderPolynomialSpiral
#endif
#if defined SCHEMA_HAS_IfcSeventhOrderPolynomialSpiral
, IfcSchema::IfcSeventhOrderPolynomialSpiral
#endif
, IfcSchema::IfcPolyline
, IfcSchema::IfcCircle
, IfcSchema::IfcPolynomialCurve
> curve_seg_types;
@@ -451,9 +458,9 @@ class curve_segment_evaluator {
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, segment_type_, inst_, next_inst_);
// see https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcClothoid.htm
// also see, https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Partial_Templates/Geometry/Curve_Segment_Geometry/Clothoid_Transition_Segment/content.html,
// which defines the clothoid constant as sqrt(L) and L is the length measured from the inflection point
// which defines the clothoid constant as sqrt(L*R) and L is the length measured from the inflection point and R is the radius at L
auto A = c->ClothoidConstant();
auto s = fabs(A * sqrt(PI));
auto s = fabs(A * sqrt(PI)); // curve length when u = 1.0
auto fn_x = [A, s](double t) -> double { return s * cos(PI * A * t * t / (2 * fabs(A))); };
auto fn_y = [A, s](double t) -> double { return s * sin(PI * A * t * t / (2 * fabs(A))); };
@@ -465,7 +472,6 @@ class curve_segment_evaluator {
#ifdef SCHEMA_HAS_IfcSecondOrderPolynomialSpiral
void operator()(const IfcSchema::IfcSecondOrderPolynomialSpiral* c)
{
// @todo: rb verify - this is an example implementation of a different kind of spiral - lots of clean up needed
auto A0 = c->ConstantTerm();
auto A1 = c->LinearTerm();
auto A2 = c->QuadraticTerm();
@@ -481,11 +487,68 @@ class curve_segment_evaluator {
auto fn_x = [theta](double t)->double {return cos(theta(t)); };
auto fn_y = [theta](double t)->double {return sin(theta(t)); };
double s = 1.0; // @todo: rb - this is supposed to be the curve length when the parametric value u = 1.0
double s = 100.0; // @todo: rb - this is supposed to be the curve length when the parametric value u = 1.0
Logger::Warning(std::string("IfcSecondOrderPolynomialSpiral - the implementation has a bug"));
set_spiral_function(mapping_, c, s, fn_x, fn_y);
}
#endif
#ifdef SCHEMA_HAS_IfcThirdOrderPolynomialSpiral
void operator()(const IfcSchema::IfcThirdOrderPolynomialSpiral* c) {
auto A0 = c->ConstantTerm();
auto A1 = c->LinearTerm();
auto A2 = c->QuadraticTerm();
auto A3 = c->CubicTerm();
auto theta = [A0, A1, A2, A3](double t) {
auto a0 = A0.has_value() ? t / A0.value() : 0.0;
auto a1 = A1.has_value() ? A1.value() * std::pow(t, 2) / (2 * fabs(std::pow(A1.value(), 3))) : 0.0;
auto a2 = A2.has_value() ? std::pow(t, 3) / (3 * std::pow(A2.value(), 3)) : 0.0;
auto a3 = A3 * std::pow(t, 4) / (4 * fabs(std::pow(A3, 5)));
return a0 + a1 + a2 + a3;
};
auto fn_x = [theta](double t) -> double { return cos(theta(t)); };
auto fn_y = [theta](double t) -> double { return sin(theta(t)); };
double s = 100.0; // @todo: rb - this is supposed to be the curve length when the parametric value u = 1.0
Logger::Warning(std::string("IfcThirdOrderPolynomialSpiral - the implementation has a bug"));
set_spiral_function(mapping_, c, s, fn_x, fn_y);
}
#endif
#ifdef SCHEMA_HAS_IfcSeventhOrderPolynomialSpiral
void operator()(const IfcSchema::IfcSeventhOrderPolynomialSpiral* c) {
auto A0 = c->ConstantTerm();
auto A1 = c->LinearTerm();
auto A2 = c->QuadraticTerm();
auto A3 = c->CubicTerm();
auto A4 = c->QuarticTerm();
auto A5 = c->QuinticTerm();
auto A6 = c->SexticTerm();
auto A7 = c->SepticTerm();
auto theta = [A0, A1, A2, A3, A4, A5, A6, A7](double t) {
auto a0 = A0.has_value() ? t / A0.value() : 0.0;
auto a1 = A1.has_value() ? A1.value() * std::pow(t, 2) / (2 * fabs(std::pow(A1.value(), 3))) : 0.0;
auto a2 = A2.has_value() ? std::pow(t, 3) / (3 * std::pow(A2.value(), 3)) : 0.0;
auto a3 = A3.has_value() ? A3.value() * std::pow(t, 4) / (4 * fabs(std::pow(A3.value(), 5))) : 0.0;
auto a4 = A4.has_value() ? std::pow(t, 5) / (5 * std::pow(A4.value(), 5)) : 0.0;
auto a5 = A5.has_value() ? A5.value() * std::pow(t, 6) / (6 * fabs(std::pow(A5.value(), 7))) : 0.0;
auto a6 = A6.has_value() ? std::pow(t, 7) / (7 * std::pow(A6.value(), 7)) : 0.0;
auto a7 = A7 * std::pow(t, 8) / (8 * fabs(std::pow(A7, 9)));
return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7;
};
auto fn_x = [theta](double t) -> double { return cos(theta(t)); };
auto fn_y = [theta](double t) -> double { return sin(theta(t)); };
double s = 100.0; // @todo: rb - this is supposed to be the curve length when the parametric value u = 1.0
Logger::Warning(std::string("IfcSeventhOrderPolynomialSpiral - the implementation has a bug"));
set_spiral_function(mapping_, c, s, fn_x, fn_y);
}
#endif
void operator()(const IfcSchema::IfcCircle* c)
{
auto R = c->Radius() * length_unit_;
@@ -714,7 +777,7 @@ class curve_segment_evaluator {
std::array<const std::vector<double>*, 2> coefficients{&coeffX, &coeffY};
std::array<double, 2> position{0.0, 0.0}; // = SUM(coeff*u^pos)
std::array<double, 2> slope{0.0, 0.0}; // slope is derivative of the curve = SUM( coeff*pos*u^(pos-1) )
for (int i = 0; i < 2; i++) {
for (int i = 0; i < 2; i++) { // loop over X and Y
auto length_conversion = length_unit;
auto begin = coefficients[i]->cbegin();
auto end = coefficients[i]->cend();
@@ -12,12 +12,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copz of the Lesser GNU General Public License *
* 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 "mapping.h"
#include "../profile_helper.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
@@ -34,17 +35,12 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
Logger::Error("IfcOffsetCurveByDistances must have at least one offset value");
}
auto basis_curve = inst->BasisCurve();
auto first_offset_value = *(offset_values->begin());
// todo@ rb - basis_curve might not be piecewise - other valid types are IfcOffsetCurveByDistances, IfcPolyline and IfcIndexedPolyCurve
// Is there a more generic type that can be evaluated at "u"?
auto basis = taxonomy::cast<taxonomy::piecewise_function>(map(basis_curve));
double basis_curve_length = 0;
for (auto& s : basis->spans) {
basis_curve_length += s.first;
}
auto basis_curve = inst->BasisCurve();
//auto pw_curve = ifcopenshell::geometry::piecewise_from_item(map(basis_curve));
auto pw_curve = taxonomy::dcast<taxonomy::piecewise_function>(map(basis_curve));
double basis_curve_length = pw_curve->length();
auto offsets = taxonomy::make<taxonomy::piecewise_function>(&settings_);
@@ -95,7 +91,10 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
#endif
if ((dp < 0.0 || basis_curve_length < dp)
or
(dn < 0.0 || basis_curve_length < dn))
(dn < 0.0 || basis_curve_length < dn)
or
(dn < dp)
)
{
Logger::Warning("IfcOffsetCurveByDistance offset value is out of bounds.");
continue;
@@ -144,14 +143,14 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
offsets->spans.push_back({l, fn});
}
auto composition = [basis, offsets](double u)->Eigen::Matrix4d {
auto p = basis->evaluate(u);
auto composition = [pw_curve, offsets](double u) -> Eigen::Matrix4d {
auto p = pw_curve->evaluate(u);
auto offset = offsets->evaluate(u);
Eigen::Matrix4d m = p * offset;
return m;
};
// current implementation assumes that offsets is equal to the full length of basis curve
// current implementation assumes that composition is equal to the full length of basis curve
// this may change depending on decisions in the bSI-IF
auto pwf = taxonomy::make<taxonomy::piecewise_function>(&settings_);
pwf->spans.emplace_back( basis_curve_length, composition );
@@ -18,6 +18,8 @@
********************************************************************************/
#include "mapping.h"
#include "../profile_helper.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
@@ -25,9 +27,11 @@ using namespace ifcopenshell::geometry;
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPointByDistanceExpression* inst) {
auto u = (*inst->DistanceAlong()->as<IfcSchema::IfcLengthMeasure>()) * length_unit_;
// @todo: rb - is it safe to assume the basis curve is a piecewise_function?
auto curve = ifcopenshell::geometry::taxonomy::cast<ifcopenshell::geometry::taxonomy::piecewise_function>(map(inst->BasisCurve()));
auto m = curve->evaluate(u);
//auto basis_curve = inst->BasisCurve();
//auto item = map(basis_curve);
//auto pw_curve = ifcopenshell::geometry::piecewise_from_item(item);
auto pw_curve = taxonomy::dcast<taxonomy::piecewise_function>(map(inst->BasisCurve()));
auto m = pw_curve->evaluate(u);
auto o = m.col(3).head<3>();
auto z = m.col(2).head<3>();
+3 -5
View File
@@ -455,9 +455,7 @@ ifcopenshell::geometry::taxonomy::solid::ptr ifcopenshell::geometry::create_box(
ifcopenshell::geometry::taxonomy::item::ptr ifcopenshell::geometry::taxonomy::piecewise_function::evaluate() const {
// @todo configure resolution
double length = 0.0;
for (auto& s : spans)
length += s.first;
double curve_length = length();
std::vector<taxonomy::point3::ptr> polygon;
@@ -466,12 +464,12 @@ ifcopenshell::geometry::taxonomy::item::ptr ifcopenshell::geometry::taxonomy::pi
int num_steps = 0;
if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) {
// parameter is max step size
num_steps = (int)std::ceil(length / param);
num_steps = (int)std::ceil(curve_length / param);
} else {
// parameter is minimum number of steps
num_steps = (int)std::ceil(param);
}
auto resolution = length / num_steps;
auto resolution = curve_length / num_steps;
for (int i = 0; i <= num_steps; ++i) {
auto u = resolution * i;
+100 -5
View File
@@ -2,6 +2,7 @@
#define TAXONOMY_H
#include "../ifcparse/IfcBaseClass.h"
#include "../ifcparse/IfcLogger.h"
#include "ConversionSettings.h"
@@ -14,6 +15,7 @@
#include <string>
#include <tuple>
#include <exception>
#include <numeric>
#ifndef TAXONOMY_USE_UNIQUE_PTR
#ifndef TAXONOMY_USE_NAKED_PTR
@@ -147,6 +149,10 @@ typedef item const* ptr;
// length of span, function to evaluate span
std::vector<std::pair<double, std::function<Eigen::Matrix4d(double u)>>> spans;
double length() const {
return std::accumulate(spans.begin(), spans.end(), 0.0, [](const auto& v,const auto& s) { return v + s.first; });
}
void print(std::ostream& o, int = 0) const {
o << "piecewise_function" << std::endl;
}
@@ -753,6 +759,7 @@ typedef item const* ptr;
DECLARE_PTR(loop)
boost::optional<bool> external, closed;
boost::optional<taxonomy::piecewise_function::ptr> pwf;
bool is_polyhedron() const {
for (auto& e : children) {
@@ -1096,6 +1103,70 @@ typedef item const* ptr;
}
};
// Hacks around not wanting to use if constexpr
template <typename T>
class loop_to_piecewise_function_upgrade {
public:
loop_to_piecewise_function_upgrade(taxonomy::ptr) {}
operator bool() const {
return false;
}
operator taxonomy::piecewise_function::ptr() const {
throw taxonomy::topology_error();
}
operator typename T::ptr() const {
throw taxonomy::topology_error();
}
};
template <>
class loop_to_piecewise_function_upgrade<taxonomy::piecewise_function> {
private:
boost::optional<taxonomy::piecewise_function::ptr> pwf_;
public:
loop_to_piecewise_function_upgrade(taxonomy::ptr item) {
auto loop = taxonomy::dcast<taxonomy::loop>(item);
if (loop) {
if (loop->pwf.is_initialized()) {
pwf_ = loop->pwf;
} else {
pwf_ = taxonomy::make<taxonomy::piecewise_function>();
for (auto& edge : loop->children) {
// the edge could be an arc or trimmed circle in the case of IfcIndexPolyCurve - support for this isn't implemented yet
if (edge->basis) {
Logger::Message(Logger::Severity::LOG_NOTICE, "Shape of basis curve ignored - edge is treated as a straight line edge");
}
const auto& s = boost::get<taxonomy::point3::ptr>(edge->start)->ccomponents();
const auto& e = boost::get<taxonomy::point3::ptr>(edge->end)->ccomponents();
Eigen::Vector3d v = e - s;
auto l = v.norm(); // the norm of a vector is a measure of its length
v.normalize(); // normalize the vector so that it is a unit direction vector
std::function<Eigen::Matrix4d(double)> fn = [s, v](double u) {
Eigen::Vector3d o(s + u * v), axis(0, 0, 1), refDirection(v);
auto Y = axis.cross(refDirection).normalized();
axis = refDirection.cross(Y).normalized();
return taxonomy::make<taxonomy::matrix4>(o, axis, refDirection)->components();
};
(*pwf_)->spans.emplace_back(l, fn);
}
loop->pwf = pwf_;
}
}
}
operator bool() const {
return pwf_.is_initialized();
}
operator taxonomy::piecewise_function::ptr() const {
return *pwf_;
}
};
#ifdef TAXONOMY_USE_SHARED_PTR
template <typename T, typename U>
@@ -1104,6 +1175,10 @@ typedef item const* ptr;
if (upg) {
return upg;
}
loop_to_piecewise_function_upgrade<T> pwupg(u);
if (pwupg) {
return pwupg;
}
return std::static_pointer_cast<T>(u);
}
template <typename T, typename U>
@@ -1112,7 +1187,11 @@ typedef item const* ptr;
if (upg) {
return upg;
}
return std::dynamic_pointer_cast<T>(u);
loop_to_piecewise_function_upgrade<T> pwupg(u);
if (pwupg) {
return pwupg;
}
return std::dynamic_pointer_cast<T>(u);
}
#endif
#ifdef TAXONOMY_USE_UNIQUE_PTR
@@ -1122,7 +1201,11 @@ typedef item const* ptr;
if (upg) {
return upg;
}
return static_cast<T*>(&*u);
loop_to_piecewise_function_upgrade<T> pwupg(u);
if (pwupg) {
return pwupg;
}
return static_cast<T*>(&*u);
}
template <typename T, typename U>
T* dcast(const std::unique_ptr<U>& u) {
@@ -1130,7 +1213,11 @@ typedef item const* ptr;
if (upg) {
return upg;
}
return dynamic_cast<T*>(&*u);
loop_to_piecewise_function_upgrade<T> pwupg(u);
if (pwupg) {
return pwupg;
}
return dynamic_cast<T*>(&*u);
}
#endif
#ifdef TAXONOMY_USE_NAKED_PTR
@@ -1140,7 +1227,11 @@ typedef item const* ptr;
if (upg) {
return upg;
}
return std::static_cast<T*>(u);
loop_to_piecewise_function_upgrade<T> pwupg(u);
if (pwupg) {
return pwupg;
}
return std::static_cast<T*>(u);
}
template <typename T, typename U>
T* dcast(const U*& u) {
@@ -1148,7 +1239,11 @@ typedef item const* ptr;
if (upg) {
return upg;
}
return std::dynamic_cast<T*>(u);
loop_to_piecewise_function_upgrade<T> pwupg(u);
if (pwupg) {
return pwupg;
}
return std::dynamic_cast<T*>(u);
}
#endif