Skip to content

Commit 33ad776

Browse files
authored
Merge pull request #1314 from Creoox/feature-branch2
adjustable number of output curve points in BSpline curves
2 parents 2c1a9ee + 63e946f commit 33ad776

File tree

6 files changed

+231
-243
lines changed

6 files changed

+231
-243
lines changed

src/cpp/web-ifc/geometry/IfcGeometryLoader.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -2104,7 +2104,8 @@ namespace webifc::geometry
21042104
ctrolPts.push_back(GetCartesianPoint3D(pointId));
21052105
}
21062106

2107-
std::vector<glm::dvec3> tempPoints = GetRationalBSplineCurveWithKnots(degree, ctrolPts, knots, weights);
2107+
double numCurvePoints = ctrolPts.size();
2108+
std::vector<glm::dvec3> tempPoints = GetRationalBSplineCurveWithKnots(degree, ctrolPts, knots, weights, numCurvePoints);
21082109
for (size_t i = 0; i < tempPoints.size(); i++) curve.Add(tempPoints[i]);
21092110
}
21102111

@@ -2192,7 +2193,8 @@ namespace webifc::geometry
21922193
uint32_t pointId = _loader.GetRefArgument(token);
21932194
ctrolPts.push_back(GetCartesianPoint3D(pointId));
21942195
}
2195-
std::vector<glm::dvec3> tempPoints = GetRationalBSplineCurveWithKnots(degree, ctrolPts, knots, weights);
2196+
double numCurvePoints = ctrolPts.size();
2197+
std::vector<glm::dvec3> tempPoints = GetRationalBSplineCurveWithKnots(degree, ctrolPts, knots, weights, numCurvePoints);
21962198
for (size_t i = 0; i < tempPoints.size(); i++) curve.Add(tempPoints[i]);
21972199
}
21982200

@@ -2277,7 +2279,8 @@ namespace webifc::geometry
22772279
ctrolPts.push_back(GetCartesianPoint3D(pointId));
22782280
}
22792281

2280-
std::vector<glm::dvec3> tempPoints = GetRationalBSplineCurveWithKnots(degree, ctrolPts, knots, weights);
2282+
double numCurvePoints = ctrolPts.size();
2283+
std::vector<glm::dvec3> tempPoints = GetRationalBSplineCurveWithKnots(degree, ctrolPts, knots, weights, numCurvePoints);
22812284
for (size_t i = 0; i < tempPoints.size(); i++) curve.Add(tempPoints[i]);
22822285
}
22832286

src/cpp/web-ifc/geometry/nurbs.cpp

