@@ -1227,7 +1227,7 @@ namespace bimGeometry
12271227 return geom;
12281228 }
12291229
1230- inline Geometry SectionedSurface (std::vector<std::vector<glm::dvec3>> profiles)
1230+ inline Geometry SectionedSurface (std::vector<std::vector<glm::dvec3>> profiles, bool buildCaps, double eps= 0.0 )
12311231 {
12321232 Geometry geom;
12331233
@@ -1275,12 +1275,94 @@ namespace bimGeometry
12751275 {
12761276 for (size_t j = 0 ; j < indices.size () - 2 ; j += 4 )
12771277 {
1278- geom.AddFace (indices[j], indices[j + 1 ], indices[j + 2 ], - 1 );
1279- geom.AddFace (indices[j + 2 ], indices[j + 1 ], indices[j + 3 ], - 1 );
1278+ geom.AddFace (indices[j], indices[j + 1 ], indices[j + 2 ], UINT32_MAX );
1279+ geom.AddFace (indices[j + 2 ], indices[j + 1 ], indices[j + 3 ], UINT32_MAX );
12801280 }
12811281 }
12821282 }
12831283
1284+ if (buildCaps && profiles.size () >= 2 )
1285+ {
1286+ // Process the first and last profiles for caps
1287+ for (size_t capIdx = 0 ; capIdx < profiles.size (); capIdx += profiles.size () - 1 )
1288+ {
1289+ std::vector<glm::dvec3>& profile = profiles[capIdx];
1290+ if (profile.size () < 3 ) // Need at least 3 points for a polygon
1291+ {
1292+ continue ;
1293+ }
1294+
1295+ // Convert profile to std::vector<Point> for bestProjection
1296+ std::vector<Point> poly3D;
1297+ for (const auto & p : profile)
1298+ {
1299+ poly3D.push_back ({ p.x , p.y , p.z });
1300+ }
1301+
1302+ // Determine the best projection plane
1303+ Projection proj = bestProjection (poly3D);
1304+
1305+ // Convert profile to std::vector<std::vector<Point>> for projectTo2D
1306+ std::vector<std::vector<Point>> poly3DVec = { poly3D };
1307+
1308+ // Project to 2D
1309+ std::vector<std::vector<Point>> poly2D = projectTo2D (poly3DVec, proj);
1310+
1311+ // Convert to std::vector<std::vector<std::array<double, 2>>> for earcut
1312+ std::vector<std::vector<std::array<double , 2 >>> polygon (1 );
1313+ for (const auto & pt : poly2D[0 ])
1314+ {
1315+ polygon[0 ].push_back ({ pt[0 ], pt[1 ] });
1316+ }
1317+
1318+ // Run earcut triangulation (assuming mapbox/earcut.hpp is included)
1319+ std::vector<uint32_t > capIndices = mapbox::earcut<uint32_t >(polygon);
1320+
1321+ // Compute average normal for the cap
1322+ glm::dvec3 avgNormal (0.0 );
1323+ for (size_t i = 0 ; i < profile.size (); ++i)
1324+ {
1325+ glm::dvec3 p1 = profile[i];
1326+ glm::dvec3 p2 = profile[(i + 1 ) % profile.size ()];
1327+ glm::dvec3 edge = p2 - p1;
1328+ glm::dvec3 crossVec = glm::cross (edge, glm::dvec3 (0.0 , 0.0 , 1.0 ));
1329+ if (glm::length (crossVec) > eps)
1330+ {
1331+ avgNormal += glm::normalize (crossVec);
1332+ }
1333+ }
1334+ if (glm::length (avgNormal) < eps)
1335+ {
1336+ avgNormal = glm::dvec3 (0.0 , 0.0 , 1.0 );
1337+ }
1338+ else
1339+ {
1340+ avgNormal = glm::normalize (avgNormal);
1341+ }
1342+
1343+ // Add cap points to geometry with appropriate normal
1344+ uint32_t baseIndex = geom.numPoints ;
1345+ for (const auto & p : profile)
1346+ {
1347+ // Use reversed normal for first cap (outward facing)
1348+ glm::dvec3 normal = (capIdx == 0 ) ? -avgNormal : avgNormal;
1349+ geom.AddPoint (p, normal);
1350+ }
1351+
1352+ // Add triangular faces for the cap
1353+ for (size_t i = 0 ; i < capIndices.size (); i += 3 )
1354+ {
1355+ uint32_t i0 = baseIndex + capIndices[i];
1356+ uint32_t i1 = baseIndex + capIndices[i + 1 ];
1357+ uint32_t i2 = baseIndex + capIndices[i + 2 ];
1358+ // Reverse indices for first cap to ensure correct winding
1359+ if (capIdx == 0 )
1360+ geom.AddFace (i0, i2, i1, UINT32_MAX);
1361+ else
1362+ geom.AddFace (i0, i1, i2, UINT32_MAX);
1363+ }
1364+ }
1365+ }
12841366 return geom;
12851367 }
12861368
0 commit comments