mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 02:47:48 +00:00
Merge pull request #4023 from RickBrice/v0.8.0
v0.8.0 Alignment Geometry - Bug fixes and more work in progress
This commit is contained in:
@@ -33,3 +33,6 @@ set_target_properties(IfcAdvancedHouse PROPERTIES FOLDER Examples)
|
||||
|
||||
endif()
|
||||
|
||||
ADD_EXECUTABLE(IfcAlignment IfcAlignment.cpp)
|
||||
TARGET_LINK_LIBRARIES(IfcAlignment ${IFCOPENSHELL_LIBRARIES})
|
||||
set_target_properties(IfcAlignment PROPERTIES FOLDER Examples)
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
// This example illustrates the basic of building an alignment model.
|
||||
// The alignment is based on "Bridge Geometry Manual", April 2022
|
||||
// US Department of Transportation, Federal Highway Administration (FHWA)
|
||||
// https://www.fhwa.dot.gov/bridge/pubs/hif22034.pdf
|
||||
//
|
||||
// Sections and page number for this document are cited in the code comments.
|
||||
|
||||
// Disable warnings coming from IfcOpenShell
|
||||
#pragma warning(disable:4018 4267 4250 4984 4985)
|
||||
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcparse/Ifc4x3_add2.h"
|
||||
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
|
||||
const double PI = boost::math::constants::pi<double>();
|
||||
double ToRadian(double deg) { return PI * deg / 180; }
|
||||
|
||||
#define Schema Ifc4x3_add2
|
||||
|
||||
// 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
|
||||
auto parent_curve = new Schema::IfcLine(
|
||||
new Schema::IfcCartesianPoint(std::vector<double>({0,0})),
|
||||
new Schema::IfcVector(new Schema::IfcDirection(std::vector<double>{1.0, 0.0}), 1.0));
|
||||
|
||||
auto curve_segment = new Schema::IfcCurveSegment(
|
||||
Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT,
|
||||
new Schema::IfcAxis2Placement2D(p, new Schema::IfcDirection(std::vector<double>{cos(dir), sin(dir)})),
|
||||
new Schema::IfcLengthMeasure(0.0), // start
|
||||
new Schema::IfcLengthMeasure(length),
|
||||
parent_curve);
|
||||
|
||||
// business logic
|
||||
auto design_parameters = new Schema::IfcAlignmentHorizontalSegment(
|
||||
boost::none, boost::none, p, dir, 0.0, 0.0, length, boost::none,
|
||||
Schema::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE);
|
||||
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(
|
||||
IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||
|
||||
return { curve_segment,alignment_segment };
|
||||
}
|
||||
|
||||
// creates geometry and business logic segments for horizontal alignment horizonal curves
|
||||
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_hcurve(typename Schema::IfcCartesianPoint* pc, typename Schema::IfcCartesianPoint* cc, double dir, double radius,double lc)
|
||||
{
|
||||
// geometry
|
||||
double sign = radius / fabs(radius);
|
||||
auto parent_curve = new Schema::IfcCircle(
|
||||
new Schema::IfcAxis2Placement2D(cc, new Schema::IfcDirection(std::vector<double>{cos(dir - sign*PI / 2), sin(dir - sign*PI / 2)})),
|
||||
fabs(radius));
|
||||
|
||||
auto curve_segment = new Schema::IfcCurveSegment(
|
||||
Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT,
|
||||
new Schema::IfcAxis2Placement2D(new Schema::IfcCartesianPoint(std::vector<double>({ 0,0 })), new Schema::IfcDirection(std::vector<double>{1, 0})),
|
||||
new Schema::IfcLengthMeasure(0.0),
|
||||
new Schema::IfcLengthMeasure(sign*lc),
|
||||
parent_curve);
|
||||
|
||||
// business logic
|
||||
auto design_parameters = new Schema::IfcAlignmentHorizontalSegment(boost::none, boost::none, pc, dir, radius, radius, lc, boost::none, Schema::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CIRCULARARC);
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr,boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||
|
||||
return { curve_segment,alignment_segment };
|
||||
}
|
||||
|
||||
// creates geometry and business logic segments for vertical profile gradient runs
|
||||
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_gradient(typename Schema::IfcCartesianPoint* p,double slope,double length)
|
||||
{
|
||||
// geometry
|
||||
auto l = sqrt(1.0 + slope * slope);
|
||||
auto dx = 1.0 / l;
|
||||
auto dy = slope / l;
|
||||
|
||||
auto parent_curve = new Schema::IfcLine(
|
||||
new Schema::IfcCartesianPoint(std::vector<double>({ 0,p->Coordinates()[1]})),
|
||||
new Schema::IfcVector(new Schema::IfcDirection(std::vector<double>{dx,dy}), 1.0));
|
||||
|
||||
auto curve_segment = new Schema::IfcCurveSegment(
|
||||
Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT,
|
||||
new Schema::IfcAxis2Placement2D(new Schema::IfcCartesianPoint(std::vector<double>({ 0, 0 })), new Schema::IfcDirection(std::vector<double>{1,0})),
|
||||
new Schema::IfcLengthMeasure(0.0), // start
|
||||
new Schema::IfcLengthMeasure(length),
|
||||
parent_curve);
|
||||
|
||||
// business logic
|
||||
auto design_parameters = new Schema::IfcAlignmentVerticalSegment(boost::none, boost::none, p->Coordinates()[0], length, p->Coordinates()[1], slope, slope, boost::none, Schema::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT);
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||
|
||||
return { curve_segment,alignment_segment };
|
||||
}
|
||||
|
||||
// creates geometry and business logic segments for vertical profile parabolic vertical curves
|
||||
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_vcurve(typename Schema::IfcCartesianPoint* p, double start_slope,double end_slope, double length)
|
||||
{
|
||||
// geometry
|
||||
double A = p->Coordinates()[1];
|
||||
double B = start_slope;
|
||||
double C = (end_slope - start_slope) / (2 * length);
|
||||
auto parent_curve_placement = new Schema::IfcAxis2Placement2D(new Schema::IfcCartesianPoint(std::vector<double>{0.0, 0.0}), new Schema::IfcDirection(std::vector<double>{1.0, 0.0}));
|
||||
auto parent_curve = new Schema::IfcPolynomialCurve(parent_curve_placement, std::vector<double>{0.0, 1.0}, std::vector<double>{A,B,C}, boost::none);
|
||||
auto segment_curve_placement = new Schema::IfcAxis2Placement2D(new Schema::IfcCartesianPoint(std::vector<double>{0.0, 0.0}), new Schema::IfcDirection(std::vector<double>{1.0, 0.0}));
|
||||
auto curve_segment = new Schema::IfcCurveSegment(Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, segment_curve_placement,
|
||||
new Schema::IfcLengthMeasure(p->Coordinates()[0]),
|
||||
new Schema::IfcLengthMeasure(length),
|
||||
parent_curve);
|
||||
|
||||
// business logic
|
||||
double k = (end_slope - start_slope) / length;
|
||||
auto design_patameters = new Schema::IfcAlignmentVerticalSegment(boost::none, boost::none, p->Coordinates()[0], length, p->Coordinates()[1], start_slope, end_slope, 1 / k, Schema::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_PARABOLICARC);
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_patameters);
|
||||
|
||||
return { curve_segment,alignment_segment };
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
IfcHierarchyHelper<Schema> file;
|
||||
|
||||
std::vector<std::string> file_description;
|
||||
std::ostringstream os;
|
||||
os << "ViewDefinition[Alignment-basedReferenceView]" << std::ends;
|
||||
file_description.push_back(os.str().c_str());
|
||||
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 site = file.addSite(project, nullptr);
|
||||
|
||||
|
||||
//
|
||||
// Define horizontal alignment
|
||||
//
|
||||
|
||||
// define key points
|
||||
// B.1.4 pg 212
|
||||
auto pob = file.addDoublet<Schema::IfcCartesianPoint>(500, 2500); // beginning
|
||||
auto pc1 = file.addDoublet<Schema::IfcCartesianPoint>(2142.237995, 1436.014820); // Point of curve (PC), Curve #1
|
||||
auto cc1 = file.addDoublet<Schema::IfcCartesianPoint>(2685.979298, 2275.267700); // Center of circle (CC), Curve #1
|
||||
auto pt1 = file.addDoublet<Schema::IfcCartesianPoint>(3660.446123, 2050.736173); // Point of tangent (PT), Curve #1
|
||||
auto pc2 = file.addDoublet<Schema::IfcCartesianPoint>(4084.115884, 3889.462938);
|
||||
auto cc2 = file.addDoublet<Schema::IfcCartesianPoint>(5302.199416, 3608.798529);
|
||||
auto pt2 = file.addDoublet<Schema::IfcCartesianPoint>(5469.395067, 4847.566310);
|
||||
auto pc3 = file.addDoublet<Schema::IfcCartesianPoint>(7019.971367, 4638.286073);
|
||||
auto cc3 = file.addDoublet<Schema::IfcCartesianPoint>(6892.902672, 3696.822560);
|
||||
auto pt3 = file.addDoublet<Schema::IfcCartesianPoint>(7790.932128, 4006.730765);
|
||||
auto poe = file.addDoublet<Schema::IfcCartesianPoint>(8480, 2010); // ending
|
||||
|
||||
// define tangent runs and curve lengths
|
||||
double run_1 = 1956.785654;
|
||||
double lc_1 = 1919.222667;
|
||||
double run_2 = 1886.905454;
|
||||
double lc_2 = 1848.115835;
|
||||
double run_3 = 1564.635765;
|
||||
double lc_3 = 1049.119737;
|
||||
double run_4 = 2112.285084;
|
||||
|
||||
// define curve radii
|
||||
double rc_1 = 1000;
|
||||
double rc_2 = -1250; // negative radius for curves to the right
|
||||
double rc_3 = -950;
|
||||
|
||||
// curve delta angles
|
||||
// pg 17, Eq 2.16 - 2.19
|
||||
double angle_1 = ToRadian(327.0613);
|
||||
double angle_2 = ToRadian(77.0247);
|
||||
double angle_3 = ToRadian(352.3133);
|
||||
double angle_4 = ToRadian(289.0395);
|
||||
|
||||
// geometric representations
|
||||
typename aggregate_of<typename Schema::IfcSegment>::ptr horizontal_curve_segments(new aggregate_of<typename Schema::IfcSegment>());
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr horizontal_segments(new aggregate_of<typename Schema::IfcObjectDefinition>());
|
||||
|
||||
// POB to PC1
|
||||
auto curve_segment_1 = create_tangent(pob, angle_1, run_1);
|
||||
horizontal_curve_segments->push(curve_segment_1.first);
|
||||
horizontal_segments->push(curve_segment_1.second);
|
||||
|
||||
// Curve 1
|
||||
auto curve_segment_2 = create_hcurve(pc1, cc1,angle_1,rc_1,lc_1);
|
||||
horizontal_curve_segments->push(curve_segment_2.first);
|
||||
horizontal_segments->push(curve_segment_2.second);
|
||||
|
||||
// PT1 to PC2
|
||||
auto curve_segment_3 = create_tangent(pt1, angle_2, run_2);
|
||||
horizontal_curve_segments->push(curve_segment_3.first);
|
||||
horizontal_segments->push(curve_segment_3.second);
|
||||
|
||||
// Curve 2
|
||||
auto curve_segment_4 = create_hcurve(pc2, cc2, angle_2, rc_2, lc_2);
|
||||
horizontal_curve_segments->push(curve_segment_4.first);
|
||||
horizontal_segments->push(curve_segment_4.second);
|
||||
|
||||
// PT2 to PC3
|
||||
auto curve_segment_5 = create_tangent(pt2, angle_3, run_3);
|
||||
horizontal_curve_segments->push(curve_segment_5.first);
|
||||
horizontal_segments->push(curve_segment_5.second);
|
||||
|
||||
// Curve 3
|
||||
auto curve_segment_6 = create_hcurve(pc3, cc3, angle_3, rc_3, lc_3);
|
||||
horizontal_curve_segments->push(curve_segment_6.first);
|
||||
horizontal_segments->push(curve_segment_6.second);
|
||||
|
||||
// PT3 to POE
|
||||
auto curve_segment_7 = create_tangent(pt3, angle_4, run_4);
|
||||
horizontal_curve_segments->push(curve_segment_7.first);
|
||||
horizontal_segments->push(curve_segment_7.second);
|
||||
|
||||
// Zero-length terminator segment
|
||||
auto terminator_segment = create_tangent(poe, angle_4, 0.0);
|
||||
terminator_segment.first->setTransition(Schema::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS);
|
||||
horizontal_curve_segments->push(terminator_segment.first);
|
||||
horizontal_segments->push(terminator_segment.second);
|
||||
|
||||
auto composite_curve = new Schema::IfcCompositeCurve(horizontal_curve_segments, false/*not self-intersecting*/);
|
||||
file.addEntity(composite_curve);
|
||||
|
||||
typename aggregate_of<typename Schema::IfcRepresentationItem>::ptr representation_items(new aggregate_of<typename Schema::IfcRepresentationItem>());
|
||||
representation_items->push(composite_curve);
|
||||
|
||||
auto geometric_representation_context = file.getRepresentationContext(std::string("3D")); // creates the representation context if it doesn't already exist
|
||||
auto representation_subcontext = new Schema::IfcGeometricRepresentationSubContext(std::string("Axis"), std::string("Model"), geometric_representation_context, boost::none, Schema::IfcGeometricProjectionEnum::IfcGeometricProjection_GRAPH_VIEW, boost::none);
|
||||
file.addEntity(representation_subcontext);
|
||||
auto shape_representation_2D = new Schema::IfcShapeRepresentation(representation_subcontext, std::string("FootPrint"), std::string("Curve2D"), representation_items);
|
||||
file.addEntity(shape_representation_2D);
|
||||
|
||||
auto horizontal_alignment = new Schema::IfcAlignmentHorizontal(IfcParse::IfcGlobalId(), nullptr, std::string("Horizontal Alignment"), boost::none, boost::none, nullptr, nullptr/*representation*/);
|
||||
file.addEntity(horizontal_alignment);
|
||||
|
||||
auto nests_horizontal_segments = new Schema::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, boost::none, std::string("Nests horizontal alignment segments with horizontal alignment"), horizontal_alignment, horizontal_segments);
|
||||
file.addEntity(nests_horizontal_segments);
|
||||
|
||||
//
|
||||
// Define vertical profile segments
|
||||
//
|
||||
|
||||
typename aggregate_of<typename Schema::IfcSegment>::ptr vertical_curve_segments(new aggregate_of<typename Schema::IfcSegment>());
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr vertical_segments(new aggregate_of<typename Schema::IfcObjectDefinition>());
|
||||
|
||||
// define key profile points
|
||||
auto vpob = file.addDoublet<Schema::IfcCartesianPoint>(0.0, 100.0); // beginning
|
||||
auto vpc1 = file.addDoublet<Schema::IfcCartesianPoint>(1200.0, 121.0); // Vertical Curve Point (VPC), Vertical Curve #1
|
||||
auto vpt1 = file.addDoublet<Schema::IfcCartesianPoint>(2800.0, 127.0); // Vertical Curve Tangent (VPT), Vertical Curve #1
|
||||
auto vpc2 = file.addDoublet<Schema::IfcCartesianPoint>(4400.0, 111.0);
|
||||
auto vpt2 = file.addDoublet<Schema::IfcCartesianPoint>(5600.0, 117.0);
|
||||
auto vpc3 = file.addDoublet<Schema::IfcCartesianPoint>(6400.0, 133.0);
|
||||
auto vpt3 = file.addDoublet<Schema::IfcCartesianPoint>(8400.0, 133.0);
|
||||
auto vpc4 = file.addDoublet<Schema::IfcCartesianPoint>(9400.0, 113.0);
|
||||
auto vpt4 = file.addDoublet<Schema::IfcCartesianPoint>(10200.0, 103.0);
|
||||
auto vpoe = file.addDoublet<Schema::IfcCartesianPoint>(12800.0, 90.0); // ending
|
||||
|
||||
// Grade start to VPC1
|
||||
auto vertical_profile_segment_1 = create_gradient(vpob, 1.75 / 100, 1200);
|
||||
vertical_curve_segments->push(vertical_profile_segment_1.first);
|
||||
vertical_segments->push(vertical_profile_segment_1.second);
|
||||
|
||||
// Vertical Curve 1
|
||||
auto vertical_profile_segment_2 = create_vcurve(vpc1, 1.75 / 100, -1.0 / 100, 1600);
|
||||
vertical_curve_segments->push(vertical_profile_segment_2.first);
|
||||
vertical_segments->push(vertical_profile_segment_2.second);
|
||||
|
||||
// Grade VPT1 to VPC2
|
||||
auto vertical_profile_segment_3 = create_gradient(vpt1, -1.0 / 100, 1600);
|
||||
vertical_curve_segments->push(vertical_profile_segment_3.first);
|
||||
vertical_segments->push(vertical_profile_segment_3.second);
|
||||
|
||||
// Vertical Curve 2
|
||||
auto vertical_profile_segment_4 = create_vcurve(vpc2, -1.0 / 100, 2.0 / 100, 1200);
|
||||
vertical_curve_segments->push(vertical_profile_segment_4.first);
|
||||
vertical_segments->push(vertical_profile_segment_4.second);
|
||||
|
||||
// Grade PVT2 to VPC3
|
||||
auto vertical_profile_segment_5 = create_gradient(vpt2, 2.0 / 100, 800);
|
||||
vertical_curve_segments->push(vertical_profile_segment_5.first);
|
||||
vertical_segments->push(vertical_profile_segment_5.second);
|
||||
|
||||
// Vertical Curve 3
|
||||
auto vertical_profile_segment_6 = create_vcurve(vpc3, 2.0 / 100, -2.0 / 100, 2000);
|
||||
vertical_curve_segments->push(vertical_profile_segment_6.first);
|
||||
vertical_segments->push(vertical_profile_segment_6.second);
|
||||
|
||||
// Grade PVT3 to VPC4
|
||||
auto vertical_profile_segment_7 = create_gradient(vpt3, -2.0 / 100, 1000);
|
||||
vertical_curve_segments->push(vertical_profile_segment_7.first);
|
||||
vertical_segments->push(vertical_profile_segment_7.second);
|
||||
|
||||
// Vertical Curve 4
|
||||
auto vertical_profile_segment_8 = create_vcurve(vpc4, -2.0 / 100, -0.5 / 100, 800);
|
||||
vertical_curve_segments->push(vertical_profile_segment_8.first);
|
||||
vertical_segments->push(vertical_profile_segment_8.second);
|
||||
|
||||
// Grade VPT4 to End
|
||||
auto vertical_profile_segment_9 = create_gradient(vpt4, -0.5 / 100, 2600);
|
||||
vertical_curve_segments->push(vertical_profile_segment_9.first);
|
||||
vertical_segments->push(vertical_profile_segment_9.second);
|
||||
|
||||
// Zero-length terminator
|
||||
auto vertical_terminator_segment = create_gradient(vpoe, -0.5 / 100, 0.0);
|
||||
vertical_curve_segments->push(vertical_terminator_segment.first);
|
||||
vertical_segments->push(vertical_terminator_segment.second);
|
||||
|
||||
auto vertical_profile = new Schema::IfcAlignmentVertical(IfcParse::IfcGlobalId(), nullptr, std::string("Vertical Alignment"), boost::none, boost::none, file.getSingle<typename Schema::IfcLocalPlacement>(), nullptr);
|
||||
file.addEntity(vertical_profile);
|
||||
|
||||
auto nests_vertical_segments = new Schema::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, boost::none, std::string("Nests vertical alignment segments with vertical alignment"), vertical_profile, vertical_segments);
|
||||
file.addEntity(nests_vertical_segments);
|
||||
|
||||
|
||||
typename aggregate_of<typename Schema::IfcRepresentationItem>::ptr representation_items2(new aggregate_of<typename Schema::IfcRepresentationItem>());
|
||||
|
||||
auto gradient_curve = new Schema::IfcGradientCurve(vertical_curve_segments, false, composite_curve, nullptr);
|
||||
representation_items2->push(gradient_curve);
|
||||
|
||||
|
||||
auto shape_representation_3D = new Schema::IfcShapeRepresentation(representation_subcontext, std::string("Axis"), std::string("Curve3D"), representation_items2);
|
||||
file.addEntity(shape_representation_3D);
|
||||
|
||||
typename aggregate_of<typename Schema::IfcRepresentation>::ptr representations(new aggregate_of<typename Schema::IfcRepresentation>());
|
||||
representations->push(shape_representation_2D); // 2D alignment geometry
|
||||
representations->push(shape_representation_3D); // 3D alignment geometry
|
||||
auto alignment_product = new Schema::IfcProductDefinitionShape(std::string("Alignment Product Definition Shape"), boost::none, representations);
|
||||
|
||||
auto local_placement = site->ObjectPlacement();
|
||||
if (!local_placement)
|
||||
{
|
||||
local_placement = file.addLocalPlacement();
|
||||
}
|
||||
|
||||
auto alignment = new Schema::IfcAlignment(IfcParse::IfcGlobalId(), nullptr, std::string("Example Alignment"), boost::none, boost::none, local_placement, alignment_product, boost::none);
|
||||
file.addEntity(alignment);
|
||||
|
||||
file.relatePlacements(site, horizontal_alignment);
|
||||
file.relatePlacements(site, vertical_profile);
|
||||
file.relatePlacements(site, alignment);
|
||||
|
||||
// 4.1.4.4.1 Alignments nest horizontal and vertical layouts
|
||||
// https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/concepts/Object_Composition/Nesting/Alignment_Layouts/content.html
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr alignment_layout_list(new aggregate_of<typename Schema::IfcObjectDefinition>());
|
||||
alignment_layout_list->push(horizontal_alignment);
|
||||
alignment_layout_list->push(vertical_profile);
|
||||
|
||||
auto nests_alignment_layouts = new Schema::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, std::string("Nest horizontal and vertical alignment layouts with the alignment"), boost::none, alignment, alignment_layout_list);
|
||||
file.addEntity(nests_alignment_layouts);
|
||||
|
||||
// IFC 4.1.4.1.1 "Every IfcAlignment must be related to IfcProject using the IfcRelAggregates relationship"
|
||||
// https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/concepts/Object_Composition/Aggregation/Alignment_Aggregation_To_Project/content.html
|
||||
// IfcProject <-> IfcRelAggregates <-> IfcAlignment
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr list_of_alignments_in_project(new aggregate_of<typename Schema::IfcObjectDefinition>());
|
||||
list_of_alignments_in_project->push(alignment);
|
||||
auto aggregate_alignments_with_project = new Schema::IfcRelAggregates(IfcParse::IfcGlobalId(), nullptr, std::string("Alignments in project"), boost::none, project, list_of_alignments_in_project);
|
||||
file.addEntity(aggregate_alignments_with_project);
|
||||
|
||||
// IFC 4.1.5.1 alignment is referenced in spatial structure of an IfcSpatialElement. In this case IfcSite is the highest level IfcSpatialElement
|
||||
// https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/concepts/Object_Connectivity/Alignment_Spatial_Reference/content.html
|
||||
// IfcSite <-> IfcRelReferencedInSpatialStructure <-> IfcAlignment
|
||||
// This means IfcAlignment is not part of the IfcSite (it is not an aggregate component) but instead IfcAlignment is used within
|
||||
// the IfcSite by reference. This implies an IfcAlignment can traverse many IfcSite instances within an IfcProject
|
||||
typename Schema::IfcSpatialReferenceSelect::list::ptr list_alignments_referenced_in_site(new Schema::IfcSpatialReferenceSelect::list);
|
||||
list_alignments_referenced_in_site->push(alignment);
|
||||
auto rel_referenced_in_spatial_structure = new Schema::IfcRelReferencedInSpatialStructure(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, list_alignments_referenced_in_site, site);
|
||||
file.addEntity(rel_referenced_in_spatial_structure);
|
||||
|
||||
std::ofstream ofs("FHWA_Bridge_Geometry_Alignment_Example.ifc");
|
||||
ofs << file;
|
||||
}
|
||||
@@ -37,3 +37,17 @@ std::istream& ifcopenshell::geometry::settings::operator>>(std::istream& in, Ite
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
std::istream& ifcopenshell::geometry::settings::operator>>(std::istream& in, PiecewiseStepMethod& ioo) {
|
||||
std::string token;
|
||||
in >> token;
|
||||
boost::to_upper(token);
|
||||
if (token == "MAXSTEPSIZE") {
|
||||
ioo = MAXSTEPSIZE;
|
||||
} else if (token == "MINSTEPS") {
|
||||
ioo = MINSTEPS;
|
||||
} else {
|
||||
in.setstate(std::ios_base::failbit);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
@@ -300,12 +300,29 @@ namespace ifcopenshell {
|
||||
static constexpr const char* const description = "Overrides transparency of spaces in geometry output.";
|
||||
};
|
||||
|
||||
enum PiecewiseStepMethod {
|
||||
MAXSTEPSIZE,
|
||||
MINSTEPS };
|
||||
|
||||
std::istream& operator>>(std::istream& in, PiecewiseStepMethod& ioo);
|
||||
|
||||
struct PiecewiseStepType : public SettingBase<PiecewiseStepType, PiecewiseStepMethod> {
|
||||
static constexpr const char* const name = "piecewise-step-type";
|
||||
static constexpr const char* const description = "Indicates the method used for defining step size when evaluating piecewise curves. Provides interpretation of piecewise-step-param";
|
||||
static constexpr PiecewiseStepMethod defaultvalue = MAXSTEPSIZE;
|
||||
};
|
||||
|
||||
struct PiecewiseStepParam : public SettingBase<PiecewiseStepParam, double> {
|
||||
static constexpr const char* const name = "piecewise-step-param";
|
||||
static constexpr const char* const description = "Indicates the parameter value for defining step size when evaluating piecewise curves.";
|
||||
static constexpr double defaultvalue = 0.5; // ceiling of this value is used when PiecewiseStepMethod is MinSteps
|
||||
};
|
||||
}
|
||||
|
||||
template <typename settings_t>
|
||||
class IFC_GEOM_API SettingsContainer {
|
||||
public:
|
||||
typedef boost::variant<bool, int, double, std::string, std::set<int>, IteratorOutputOptions> value_variant_t;
|
||||
typedef boost::variant<bool, int, double, std::string, std::set<int>, IteratorOutputOptions, PiecewiseStepMethod> value_variant_t;
|
||||
private:
|
||||
settings_t settings;
|
||||
|
||||
@@ -385,7 +402,7 @@ namespace ifcopenshell {
|
||||
};
|
||||
|
||||
class IFC_GEOM_API Settings : public SettingsContainer<
|
||||
std::tuple<MesherLinearDeflection, MesherAngularDeflection, ReorientShells, LengthUnit, PlaneUnit, Precision, IncludeCurves, IncludeSurfaces, LayersetFirst, DisableBooleanResult, NoWireIntersectionCheck, NoWireIntersectionTolerance, PrecisionFactor, DebugBooleanOperations, BooleanAttempt2d, WeldVertices, UseWorldCoords, ConvertBackUnits, ContextIds, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency>
|
||||
std::tuple<MesherLinearDeflection, MesherAngularDeflection, ReorientShells, LengthUnit, PlaneUnit, Precision, IncludeCurves, IncludeSurfaces, LayersetFirst, DisableBooleanResult, NoWireIntersectionCheck, NoWireIntersectionTolerance, PrecisionFactor, DebugBooleanOperations, BooleanAttempt2d, WeldVertices, UseWorldCoords, ConvertBackUnits, ContextIds, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency, PiecewiseStepType, PiecewiseStepParam>
|
||||
>
|
||||
{};
|
||||
}
|
||||
|
||||
@@ -43,23 +43,27 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcAxis2PlacementLinear* inst)
|
||||
if (hasAxis) {
|
||||
taxonomy::direction3::ptr v = taxonomy::cast<taxonomy::direction3>(map(inst->Axis()));
|
||||
axis = *v->components_;
|
||||
}
|
||||
} else {
|
||||
// 8.9.3.4 IfcAxis2LinearPlacement does not specify the default when Axis is omitted
|
||||
// When RefDirection is omitted, see comment below, it is taken to be tangent to the curve.
|
||||
// To be consistent, Axis is taken to be orthogonal to RefDirection
|
||||
axis = m->components().col(2).head<3>();
|
||||
}
|
||||
|
||||
if (hasRef) {
|
||||
taxonomy::direction3::ptr v = taxonomy::cast<taxonomy::direction3>(map(inst->RefDirection()));
|
||||
refDirection = *v->components_;
|
||||
} else {
|
||||
// @todo: rb "If RefDirection is omitted, the direction is taken from the curve tangent at Location"
|
||||
// 8.9.3.4 IfcAxis2LinearPlacement
|
||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcAxis2PlacementLinear.htm
|
||||
// Based on our email discussion, I'm using the perpendicular direction towards the right as viewed in the XY Plane for .RefDirection
|
||||
// "If RefDirection is omitted, the direction is taken from the curve tangent at Location"
|
||||
//
|
||||
// When the PointByDistanceExpression Location is evaluated, it is evaluating the basis curve and returning
|
||||
// the matrix of orthogonal vectors that define the coordinate system at the point on curve as well as the point on curve
|
||||
// In other words, the m matrix has everything needed
|
||||
|
||||
refDirection = m->components().col(1).head<3>();
|
||||
|
||||
axis = m->components().col(2).head<3>();
|
||||
refDirection = m->components().col(0).head<3>();
|
||||
|
||||
}
|
||||
return taxonomy::make<taxonomy::matrix4>(o, axis, refDirection);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ using namespace ifcopenshell::geometry;
|
||||
|
||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) {
|
||||
auto loop = taxonomy::make<taxonomy::loop>();
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>();
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(&settings_);
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcSegment
|
||||
// 4x3
|
||||
|
||||
@@ -25,6 +25,8 @@ using namespace ifcopenshell::geometry;
|
||||
|
||||
#include "../profile_helper.h"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include <boost/mpl/vector.hpp>
|
||||
#include <boost/mpl/for_each.hpp>
|
||||
#include <boost/math/quadrature/trapezoidal.hpp>
|
||||
@@ -263,7 +265,7 @@ class linear_segment_geometry_adjuster : public segment_geometry_adjuster {
|
||||
// For now, the derivative of the curvature of the IfcCurve subtype is difficult to implement and example models from the IFC spec
|
||||
// always use IfcAxis2Placement3D with Axis and RefDirection specified, the basic interpolation is used, ignoring the IfcCurve type.
|
||||
//
|
||||
// This implementation will be revised as the understanding of IfcSegmentedRefereneCurve improves.
|
||||
// This implementation will be revised as the understanding of IfcSegmentedReferenceCurve improves.
|
||||
class cant_adjuster : public segment_geometry_adjuster {
|
||||
public:
|
||||
using segment_geometry_adjuster::segment_geometry_adjuster;
|
||||
@@ -274,17 +276,35 @@ class cant_adjuster : public segment_geometry_adjuster {
|
||||
auto& start_next = get_start_of_next_segment();
|
||||
auto l = get_length();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
p.col(i) = start_this.col(i) + (start_next.col(i) - start_this.col(i)) * u / l;
|
||||
if (i < 3) {
|
||||
p.col(i).normalize();
|
||||
};
|
||||
}
|
||||
// tilt angle of vector normal to cant at start of this and start of next segment
|
||||
auto tilt_start_this = atan2(start_this.col(2)(2), start_this.col(2)(1));
|
||||
auto tilt_start_next = atan2(start_next.col(2)(2), start_next.col(2)(1));
|
||||
|
||||
// when cant results are combined with the gradient curve
|
||||
// the x-locate will be added which effective doubles them
|
||||
// for this reason, set x location to 0
|
||||
p.col(3)(0) = 0;
|
||||
// tilt angle of vector normal to cant at u assuming linear interpolation
|
||||
// @todo: rb - rate of change of slope is related to curve type (such as clothoid or line)
|
||||
// need to somehow account for that - it is important when tilt at start of next isn't provided
|
||||
// because it defines how much tilt_start_this varies along the length
|
||||
auto tilt = tilt_start_this + (tilt_start_next - tilt_start_this) * u / l;
|
||||
|
||||
// populate Axis vector
|
||||
p.col(2)(1) = cos(tilt);
|
||||
p.col(2)(2) = sin(tilt);
|
||||
|
||||
// populate Y vector
|
||||
p.col(1)(1) = -p.col(2)(2);
|
||||
p.col(1)(2) = p.col(2)(1);
|
||||
|
||||
// use linear interpolation to compute elevation change due to cant
|
||||
auto st = start_this.col(3)(1);
|
||||
auto sn = start_next.col(3)(1);
|
||||
auto slope = (sn - st) / l;
|
||||
|
||||
// RefDirection.z is due to cant elevation change slope
|
||||
p.col(0)(2) = slope;
|
||||
p.col(0).normalize();
|
||||
|
||||
auto result = st + u * slope;
|
||||
p.col(3)(1) = result;
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -297,7 +317,7 @@ typedef boost::mpl::vector<
|
||||
, IfcSchema::IfcClothoid
|
||||
#endif
|
||||
#if defined SCHEMA_HAS_IfcSecondOrderPolynomialSpiral
|
||||
, IfcSchema::IfcSecondOrderPolynomialSpiral
|
||||
//, IfcSchema::IfcSecondOrderPolynomialSpiral // this isn't implemented yet, just some stubbed out dummy code
|
||||
#endif
|
||||
, IfcSchema::IfcPolyline
|
||||
, IfcSchema::IfcCircle
|
||||
@@ -366,7 +386,7 @@ class curve_segment_evaluator {
|
||||
}
|
||||
}
|
||||
|
||||
void set_spiral_function(mapping* mapping_, const IfcSchema::IfcSpiral* c, double s, std::function<double(double)> signX, std::function<double(double)> fnX, std::function<double(double)> signY, std::function<double(double)> fnY) {
|
||||
void set_spiral_function(mapping* mapping_, const IfcSchema::IfcSpiral* c, double s, std::function<double(double)> signX, std::function<double(double)> fnX, std::function<double(double)> signY, std::function<double(double)> fnY, std::function<double(double)> fnSlope) {
|
||||
// determine the length of the spiral from the local origin to the end point
|
||||
auto sign_s = binary_sign(start_);
|
||||
auto sign_l = binary_sign(length_);
|
||||
@@ -384,7 +404,7 @@ class curve_segment_evaluator {
|
||||
auto segment_type = segment_type_;
|
||||
auto transformation_matrix = taxonomy::cast<taxonomy::matrix4>(mapping_->map(c->Position()))->ccomponents();
|
||||
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, segment_type_, inst_, next_inst_);
|
||||
eval_ = [L, start, s, signX, fnX, signY, fnY, transformation_matrix, segment_type, geometry_adjuster = this->geometry_adjuster](double u) {
|
||||
eval_ = [L, start, s, signX, fnX, signY, fnY, fnSlope, transformation_matrix, segment_type, geometry_adjuster = this->geometry_adjuster](double u) {
|
||||
|
||||
u += start;
|
||||
|
||||
@@ -402,11 +422,17 @@ class curve_segment_evaluator {
|
||||
// However, Dx and Dy are not normalized. Recall that slope = rise/run
|
||||
// If run = 1.0, then rise = Dy/Dx = fnY(u)/fnX(u) and l = sqrt((fnY(u)/fnX(u))^2 + 1.0^2)
|
||||
// The direction ratios are dx = 1.0/l and dy = (fnY/fnX)/l;
|
||||
auto rise = fnY(u) / fnX(u);
|
||||
auto run = 1.0;
|
||||
auto l = sqrt(run * run + rise * rise);
|
||||
auto dx = run / l;
|
||||
auto dy = rise / l;
|
||||
//auto fy = fnY(u);
|
||||
//auto fx = fnX(u);
|
||||
//auto rise = fy / fx;
|
||||
//auto run = 1.0;
|
||||
//auto l = sqrt(run * run + rise * rise);
|
||||
//auto dx = run / l;
|
||||
//auto dy = rise / l;
|
||||
|
||||
auto slope = fnSlope(b);
|
||||
auto dx = signX(u) * cos(slope);
|
||||
auto dy = signY(u) * sin(slope);
|
||||
|
||||
Eigen::Matrix4d m;
|
||||
if (segment_type == ST_HORIZONTAL) {
|
||||
@@ -507,8 +533,10 @@ class curve_segment_evaluator {
|
||||
auto sign_y = [A](double t) { return sign(t) == sign(A) ? 1.0 : -1.0; };
|
||||
auto fn_x = [A, s](double t) -> double { return s * cos(PI * fabs(A) * t * t / (2 * fabs(A))); };
|
||||
auto fn_y = [A, s](double t) -> double { return s * sin(PI * fabs(A) * t * t / (2 * fabs(A))); };
|
||||
//auto fn_slope = [A](double t) -> double { return sqrt(PI) * t * t / (2 * abs(A)); };
|
||||
auto fn_slope = [A, s](double t) -> double { return pow(t*s / A, 2) / 2; };
|
||||
|
||||
set_spiral_function(mapping_, c, s, sign_x, fn_x, sign_y, fn_y);
|
||||
set_spiral_function(mapping_, c, s, sign_x, fn_x, sign_y, fn_y, fn_slope);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -533,18 +561,19 @@ 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)); };
|
||||
auto fn_slope = [](double t)->double { return tan(t); };
|
||||
|
||||
double s = 1.0; // @todo: rb - this is supposed to be the curve length when the parametric value u = 1.0
|
||||
set_spiral_function(mapping_, c, s, sign_x, fn_x, sign_y, fn_y);
|
||||
set_spiral_function(mapping_, c, s, sign_x, fn_x, sign_y, fn_y, fn_slope);
|
||||
}
|
||||
#endif
|
||||
|
||||
void operator()(const IfcSchema::IfcCircle* c)
|
||||
{
|
||||
auto R = c->Radius();
|
||||
auto R = c->Radius() * length_unit_;
|
||||
|
||||
auto sign_l = sign(length_);
|
||||
auto start = start_;
|
||||
auto start_angle = start_/R;
|
||||
|
||||
auto transformation_matrix = taxonomy::cast<taxonomy::matrix4>(mapping_->map(c->Position()))->ccomponents();
|
||||
|
||||
@@ -552,9 +581,9 @@ class curve_segment_evaluator {
|
||||
|
||||
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, segment_type_, inst_, next_inst_);
|
||||
|
||||
eval_ = [R, start, sign_l, transformation_matrix, segment_type, geometry_adjuster = this->geometry_adjuster](double u)
|
||||
eval_ = [R, start_angle, sign_l, transformation_matrix, segment_type, geometry_adjuster = this->geometry_adjuster](double u)
|
||||
{
|
||||
auto angle = start + sign_l * u / R;
|
||||
auto angle = start_angle + sign_l * u / R;
|
||||
|
||||
auto dx = cos(angle);
|
||||
auto dy = sin(angle);
|
||||
@@ -565,15 +594,15 @@ class curve_segment_evaluator {
|
||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||
if (segment_type == ST_HORIZONTAL) {
|
||||
// rotate about the Z-axis
|
||||
m.col(0) = Eigen::Vector4d(dx, dy, 0, 0); // vector tangent to the curve, in the direction of the curve
|
||||
m.col(1) = Eigen::Vector4d(-dy, dx, 0, 0); // vector perpendicular to the curve, towards the left when looking from start to end along the curve (this is used for IfcAxis2PlacementLinear.RefDirection when it is not provided)
|
||||
m.col(2) = Eigen::Vector4d(0, 0, 1.0, 0); // cross product of x and y and will always be up (this is used for IfcAxis2PlacementLinear.Axis when it is not provided)
|
||||
m.col(0) = Eigen::Vector4d(-dy, dx, 0, 0); // vector tangent to the curve, in the direction of the curve
|
||||
m.col(1) = Eigen::Vector4d(-sign_l * dx, -sign_l * dy, 0, 0); // vector perpendicular to the curve, towards the left when looking from start to end along the curve (this is used for IfcAxis2PlacementLinear.RefDirection when it is not provided)
|
||||
m.col(2) = Eigen::Vector4d(0, 0, 1.0, 0); // cross product of x and y and will always be up (this is used for IfcAxis2PlacementLinear.Axis when it is not provided)
|
||||
m.col(3) = Eigen::Vector4d(x, y, 0.0, 1.0);
|
||||
} else if (segment_type == ST_VERTICAL) {
|
||||
// rotate about the Y-axis (slope along u is dx, slope vertically is dy, vertical position is y)
|
||||
m.col(0) = Eigen::Vector4d(dx, 0, dy, 0);
|
||||
m.col(0) = Eigen::Vector4d(-dy, 0, dx, 0);
|
||||
m.col(1) = Eigen::Vector4d(0, 1, 0, 0);
|
||||
m.col(2) = Eigen::Vector4d(-dy, 0, dx, 0);
|
||||
m.col(2) = Eigen::Vector4d(-dx, 0, -dy, 0);
|
||||
m.col(3) = Eigen::Vector4d(0, 0, y, 1.0); // y is an elevation so store it as z
|
||||
} else if (segment_type == ST_CANT) {
|
||||
Logger::Warning(std::runtime_error("Use of IfcCircle for cant is not supported"));
|
||||
@@ -700,12 +729,24 @@ class curve_segment_evaluator {
|
||||
auto s = l->Pnt();
|
||||
auto c = s->Coordinates();
|
||||
auto v = l->Dir();
|
||||
|
||||
// 8.9.3.75 IfcVector https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcVector.htm
|
||||
// 8.9.3.30 IfcDirection https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcDirection.htm
|
||||
// "The IfcDirection does not imply a vector length, and the direction ratios does not have to be normalized."
|
||||
//
|
||||
// Therefore, the direction ratios need to be normalized to compute points on the line. Magnitude is not used
|
||||
// because it relates to the parameterization of the line, which isn't currently done for IfcCurveSegment
|
||||
auto dr = v->Orientation()->DirectionRatios();
|
||||
auto m = v->Magnitude();
|
||||
auto px = c[0];
|
||||
auto py = c[1];
|
||||
auto dx = dr[0] / m;
|
||||
auto dy = dr[1] / m;
|
||||
|
||||
// normalize the direction ratios
|
||||
double m_squared = std::inner_product(dr.begin(), dr.end(), dr.begin(), 0.0);
|
||||
double m = sqrt(m_squared);
|
||||
std::for_each(dr.begin(), dr.end(), [m](auto& d) { return d / m; });
|
||||
auto dx = dr[0];
|
||||
auto dy = dr[1];
|
||||
|
||||
auto px = c[0] * length_unit_;
|
||||
auto py = c[1] * length_unit_;
|
||||
|
||||
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, segment_type_, inst_, next_inst_);
|
||||
if (segment_type_ == ST_HORIZONTAL) {
|
||||
@@ -765,28 +806,35 @@ class curve_segment_evaluator {
|
||||
if (!coeffZ.empty())
|
||||
Logger::Warning("Expected IfcPolynomialCurve.CoefficientsZ to be undefined for alignment geometry. Coefficients ignored.", p);
|
||||
|
||||
|
||||
|
||||
auto transformation_matrix = taxonomy::cast<taxonomy::matrix4>(mapping_->map(p->Position()))->ccomponents();
|
||||
|
||||
auto segment_type = segment_type_;
|
||||
auto length_unit = length_unit_;
|
||||
|
||||
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, segment_type_, inst_, next_inst_);
|
||||
|
||||
|
||||
eval_ = [coeffX, coeffY, transformation_matrix, segment_type, geometry_adjuster = this->geometry_adjuster](double u) {
|
||||
eval_ = [coeffX, coeffY, transformation_matrix, segment_type, length_unit, geometry_adjuster = this->geometry_adjuster](double u) {
|
||||
std::array<const std::vector<double>*, 2> coefficients{&coeffX, &coeffY};
|
||||
std::array<double, 2> position{0.0, 0.0};
|
||||
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++) {
|
||||
auto length_conversion = length_unit;
|
||||
auto begin = coefficients[i]->cbegin();
|
||||
auto end = coefficients[i]->cend();
|
||||
for (auto iter = begin; iter != end; iter++) {
|
||||
for (auto iter = begin; iter != end; iter++) {
|
||||
auto exp = std::distance(begin, iter);
|
||||
position[i] += (*iter) * pow(u, exp);
|
||||
auto coeff = (*iter)*length_conversion;
|
||||
position[i] += coeff* pow(u, exp);
|
||||
|
||||
if (iter != begin) {
|
||||
slope[i] += (*iter) * exp * pow(u, exp - 1);
|
||||
slope[i] += coeff * exp * pow(u, exp - 1);
|
||||
}
|
||||
}
|
||||
|
||||
length_conversion /= length_unit;
|
||||
}
|
||||
}
|
||||
|
||||
auto x = position[0];
|
||||
@@ -804,9 +852,9 @@ class curve_segment_evaluator {
|
||||
m.col(3) = Eigen::Vector4d(x, y, 0.0, 1.0);
|
||||
} else if (segment_type == ST_VERTICAL) {
|
||||
// rotate about the Y-axis (slope along u is dx, slope vertically is dy, vertical position is y)
|
||||
m.col(0) = Eigen::Vector4d(dx, 0, -dy, 0);
|
||||
m.col(0) = Eigen::Vector4d(dx, 0, dy, 0);
|
||||
m.col(1) = Eigen::Vector4d(0, 1, 0, 0);
|
||||
m.col(2) = Eigen::Vector4d(dy, 0, dx, 0);
|
||||
m.col(2) = Eigen::Vector4d(-dy, 0, dx, 0);
|
||||
m.col(3) = Eigen::Vector4d(0, 0, y, 1.0); // y is an elevation so store it as z
|
||||
} else if (segment_type == ST_CANT) {
|
||||
Logger::Warning(std::runtime_error("Use of IfcPolynomialCurve for cant is not supported"));
|
||||
@@ -894,7 +942,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) {
|
||||
auto length = fabs(cse.length());
|
||||
|
||||
// @todo it might be suboptimal that we no longer have the spans now
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>();
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(&settings_);
|
||||
pwf->spans.push_back({ length, fn });
|
||||
pwf->instance = inst;
|
||||
return pwf;
|
||||
|
||||
@@ -28,7 +28,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) {
|
||||
Logger::Warning("Expected IfcGradientCurve.BaseCurve to be IfcCompositeCurve", inst); // CT 4.1.7.1.1.2
|
||||
|
||||
auto horizontal = taxonomy::cast<taxonomy::piecewise_function>(map(inst->BaseCurve()));
|
||||
auto vertical = taxonomy::make<taxonomy::piecewise_function>();
|
||||
auto vertical = taxonomy::make<taxonomy::piecewise_function>(&settings_);
|
||||
|
||||
auto segments = inst->Segments();
|
||||
|
||||
@@ -73,7 +73,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>();
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(&settings_);
|
||||
pwf->spans.emplace_back( min_length, composition );
|
||||
pwf->instance = inst;
|
||||
return pwf;
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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 copz of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include "mapping.h"
|
||||
#define mapping POSTFIX_SCHEMA(mapping)
|
||||
using namespace ifcopenshell::geometry;
|
||||
|
||||
// ifc4x1
|
||||
//#define SCHEMA_IfcOffsetCurveByDistances_HAS_OffsetValues
|
||||
//#define SCHEMA_IfcOffsetCurveByDistances_HAS_Tag
|
||||
//#define SCHEMA_IfcOffsetCurveByDistances_Tag_IS_OPTIONAL
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcOffsetCurveByDistances
|
||||
|
||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst) {
|
||||
auto offset_values = inst->OffsetValues();
|
||||
if (offset_values->size() == 0) {
|
||||
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 offsets = taxonomy::make<taxonomy::piecewise_function>(&settings_);
|
||||
|
||||
#if defined SCHEMA_HAS_IfcDistanceExpression
|
||||
double first_distance = first_offset_value->DistanceAlong();
|
||||
#else
|
||||
double first_distance = *first_offset_value->DistanceAlong()->as<IfcSchema::IfcLengthMeasure>();
|
||||
#endif
|
||||
|
||||
if (first_distance < 0.0) {
|
||||
Logger::Warning("IfcOffsetCurveByDistance first offset value is before the start of the curve.");
|
||||
}
|
||||
|
||||
if(0.0 < first_distance)
|
||||
{
|
||||
// First offset is defined after the start of the curve so the lateral and vertical offsets
|
||||
// implicitly continue with the same value towards the start of the basis curve
|
||||
double py = first_offset_value->OffsetLateral().get_value_or(0.0);
|
||||
double pz = first_offset_value->OffsetVertical().get_value_or(0.0);
|
||||
py *= length_unit_;
|
||||
pz *= length_unit_;
|
||||
|
||||
auto fn = [py, pz](double u) -> Eigen::Matrix4d {
|
||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||
m.col(3)(1) = py;
|
||||
m.col(3)(2) = pz;
|
||||
return m; };
|
||||
offsets->spans.push_back({first_distance, fn});
|
||||
}
|
||||
|
||||
auto iter = offset_values->begin();
|
||||
auto next = std::next(iter);
|
||||
auto prev = std::prev(next);
|
||||
auto end = offset_values->end();
|
||||
for (; next != end; prev++, next++) {
|
||||
#if defined SCHEMA_HAS_IfcPointByDistanceExpression
|
||||
if ((*prev)->BasisCurve() != basis_curve || (*next)->BasisCurve() != basis_curve) {
|
||||
Logger::Error("All offsets from a IfcOffsetCurveByDistances must refer to the same BasisCurve");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined SCHEMA_HAS_IfcDistanceExpression
|
||||
double dn = (*next)->DistanceAlong();
|
||||
double dp = (*prev)->DistanceAlong();
|
||||
#else
|
||||
double dn = *(*next)->DistanceAlong()->as<IfcSchema::IfcLengthMeasure>();
|
||||
double dp = *(*prev)->DistanceAlong()->as<IfcSchema::IfcLengthMeasure>();
|
||||
#endif
|
||||
if ((dp < 0.0 || basis_curve_length < dp)
|
||||
or
|
||||
(dn < 0.0 || basis_curve_length < dn))
|
||||
{
|
||||
Logger::Warning("IfcOffsetCurveByDistance offset value is out of bounds.");
|
||||
continue;
|
||||
}
|
||||
|
||||
double l = (dn - dp)*length_unit_;
|
||||
double yn = (*next)->OffsetLateral().get_value_or(0.0) * length_unit_;
|
||||
double yp = (*prev)->OffsetLateral().get_value_or(0.0) * length_unit_;
|
||||
double zn = (*next)->OffsetVertical().get_value_or(0.0) * length_unit_;
|
||||
double zp = (*prev)->OffsetVertical().get_value_or(0.0) * length_unit_;
|
||||
|
||||
auto fn = [yp, yn, zp, zn, l](double u) -> Eigen::Matrix4d {
|
||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||
m.col(3)(1) = (l == 0.0 ? yp : (yp + (yn - yp) * u / l));
|
||||
m.col(3)(2) = (l == 0.0 ? zp : (zp + (zn - zp) * u / l));
|
||||
return m;
|
||||
};
|
||||
offsets->spans.push_back({l, fn});
|
||||
}
|
||||
|
||||
// at this point, next == end and prev == end-1
|
||||
#if defined SCHEMA_HAS_IfcDistanceExpression
|
||||
double last_distance = (*prev)->DistanceAlong() * length_unit_;
|
||||
#else
|
||||
double last_distance = *(*prev)->DistanceAlong()->as<IfcSchema::IfcLengthMeasure>() * length_unit_;
|
||||
#endif
|
||||
|
||||
if (basis_curve_length < last_distance) {
|
||||
Logger::Warning("IfcOffsetCurveByDistance last offset value is after the end of the curve.");
|
||||
}
|
||||
|
||||
if (last_distance < basis_curve_length) {
|
||||
// Last offset is defined before the end of the curve so the lateral and vertical offsets
|
||||
// implicitly continue with the same value towards the end of the basis curve
|
||||
double py = (*prev)->OffsetLateral().get_value_or(0.0);
|
||||
double pz = (*prev)->OffsetVertical().get_value_or(0.0);
|
||||
py *= length_unit_;
|
||||
pz *= length_unit_;
|
||||
double l = basis_curve_length - last_distance;
|
||||
auto fn = [py, pz](double u) -> Eigen::Matrix4d {
|
||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||
m.col(3)(1) = py;
|
||||
m.col(3)(2) = pz;
|
||||
return m; };
|
||||
|
||||
offsets->spans.push_back({l, fn});
|
||||
}
|
||||
|
||||
auto composition = [basis, offsets](double u)->Eigen::Matrix4d {
|
||||
auto p = basis->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
|
||||
// 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 );
|
||||
pwf->instance = inst;
|
||||
return pwf;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -28,7 +28,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins
|
||||
Logger::Warning("Expected IfcSegmentedReferenceCurve.BaseCurve to be IfcGradient", inst); // CT 4.1.7.1.1.3
|
||||
|
||||
auto gradient = taxonomy::cast<taxonomy::piecewise_function>(map(inst->BaseCurve()));
|
||||
auto cant = taxonomy::make<taxonomy::piecewise_function>();
|
||||
auto cant = taxonomy::make<taxonomy::piecewise_function>(&settings_);
|
||||
|
||||
auto segments = inst->Segments();
|
||||
|
||||
@@ -50,12 +50,13 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins
|
||||
}
|
||||
|
||||
auto composition = [gradient, cant](double u)->Eigen::Matrix4d {
|
||||
auto xyz = gradient->evaluate(u);
|
||||
auto g = gradient->evaluate(u);
|
||||
auto c = cant->evaluate(u);
|
||||
c.col(3)(0) = 0;
|
||||
std::swap(c.col(3)(1),c.col(3)(2));
|
||||
|
||||
std::swap(c.col(3)(1), c.col(3)(2));
|
||||
|
||||
Eigen::Matrix4d m;
|
||||
m = xyz * c;
|
||||
m = g * c;
|
||||
return m;
|
||||
};
|
||||
|
||||
@@ -75,7 +76,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins
|
||||
}
|
||||
}
|
||||
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>();
|
||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(&settings_);
|
||||
pwf->spans.emplace_back( min_length, composition );
|
||||
pwf->instance = inst;
|
||||
return pwf;
|
||||
|
||||
@@ -120,6 +120,9 @@ BIND(IfcSegmentedReferenceCurve);
|
||||
BIND(IfcGradientCurve);
|
||||
#endif
|
||||
BIND(IfcCompositeCurve);
|
||||
#ifdef SCHEMA_HAS_IfcOffsetCurveByDistances
|
||||
BIND(IfcOffsetCurveByDistances)
|
||||
#endif
|
||||
BIND(IfcTrimmedCurve);
|
||||
BIND(IfcArbitraryOpenProfileDef);
|
||||
#ifdef SCHEMA_HAS_IfcIndexedPolyCurve
|
||||
|
||||
@@ -460,11 +460,20 @@ ifcopenshell::geometry::taxonomy::item::ptr ifcopenshell::geometry::taxonomy::pi
|
||||
length += s.first;
|
||||
|
||||
std::vector<taxonomy::point3::ptr> polygon;
|
||||
|
||||
auto param_type = settings_ ? settings_->get<ifcopenshell::geometry::settings::PiecewiseStepType>().get() : ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE;
|
||||
auto param = settings_ ? settings_->get<ifcopenshell::geometry::settings::PiecewiseStepParam>().get() : 0.5;
|
||||
int num_steps = 0;
|
||||
if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) {
|
||||
// parameter is max step size
|
||||
num_steps = (int)std::ceil(length / param);
|
||||
} else {
|
||||
// parameter is minimum number of steps
|
||||
num_steps = (int)std::ceil(param);
|
||||
}
|
||||
auto resolution = length / num_steps;
|
||||
|
||||
static const double target_resolution = 0.5;
|
||||
int num_steps = (int)std::ceil(length / target_resolution);
|
||||
auto resolution = length / num_steps;
|
||||
for (int i = 0; i <= num_steps; ++i) {
|
||||
for (int i = 0; i <= num_steps; ++i) {
|
||||
auto u = resolution * i;
|
||||
Eigen::Matrix4d m = evaluate(u);
|
||||
polygon.push_back(taxonomy::make<taxonomy::point3>(m.col(3)(0), m.col(3)(1), m.col(3)(2)));
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
|
||||
#include "ConversionSettings.h"
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
@@ -127,6 +129,7 @@ typedef item const* ptr;
|
||||
|
||||
struct implicit_item : public item {
|
||||
DECLARE_PTR(implicit_item)
|
||||
using item::item;
|
||||
|
||||
virtual item::ptr evaluate() const = 0;
|
||||
};
|
||||
@@ -134,6 +137,13 @@ typedef item const* ptr;
|
||||
struct piecewise_function : public implicit_item {
|
||||
DECLARE_PTR(piecewise_function)
|
||||
|
||||
piecewise_function(const IfcUtil::IfcBaseInterface* instance = nullptr) : implicit_item(instance){};
|
||||
piecewise_function(ifcopenshell::geometry::Settings* settings) : settings_(settings){};
|
||||
piecewise_function(piecewise_function&&) = default;
|
||||
piecewise_function(const piecewise_function&) = default;
|
||||
|
||||
ifcopenshell::geometry::Settings* settings_ = nullptr;
|
||||
|
||||
// length of span, function to evaluate span
|
||||
std::vector<std::pair<double, std::function<Eigen::Matrix4d(double u)>>> spans;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user