+92-7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
namespace webifc::geometry{
1212

1313
void Nurbs::fill_geometry(){
14+
if (!_initialized) {
15+
return;
16+
}
1417
auto uv_points {this->get_uv_points()};
1518
auto indices {get_triangulation_uv_points(uv_points)};
1619

@@ -78,14 +81,89 @@ namespace webifc::geometry{
7881
this->init();
7982
}
8083

81-
void Nurbs::init(){
84+
void Nurbs::init() {
85+
// Check that the control point grid has sufficient dimensions.
86+
// We need at least (degree + 1) control points in each direction.
87+
if (this->num_u < static_cast<size_t>(this->bspline_surface.UDegree) + 1) {
88+
spdlog::error("Insufficient control point rows: num_u = {} but UDegree = {} requires at least {} rows",
89+
this->num_u, this->bspline_surface.UDegree, this->bspline_surface.UDegree + 1);
90+
return; // Or throw an exception.
91+
}
92+
if (this->num_v < static_cast<size_t>(this->bspline_surface.VDegree) + 1) {
93+
spdlog::error("Insufficient control point columns: num_v = {} but VDegree = {} requires at least {} columns",
94+
this->num_v, this->bspline_surface.VDegree, this->bspline_surface.VDegree + 1);
95+
return; // Or throw an exception.
96+
}
97+
98+
// Validate degrees.
99+
if (this->bspline_surface.UDegree < 0 || this->bspline_surface.VDegree < 0) {
100+
spdlog::error("Invalid degree values: UDegree={}, VDegree={}",
101+
this->bspline_surface.UDegree, this->bspline_surface.VDegree);
102+
return;
103+
}
104+
105+
// Validate control points count.
106+
size_t expectedCount = this->num_u * this->num_v;
107+
auto controlPoints = this->get_control_points();
108+
if (controlPoints.size() != expectedCount) {
109+
spdlog::error("Control points count mismatch: expected {}, got {}", expectedCount, controlPoints.size());
110+
return;
111+
}
112+
auto weights = this->get_weights();
113+
if (weights.size() != expectedCount) {
114+
spdlog::error("Weights count mismatch: expected {}, got {}", expectedCount, weights.size());
115+
return;
116+
}
117+
118+
// Create the NURBS surface.
82119
this->nurbs = std::make_shared<tinynurbs::RationalSurface3d>(
83-
static_cast<int>(static_cast<uint32_t>(this->bspline_surface.UDegree)),
84-
static_cast<int>(static_cast<uint32_t>(this->bspline_surface.VDegree)),
85-
this->get_knots(this->bspline_surface.UKnots, this->bspline_surface.UMultiplicity),
86-
this->get_knots(this->bspline_surface.VKnots, this->bspline_surface.VMultiplicity),
87-
tinynurbs::array2<glm::dvec3>{this->num_u, this->num_v, this->get_control_points()},
88-
tinynurbs::array2<double>{this->num_u, this->num_v, this->get_weights()});
120+
static_cast<int>(this->bspline_surface.UDegree),
121+
static_cast<int>(this->bspline_surface.VDegree),
122+
this->get_knots(this->bspline_surface.UKnots, this->bspline_surface.UMultiplicity),
123+
this->get_knots(this->bspline_surface.VKnots, this->bspline_surface.VMultiplicity),
124+
tinynurbs::array2<glm::dvec3>{this->num_u, this->num_v, controlPoints},
125+
tinynurbs::array2<double>{this->num_u, this->num_v, weights}
126+
);
127+
128+
// Check that the knot vectors are large enough.
129+
if (this->nurbs->knots_u.size() < static_cast<size_t>(this->nurbs->degree_u + 1)) {
130+
spdlog::error("Invalid knots_u: size {} is less than degree_u+1 ({})",
131+
this->nurbs->knots_u.size(), this->nurbs->degree_u + 1);
132+
return;
133+
}
134+
if (this->nurbs->knots_v.size() < static_cast<size_t>(this->nurbs->degree_v + 1)) {
135+
spdlog::error("Invalid knots_v: size {} is less than degree_v+1 ({})",
136+
this->nurbs->knots_v.size(), this->nurbs->degree_v + 1);
137+
return;
138+
}
139+
140+
// Helper lambda to check if a knot vector is monotonic increasing.
141+
auto check_monotonic = [](const std::vector<double>& knots, const std::string& name) -> bool {
142+
for (size_t i = 1; i < knots.size(); i++) {
143+
if (knots[i] < knots[i - 1]) {
144+
spdlog::error("{} is not monotonic increasing at index {} ({} < {})", name, i, knots[i], knots[i - 1]);
145+
return false;
146+
}
147+
}
148+
return true;
149+
};
150+
151+
if (!check_monotonic(this->nurbs->knots_u, "knots_u")) return;
152+
if (!check_monotonic(this->nurbs->knots_v, "knots_v")) return;
153+
154+
// Ensure that we have enough knots to set the range.
155+
if (this->nurbs->knots_u.size() <= this->nurbs->degree_u ||
156+
this->nurbs->knots_u.size() <= this->nurbs->degree_u + 1) {
157+
spdlog::error("Not enough knots in knots_u to determine range, size={}, degree_u={}",
158+
this->nurbs->knots_u.size(), this->nurbs->degree_u);
159+
return;
160+
}
161+
if (this->nurbs->knots_v.size() <= this->nurbs->degree_v ||
162+
this->nurbs->knots_v.size() <= this->nurbs->degree_v + 1) {
163+
spdlog::error("Not enough knots in knots_v to determine range, size={}, degree_v={}",
164+
this->nurbs->knots_v.size(), this->nurbs->degree_v);
165+
return;
166+
}
89167
this->range_knots_u = {
90168
this->nurbs->knots_u[this->nurbs->degree_u],
91169
this->nurbs->knots_u[this->nurbs->knots_u.size() - this->nurbs->degree_u - 1]
@@ -94,14 +172,21 @@ namespace webifc::geometry{
94172
this->nurbs->knots_v[this->nurbs->degree_v],
95173
this->nurbs->knots_v[this->nurbs->knots_v.size() - this->nurbs->degree_v - 1]
96174
};
175+
176+
// Compute sample surface points.
97177
this->ptc = tinynurbs::surfacePoint(*this->nurbs, 0.0, 0.0);
98178
this->pth = tinynurbs::surfacePoint(*this->nurbs, 1.0, 0.0);
99179
this->ptv = tinynurbs::surfacePoint(*this->nurbs, 0.0, 1.0);
180+
181+
// Compute distances for further use.
100182
this->dh = glm::distance(ptc, pth);
101183
this->dv = glm::distance(ptc, ptv);
102184
this->pr = (dh + 1) / (dv + 1);
185+
186+
// Scale error tolerances.
103187
this->minError /= this->scaling;
104188
this->maxError /= this->scaling;
189+
_initialized = true;
105190
}
106191
std::vector<double> Nurbs::get_weights() const{
107192
std::vector<double> result(this->num_u * this->num_v);

src/cpp/web-ifc/geometry/nurbs.h

+1
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,6 @@ namespace webifc::geometry{
6363
double dh{0.0};
6464
double dv{0.0};
6565
double pr{0.0};
66+
bool _initialized = false;
6667
};
6768
}

src/cpp/web-ifc/geometry/operations/curve-utils.h

+3-4
Original file line numberDiff line numberDiff line change
@@ -296,13 +296,12 @@ inline IfcCurve Build3DArc3Pt(const glm::dvec3 &p1, const glm::dvec3 &p2, const
296296

297297

298298

299-
inline std::vector<glm::dvec3> GetRationalBSplineCurveWithKnots(int degree, std::vector<glm::dvec3> points, std::vector<double> knots, std::vector<double> weights)
299+
inline std::vector<glm::dvec3> GetRationalBSplineCurveWithKnots(int degree, std::vector<glm::dvec3> points, std::vector<double> knots, std::vector<double> weights, double numCurvePoints)
300300
{
301-
302301
spdlog::debug("[GetRationalBSplineCurveWithKnots({})]");
303302
std::vector<glm::dvec3> c;
304-
305-
for (double i = 0; i < 1; i += 0.05)
303+
double step = 1.0/numCurvePoints;
304+
for (double i = 0; i < 1; i += step)
306305
{
307306
glm::dvec3 point = InterpolateRationalBSplineCurveWithKnots(i, degree, points, knots, weights);
308307
c.push_back(point);

tests/regression/results.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"tests/ifcfiles/public/AC20-FZK-Haus.ifc":"6bf7a4d4e18776a6952914fd03acd96f81623ca5da6e5a7876fe4e64fd8df22e","tests/ifcfiles/public/C20-Institute-Var-2.ifc":"288a7ba65b562e1bdd6548f60f0e2339306578625cb445670f0bbf1c8a4560c3","tests/ifcfiles/public/FM_ARC_DigitalHub.ifc":"3f748003c12da6c4ad13df2c202a0997c7517a10cc5303b1555568e03f5b001c","tests/ifcfiles/public/ISSUE_005_haus.ifc":"6bf7a4d4e18776a6952914fd03acd96f81623ca5da6e5a7876fe4e64fd8df22e","tests/ifcfiles/public/ISSUE_021_Mini Project.ifc":"b5076f409b371d847cb453a9ab9a8d9de9d1c0eb27748ba7adc65571e0555a37","tests/ifcfiles/public/ISSUE_034_HouseZ.ifc":"ef512e88e26c916849445d3b741dd7979ee8a0dbf79ca788502092f84ea8f92b","tests/ifcfiles/public/ISSUE_044_test_IFCCOMPOSITEPROFILEDEF.ifc":"1b10710a3237960ed421ed8069bfd7ba2fa42cf15a52ef2139c8b3ed887e9b85","tests/ifcfiles/public/ISSUE_053_20181220Holter_Tower_10.ifczip":"5a7a3711e8841135cc86bf92ba0ae9acc7e97b7a069927a64678c17653d3e609","tests/ifcfiles/public/ISSUE_068_ARK_NUS_skolebygg.ifc":"cd3081a53bd5667173caf391b17c5b1878fef4bfc252ef8330556e39afbd7c0d","tests/ifcfiles/public/ISSUE_102_M3D-CON-CD.ifc":"6e9612de56c08323e96b21e8f4097e42964bdf93066d460ddb0e7c184abdd15b","tests/ifcfiles/public/ISSUE_102_M3D-CON.ifc":"c83a0f31c6a52928fb9797456689c69c63d156c5bdf53cb49a83ed10d9e5bc23","tests/ifcfiles/public/ISSUE_126_model.ifc":"78e336de53ec658f2cd8fb51d5e2797ae1b5832cdadcdc22e7d0330a275a9545","tests/ifcfiles/public/ISSUE_129_N1540_17_EXE_MOD_448200_02_09_11SMC_IGC_V17.ifc":"a36fc248e0a8012045cd11f54ad0e9ed9fd7d7e7729a100599742fa8b06b4971","tests/ifcfiles/public/ISSUE_159_kleine_Wohnung_R22.ifc":"a304e69ac97a6300ef5213254ca5ca3d8b2dd88d7daa026115138e138c5cf395","tests/ifcfiles/public/ISSUE_171_IfcSurfaceCurveSweptAreaSolid.ifc":"f2db8111ba05365cc54a6bcf8aade27764acfb615889f9302088aa1f1aa57c50","tests/ifcfiles/public/IfcOpenHouse_IFC4.ifc":"a9d854a4e47646bdc613aeca3c385474e851ac22b98bb8b7d783f7fee2f42c60","tests/ifcfiles/public/KIT-Simple-Road-Test-Web-IFC4x3_RC2.ifc":"3d8707ee9d70e2aeb67b8bebdc96016a1eceb30afd9a25e9cf6c258d978a6fe6","tests/ifcfiles/public/Office_A_20110811.ifc":"347c5e27081f0e29460124352781054ad2e00c0c09316b20f1981ae4e86a22ee","tests/ifcfiles/public/S_Office_Integrated Design Archi.ifc":"62cb878a60d880d989797a00f0faacdf5ad01a7f9ee1131e6804977366fb509a","tests/ifcfiles/public/Sample_entities.ifc":"270a55940a77ebb4f245cabf8c7429dc8fc7044baf9c9434fc747842f602d792","tests/ifcfiles/public/advanced_model.ifc":"c3247ec09d381688bb901d3bd85ae51a01dba70105cd9274b8cfd194ba94da12","tests/ifcfiles/public/dental_clinic.ifc":"757f4eccbbf9cd59806bee813e902cb13b6ace71bac106e4bce109764a79fbb2","tests/ifcfiles/public/duplex.ifc":"1da1e60682e4be608affcc697e79d24b76b984d7cc9cd465c3b97499a3a681ed","tests/ifcfiles/public/example.ifc":"4b9ab3d47e0b8e82fe2cb9a69ef452192204891528a291e989ccbc02aae27391","tests/ifcfiles/public/ifcbridge-model01.ifc":"00c016be5509e4145b3662dbdeedd67e7e60e6648ad1c247d6a41e41657b7e33","tests/ifcfiles/public/schependomlaan.ifc":"a0c7e4f336323a772f2dde464ed8919c4c5d3b3d4b62342129a4da73f5526880","tests/ifcfiles/public/tested_sample_project.ifc":"c4bf6c87a2e09795f0d854dc2c6eeb92829790c565ff86b1e03007648729bb26"}
1+
{"tests/ifcfiles/public/AC20-FZK-Haus.ifc":"6bf7a4d4e18776a6952914fd03acd96f81623ca5da6e5a7876fe4e64fd8df22e","tests/ifcfiles/public/C20-Institute-Var-2.ifc":"288a7ba65b562e1bdd6548f60f0e2339306578625cb445670f0bbf1c8a4560c3","tests/ifcfiles/public/FM_ARC_DigitalHub.ifc":"84426eba85d565dc882022c5a41f17e82bc96d859ed9ca906fbf42b4461f28c6","tests/ifcfiles/public/ISSUE_005_haus.ifc":"6bf7a4d4e18776a6952914fd03acd96f81623ca5da6e5a7876fe4e64fd8df22e","tests/ifcfiles/public/ISSUE_021_Mini Project.ifc":"b5076f409b371d847cb453a9ab9a8d9de9d1c0eb27748ba7adc65571e0555a37","tests/ifcfiles/public/ISSUE_034_HouseZ.ifc":"ef512e88e26c916849445d3b741dd7979ee8a0dbf79ca788502092f84ea8f92b","tests/ifcfiles/public/ISSUE_044_test_IFCCOMPOSITEPROFILEDEF.ifc":"1b10710a3237960ed421ed8069bfd7ba2fa42cf15a52ef2139c8b3ed887e9b85","tests/ifcfiles/public/ISSUE_053_20181220Holter_Tower_10.ifc":"5a7a3711e8841135cc86bf92ba0ae9acc7e97b7a069927a64678c17653d3e609","tests/ifcfiles/public/ISSUE_068_ARK_NUS_skolebygg.ifc":"cd3081a53bd5667173caf391b17c5b1878fef4bfc252ef8330556e39afbd7c0d","tests/ifcfiles/public/ISSUE_102_M3D-CON-CD.ifc":"6e9612de56c08323e96b21e8f4097e42964bdf93066d460ddb0e7c184abdd15b","tests/ifcfiles/public/ISSUE_102_M3D-CON.ifc":"c83a0f31c6a52928fb9797456689c69c63d156c5bdf53cb49a83ed10d9e5bc23","tests/ifcfiles/public/ISSUE_126_model.ifc":"78e336de53ec658f2cd8fb51d5e2797ae1b5832cdadcdc22e7d0330a275a9545","tests/ifcfiles/public/ISSUE_129_N1540_17_EXE_MOD_448200_02_09_11SMC_IGC_V17.ifc":"a36fc248e0a8012045cd11f54ad0e9ed9fd7d7e7729a100599742fa8b06b4971","tests/ifcfiles/public/ISSUE_159_kleine_Wohnung_R22.ifc":"54d99140a79b693ebfbf92cab677e8579ad9b1a07f0654613b0bd519c4a8eafb","tests/ifcfiles/public/ISSUE_171_IfcSurfaceCurveSweptAreaSolid.ifc":"f2db8111ba05365cc54a6bcf8aade27764acfb615889f9302088aa1f1aa57c50","tests/ifcfiles/public/IfcOpenHouse_IFC4.ifc":"a9d854a4e47646bdc613aeca3c385474e851ac22b98bb8b7d783f7fee2f42c60","tests/ifcfiles/public/KIT-Simple-Road-Test-Web-IFC4x3_RC2.ifc":"3d8707ee9d70e2aeb67b8bebdc96016a1eceb30afd9a25e9cf6c258d978a6fe6","tests/ifcfiles/public/Office_A_20110811.ifc":"347c5e27081f0e29460124352781054ad2e00c0c09316b20f1981ae4e86a22ee","tests/ifcfiles/public/S_Office_Integrated Design Archi.ifc":"62cb878a60d880d989797a00f0faacdf5ad01a7f9ee1131e6804977366fb509a","tests/ifcfiles/public/Sample_entities.ifc":"270a55940a77ebb4f245cabf8c7429dc8fc7044baf9c9434fc747842f602d792","tests/ifcfiles/public/advanced_model.ifc":"c3247ec09d381688bb901d3bd85ae51a01dba70105cd9274b8cfd194ba94da12","tests/ifcfiles/public/dental_clinic.ifc":"757f4eccbbf9cd59806bee813e902cb13b6ace71bac106e4bce109764a79fbb2","tests/ifcfiles/public/duplex.ifc":"1da1e60682e4be608affcc697e79d24b76b984d7cc9cd465c3b97499a3a681ed","tests/ifcfiles/public/example.ifc":"4b9ab3d47e0b8e82fe2cb9a69ef452192204891528a291e989ccbc02aae27391","tests/ifcfiles/public/ifcbridge-model01.ifc":"00c016be5509e4145b3662dbdeedd67e7e60e6648ad1c247d6a41e41657b7e33","tests/ifcfiles/public/schependomlaan.ifc":"a0c7e4f336323a772f2dde464ed8919c4c5d3b3d4b62342129a4da73f5526880","tests/ifcfiles/public/tested_sample_project.ifc":"c4bf6c87a2e09795f0d854dc2c6eeb92829790c565ff86b1e03007648729bb26"}

0 commit comments

Comments
 (0)