Repository navigation
|
Hey all! I'd appreciate advice on the most suitable native MeshLib construction for this problem. I'll go over what is needed as well as the downstream uses of it to explain its constraints. I start with an oriented triangle mesh and an ordered, closed 3D curve on its surface. After cutting out the selected region, I have a protected surface patch F whose boundary is C. I need to extend C outward into a configurable border B, ending at a new outer rim R. F and C must retain their geometry. The result F+B is initially an open surface with R as its boundary. Border profile Relative to a chosen construction axis d, the local profile has: H — horizontal/transverse width: perpendicular to d, outward from the patch. Optionally, an initial axial drop b before the horizontal segment, while retaining the original attachment at C. (This is not mandatory)
The problem The boundary can be nonplanar and have concave turns, hairpins, narrow necks and nearby branches. It can also cross itself in projection along the chosen construction axis d while remaining separated in 3D (an overpass, not necessarily a true curve intersection.) Flattening it or welding spatially close branches loses information. I'm not requesting an impossible constant-width, intersection-free offset for every curve. Local intersections between generated border triangles can be acceptable if their extent is small, the protected surface remains faithful, and downstream operations work. Broad folds, branch swaps, cracks and substantial unexplained width loss are not acceptable. When dimensions conflict with local clearance or curvature, I would like a principled, measurable compromise rather than a silent change of shape.
We currently use offsetContours, source mapping, constrained makeSpline smoothing, and guided MeshLib bridge/fill operations. Full H/A/V strips work well on ordinary boundaries; intermediate rows are retained where needed to preserve shape. The remaining challenge is matching points between generated curves without distorting the border:
Is there a native workflow for coordinating these curves and their samples? The attachment C must remain fixed, and generated curves should retain the intended width, angle, height and smooth rim. Local transition fans and small, measured curve adjustments are acceptable. The 3D contour offset operates in XY before restoring height. How should we handle overpasses... branches that cross in this flat view but remain separated in 3D... without accidentally joining them? Lastly, we need this outer rim for two uses:
A small example or recommended sequence of native operations would be extremely helpful. Thank you! |
Replies: 6 comments 5 replies
|
|
|
MRMESH_API EdgeId extendHole( Mesh& mesh, EdgeId a, std::function<Vector3f(const Vector3f&)> getVertPos,
FaceBitSet* outNewFaces = nullptr );in For the direction: precompute one outward vector per boundary vertex from the loop tangent |
|
The matching problem disappears if the curves are never created independently. Build the border row by row as mesh topology first, and assign positions afterwards: every row then has exactly the vertices of C, the correspondence is the mesh itself, and Recipe (all native, the attached script does it in ~100 lines):
Downstream
Results (profile A from your sketch plus Hairpin, legs 1.1 apart, reach
Helix strip, 1.5 turns, pitch 3.0 along d:
Script: border6887.py (needs |
|
The failure you describe is structural, not a matter of tuning the lift, and I would stop deforming the fitting surface altogether. Two things are going on:
Formulate the constraint as a set operation instead. "At least t from the anatomy" means and that is a plain
Everything you were validating by hand comes out of the construction: the boundary is R to 1e-8, clearance ≥ t up to the voxel error, no self-intersections, the anatomy-fitting crossings are gone (a crossing is just a negative deficit), and D is bit-identical wherever it already had clearance - 65-74 % of its vertices in the tests, the rest replaced by the offset surface with the boolean's own seam. No transition band, no LP, no candidate screening; both tests below run in 7 s together. If the crease where D meets the offset must be soft, subdivide around the intersection contour and Tests (pip Undercut tooth of revolution on a plate, fitting shell at 1 mm where 3 mm is required, rim = plate boundary fixed:
Dipped design shell over the #6885 anatomy (moderate slope, 38°, dip 4.5 mm reaching 1.5 mm into F): here the axial lift does work (0 crossings, 7 flips), the union gives 0 crossings, 65 % of D untouched, and the 11 self-intersecting pairs it reports are the seam slivers of the input guide itself.
On your steps 1-2: with the union the fitting surface's boundary is R, so nothing has to be resampled or decimated to a matching count before Scripts: |
|
Good to hear the union holds on real data; 1. The lift The native solver for "smooth displacement over a region, handles fixed, zero value and zero slope at the edge, relief kept" is Laplacian lap( mesh );
lap.init( freeVerts, EdgeWeights::Cotan, VertexMass::Unit, RememberShape::Yes );
for ( auto v : handles ) lap.fixVertex( v, mesh.points[v] + s[v] * d );
lap.apply();With The fossae are not a solver problem. Depth is Occlusal table with four cusps and a central fossa 0.5 mm below E, margin 0.03, 6 blend rings:
2. Cut line at a fixed height above the rim Harmonic extension is one call: fix the rim vertices at their height in a 3. Expected. Subdivision stops when every edge in the region is at most Scripts: |











The matching problem disappears if the curves are never created independently. Build the border row by row as mesh topology first, and assign positions afterwards: every row then has exactly the vertices of C, the correspondence is the mesh itself, and
offsetContours- the only step in your pipeline that looks at the projection globally and can weld branches - is not used at all.Recipe (all native, the attached script does it in ~100 lines):
findRightBoundary( mesh.topology )gives the loop C with the hole to the left of every edge, vertices in order.makeDegenerateBandAroundHole( mesh, e, &newFaces ). It returns the edge of the new hole opposite toe, so