@@ -2249,6 +2249,101 @@ inline std::vector<Vec2> sampleCurveWireByU(const CurveWire& wire, int segmentsP
22492249 return samples;
22502250}
22512251
2252+ // / Ear-clipping of a simple parametric wire that clips the ear with the shortest 3D diagonal, so a trimmed strip is not fanned across.
2253+ inline std::vector<std::array<int , 3 >> triangulateWireByShortestChord (const SurfaceWire& wire,
2254+ const std::vector<Vec3>& points)
2255+ {
2256+ std::vector<int > remainingIndices;
2257+ remainingIndices.reserve (wire.vertices .size ());
2258+ if (wire.signedArea () >= 0 .) {
2259+ for (size_t vertexIndex = 0 ; vertexIndex < wire.vertices .size (); ++vertexIndex) {
2260+ remainingIndices.push_back (static_cast <int >(vertexIndex));
2261+ }
2262+ } else {
2263+ for (size_t reverseIndex = wire.vertices .size (); reverseIndex > 0 ; --reverseIndex) {
2264+ remainingIndices.push_back (static_cast <int >(reverseIndex - 1 ));
2265+ }
2266+ }
2267+
2268+ // the 3D diagonal of the ear at a position, or infinity when it is not an ear (same tests as triangulateSimpleWire)
2269+ const double notAnEar = std::numeric_limits<double >::infinity ();
2270+ std::vector<char > blocked (remainingIndices.size (), 0 ); // convex, but another vertex lies in its triangle
2271+ auto earChord = [&](size_t indexPosition) {
2272+ blocked[indexPosition] = 0 ;
2273+ const int previousIndex = remainingIndices[(indexPosition + remainingIndices.size () - 1 ) % remainingIndices.size ()];
2274+ const int currentIndex = remainingIndices[indexPosition];
2275+ const int nextIndex = remainingIndices[(indexPosition + 1 ) % remainingIndices.size ()];
2276+ const auto & previousVertex = wire.vertices [previousIndex];
2277+ const auto & currentVertex = wire.vertices [currentIndex];
2278+ const auto & nextVertex = wire.vertices [nextIndex];
2279+ if (cross2D (currentVertex - previousVertex, nextVertex - currentVertex) <= kTolerance ) {
2280+ return notAnEar;
2281+ }
2282+ for (int candidateIndex : remainingIndices) {
2283+ if (candidateIndex == previousIndex || candidateIndex == currentIndex || candidateIndex == nextIndex) {
2284+ continue ;
2285+ }
2286+ if (pointInTriangle (wire.vertices [candidateIndex], previousVertex, currentVertex, nextVertex)) {
2287+ blocked[indexPosition] = 1 ;
2288+ return notAnEar;
2289+ }
2290+ }
2291+ return norm (points[nextIndex] - points[previousIndex]);
2292+ };
2293+
2294+ std::vector<double > chords (remainingIndices.size ());
2295+ for (size_t indexPosition = 0 ; indexPosition < remainingIndices.size (); ++indexPosition) {
2296+ chords[indexPosition] = earChord (indexPosition);
2297+ }
2298+
2299+ std::vector<std::array<int , 3 >> triangles;
2300+ while (remainingIndices.size () > 3 ) {
2301+ // the shortest diagonal wins; ties go to the lowest position
2302+ size_t bestPosition = remainingIndices.size ();
2303+ double bestChord = notAnEar;
2304+ for (size_t indexPosition = 0 ; indexPosition < remainingIndices.size (); ++indexPosition) {
2305+ if (chords[indexPosition] < bestChord) {
2306+ bestChord = chords[indexPosition];
2307+ bestPosition = indexPosition;
2308+ }
2309+ }
2310+ if (bestPosition == remainingIndices.size ()) {
2311+ break ;
2312+ }
2313+
2314+ const size_t count = remainingIndices.size ();
2315+ const int previousIndex = remainingIndices[(bestPosition + count - 1 ) % count];
2316+ const int clippedIndex = remainingIndices[bestPosition];
2317+ const int nextIndex = remainingIndices[(bestPosition + 1 ) % count];
2318+ triangles.push_back ({previousIndex, clippedIndex, nextIndex});
2319+ remainingIndices.erase (remainingIndices.begin () + bestPosition);
2320+ chords.erase (chords.begin () + bestPosition);
2321+ blocked.erase (blocked.begin () + bestPosition);
2322+
2323+ // the two neighbours change shape; any other non-ear can only change if the clipped vertex was what blocked it
2324+ const size_t newCount = remainingIndices.size ();
2325+ const size_t beforePosition = (bestPosition + newCount - 1 ) % newCount;
2326+ const size_t afterPosition = bestPosition % newCount;
2327+ for (size_t indexPosition = 0 ; indexPosition < newCount; ++indexPosition) {
2328+ if (indexPosition == beforePosition || indexPosition == afterPosition) {
2329+ chords[indexPosition] = earChord (indexPosition);
2330+ } else if (blocked[indexPosition]) {
2331+ const auto & previousVertex = wire.vertices [remainingIndices[(indexPosition + newCount - 1 ) % newCount]];
2332+ const auto & currentVertex = wire.vertices [remainingIndices[indexPosition]];
2333+ const auto & nextVertex = wire.vertices [remainingIndices[(indexPosition + 1 ) % newCount]];
2334+ if (pointInTriangle (wire.vertices [clippedIndex], previousVertex, currentVertex, nextVertex)) {
2335+ chords[indexPosition] = earChord (indexPosition);
2336+ }
2337+ }
2338+ }
2339+ }
2340+
2341+ if (remainingIndices.size () == 3 ) {
2342+ triangles.push_back ({remainingIndices[0 ], remainingIndices[1 ], remainingIndices[2 ]});
2343+ }
2344+ return triangles;
2345+ }
2346+
22522347// / Append the display triangulation of a wire-trimmed quadric patch: the sampled outer loop, ear-clipped; holes are omitted.
22532348template <typename MapUV>
22542349void appendCurveTrimMesh (const CurveWire& outerWire, const MapUV& mapUV, std::vector<Vec3>& vertices,
@@ -2260,10 +2355,13 @@ void appendCurveTrimMesh(const CurveWire& outerWire, const MapUV& mapUV, std::ve
22602355 return ;
22612356 }
22622357 const int firstVertexIndex = static_cast <int >(vertices.size ());
2358+ std::vector<Vec3> points;
2359+ points.reserve (sampledWire.vertices .size ());
22632360 for (const auto & sample : sampledWire.vertices ) {
2264- vertices .push_back (mapUV (sample.uCoord , sample.vCoord ));
2361+ points .push_back (mapUV (sample.uCoord , sample.vCoord ));
22652362 }
2266- for (const auto & triangle : triangulateSimpleWire (sampledWire)) {
2363+ vertices.insert (vertices.end (), points.begin (), points.end ());
2364+ for (const auto & triangle : triangulateWireByShortestChord (sampledWire, points)) {
22672365 triangles.push_back (
22682366 {firstVertexIndex + triangle[0 ], firstVertexIndex + triangle[1 ], firstVertexIndex + triangle[2 ]});
22692367 }
0 commit comments