Skip to content

Releases: Krateian/Sutura

Sutura v0.9.1

Choose a tag to compare

@Krateian Krateian released this 12 Oct 10:54
dcddaed

Sutura v0.9.1

A small maintenance release. It removes two sources of run-to-run variation from the deterministic tiers, eliminates a geometry step that could make the shell analysis crawl or hang, and trims dependencies.

Fewer sources of run-to-run variation

Before this release, two things could change a result without you changing anything:

  • Graft (the shell-wrap tier that closes severe damage) picked its resolution partly from how much free RAM the machine had, so the same model could come out slightly different on a busy machine or after other work had claimed memory. Graft now chooses its grid only from the input and the intensity preset, and if that grid does not fit it falls back once to the smaller "Balanced" grid and says so, instead of quietly shrinking the model or refusing.
  • The fTetWild last-resort tier decided whether to try a second pass based on how long the first pass had taken. That decision is now made only from whether the first result was good enough, not from the clock.

The practical effect: Graft's result no longer depends on how much free RAM the machine has or on CPU speed and load, and the fTetWild retry decision no longer depends on the clock.

Faster on a hard mesh

A step in the shell analysis that triangulates the mesh around self-intersections was not converging: each pass could roughly double the number of triangles and keep going. On the reference hard mesh it could take several seconds and on a denser one it could grow toward hundreds of millions of triangles and effectively hang. It now finishes the same geometry directly.

On the integration 8-mesh smoke set, total CLI repair time fell from 171.7 s to 146.2 s:

  • the reference hard mesh (thingi10k_100045): 7.49 s → 1.44 s end to end;
  • thingi10k_40886: 62.4 s → 37.4 s.

Interleaved re-measurement confirmed no regression on the other meshes, with medians improving as well: artec_bovine-heart 6.88 s → 5.57 s and thingi10k_46012 37.1 s → 35.5 s.

Fewer dependencies

psutil and pyrobust-predicates are no longer needed, making the installation lighter. In addition, an optional set of native Rust replacements exists behind SUTURA_NATIVE_* environment flags, off by default.

What you will see in the GUI

The repair log now states plainly when Graft used the smaller "Balanced" grid, when it was skipped for low memory, and when Graft or fTetWild was stopped for time — in the last two cases the earlier stage result is kept, so nothing is lost.

Smaller change

Tamponade's final safety check is now much cheaper to run (it measures only when it might need to, about 0.84 s → 0.14 s on one reference mesh) with no change to the output.

Known limitations

fTetWild (the last-resort tier) is still not byte-reproducible between runs.

Sutura v0.9.0

Choose a tag to compare

@Krateian Krateian released this 11 Oct 16:29
40dce1e

Sutura v0.9.0

Sutura v0.9.0 adds a way to seal holes from the inside, makes the default hole
filler try harder before it gives up, and quietly trims two dependencies.
Tamponade and its Stent candidate are on by default, following the standing rule
that improvements are opt-out: turn them off with --no-tamponade (env
SUTURA_TAMPONADE=0) or the GUI checkbox. Flap also now tries harder — a
boundary loop it used to give up on tries alternative fills, one at a time. A
loop Flap already filled is byte-identical and costs nothing extra.

What is new

  • Tamponade — a "fill from the inside" plug (repair method #17).
    Right after the Stage 1 clean-up, before Flap and the generic hole closing,
    Tamponade fills each open hole from inside, like lava (holes it skips get a
    second chance after Flap, before the deep Graft/fTetWild tiers): the plug is sealed at the narrowest point
    right at the shell, and the surrounding healthy surface is never touched (the
    original triangles are kept exactly as they were). Trapped folds inside the
    part are melted out one thin layer at a time, and any layer that would make
    things worse is undone. It runs by default; turn it off with --no-tamponade
    (env SUTURA_TAMPONADE=0) or the GUI checkbox Tamponade (fill holes from
    inside)
    .

  • Stent, and multi-Stent. Inside Tamponade, if a loop's normal plug is
    rejected, Sutura now inflates a plug inward from the rim instead — called a
    Stent. It grows from inside the part and freezes the moment it touches the
    shell, so it can never push outside. A large hole that one Stent cannot seal is
    split into several sub-loops, one Stent per piece, sharing a seam so the result
    stays a single connected solid. Stent has no separate switch; it runs only when
    Tamponade is on. If you want Tamponade without it, set SUTURA_TAMP_STENT=0.
    (This is not the same as method #8 Balloon, which rebuilds a whole surface with
    Poisson reconstruction.)

  • Clear, strict acceptance rules for every plug. A Tamponade plug is kept
    only if it: does not leak outside the part beyond a tiny tolerance; adds no new
    holes or bad edges when the file is re-opened from disk; does not lay an
    opposite-facing double wall over more than 20% of its area; stays connected to
    the main shell and adds no separate part; and keeps at least 99% of the
    pre-tier faces byte-for-byte (the input faces carried through verbatim).
    Self-intersections are reported only — a slicer unions overlapping volumes, so
    they never block a seal.

  • Graft refuses a result that covers too little of the input. A Graft
    candidate that misses too much of the input surface is now refused, and the
    repair falls through to the next deep tier instead of adopting it.

  • A final safety net for Tamponade. If an adopted Tamponade result collapses
    or leaks, the repair is re-run without Tamponade, so a bad plug can never make
    the output worse.

  • Flap tries harder before giving up (on by default). Each boundary loop
    still gets the same patch as before first, so a loop that works today is
    unchanged and costs nothing extra. Only when that patch is rejected does the
    loop now try a few alternative fills, one at a time, keeping the first that
    passes the same safety checks. The extra tries are charged against Flap's
    existing time budget, so this cannot make a repair run past its limit. Opt out
    with SUTURA_FLAP_SELECT=0.

  • A memory/time guard on the Scaffold resample (method #10). On meshes with
    long, skinny triangle fans the resample filter could slow to a crawl and eat
    all the memory, killing the whole repair. It now runs in a child process under
    a time limit (20 s, SUTURA_PROXY_RESAMPLE_TIMEOUT) and a memory cap (2 GB,
    SUTURA_PROXY_RESAMPLE_MAX_RSS_MB), and falls through to the existing fallback
    as soon as either is reached. Output is unchanged whenever the resample
    finishes in time.

  • Smaller install, faster clean-up. Phase 1 removes two dependencies
    (psutil and pyrobust-predicates, replaced by the standard library and the
    Rust core), saving about 2.1 MB. Phase 2a/2b move more of the Stage 1
    mesh-cleaning work (duplicate/loose faces and vertices, vertex welding,
    connected-component removal, face flipping) onto the Rust kernel, each with a
    pure-NumPy fallback that produces bit-for-bit identical results.

Screenshots

Options → Repair: Tamponade and Flap on by default

Before/after view

Under the hood

  • Native self-intersection detection (off by default). The self-intersection
    count can now come from Sutura's own Rust kernel instead of PyMeshLab. It is
    behind SUTURA_NATIVE_SI=1 and off by default because the count is not
    byte-identical to PyMeshLab's; it is an evaluation switch with no CLI flag or
    GUI control, so the CLI and the GUI always behave the same.

  • The README's license-rationale paragraph was moved into the ## License
    section (text unchanged).

Notes

  • The Rust extension (sutura_geom) must provide sutura_geom.tamponade for
    method #17; there is no NumPy fallback, and the method reports itself
    unavailable when the extension lacks it.
  • Every user-facing entry point (CLI, GUI, file-manager menu, OrcaSlicer
    plugin) runs Tamponade (and its Stent candidate) by default; the Flap
    per-loop selection is likewise on by default. Disable Tamponade with
    --no-tamponade / SUTURA_TAMPONADE=0 / the GUI checkbox. Every other new
    tier is opt-in or adopted only when it is not worse.

Known limitations

  • On most real files the later repair stages already close tiny holes, so Stent
    mostly matters inside Tamponade.
  • Graft output can vary slightly between runs depending on how much free RAM is
    available (the shape stays valid); a fix is planned for 0.9.1.

v0.8.1

Choose a tag to compare

@Krateian Krateian released this 03 Oct 14:34
f983361

Sutura v0.8.1 — Faster topology, cleaner solids

The short version: Sutura v0.8.1 speeds up mesh repair by moving repetitive triangle and vertex welding into a native Rust kernel, shaving 4% to 29% off repair times while producing bit-for-bit identical geometry for the accelerated operations. A post-union cleanup step also removes microscopic scrap slivers from solid rebuilds, so single-body models open as exactly one part in the slicer without disturbing genuine multi-part assemblies.

What changed, in plain words

Faster core topology (Rust kernel). During repair, Sutura constantly welds vertices, checks for duplicate triangles, and counts shared edges. These operations now run in a native Rust kernel (sutura_geom) instead of pure Python/numpy loops. It is completely automatic and requires no configuration. If the prebuilt Rust extension is not installed, Sutura falls back to the pure Python path transparently. The output of the accelerated operations is bit-for-bit identical to previous releases; the separate sliver cleanup described below is the only change in this release that alters geometry, and only on the two affected models.

Cleaner solid output: post-union micro-sliver cleanup. In Stage 2, when dozens or hundreds of overlapping shells are merged into a solid using boolean operations, the intersection cuts can occasionally cleave off detached, microscopic slivers (often just 6 triangles with virtually zero volume). Previously, these stray slivers escaped into the output file, causing slicers like OrcaSlicer to report "2 parts" or "3 parts" on what should be a single print. A second debris filter now inspects the components immediately after the boolean union and prunes isolated micro-slivers with zero printable volume ($V \le 0.001\text{ mm}^3$ or paper-thin thickness). The largest body is unconditionally protected, and genuine multi-solid assemblies are never touched.

Defect-report fix for non-manifold face indices. The defect scanner's non-manifold region detector had a half-edge mapping defect where block-stacked edges were mapped with $h // 3$ instead of $h \pmod F$. This could cause the reported list of non-manifold face indices (faces_idx) to attribute bad edges to the wrong neighbouring triangles in the JSON report and the GUI heatmap. The fix aligns the face mapping so reported defect regions point precisely to the culprit faces. Measured effect on the 41-model corpus: the correction changes the defects report on 8 models, 7 of which also show the corrected counts in the flap/p_weld diagnostic metadata; no repair decision, output geometry, watertight verdict, part count or self-intersection count changes.

Documentation and Turkish naming. The test suite count in the documentation has been updated to reflect the full test suite (63 Python test modules and 92 Rust unit tests). In the Turkish documentation, GUI options, and CLI hints, Dressing (method #16) is now consistently referred to as "Pansuman (Dressing)" to clarify its medical metaphor as a protective, contour-wrapping bandage rather than clothing.


What we measured

  • Faster wall-clock repairs: In interleaved, alternating benchmark runs across 5 representative models, the Rust topology kernel delivered measurable end-to-end speedups:
    • thingi10k_1038441 (dense self-intersections): 14.79s → 10.45s (+29.3% faster)
    • artec_copper-key (smooth curved scan): 3.93s → 3.48s (+11.5% faster)
    • thingi10k_100281 (heavy self-intersections): 3.77s → 3.34s (+11.4% faster)
    • thingi10k_224108 (closed shell, Flap + P-HONEST): 5.06s → 4.62s (+8.7% faster)
    • thingi10k_145065 (non-manifold vertices): 2.64s → 2.54s (+4.0% faster)
  • Bit-for-bit geometric identity (topology-kernel benchmark): Across the 41-model regression corpus, 41 out of 41 models exit 0 and achieve category: watertight. 40 out of 41 produce bit-for-bit identical SHA256 file hashes between the Python oracle and the Rust topology kernel. The single differing mesh (thingi10k_100827) triggered the external fTetWild engine, which is known to produce nondeterministic tetrahedralizations across runs. The sliver cleanup below separately changes the geometry of the two models it repairs.
  • Micro-sliver removal:
    • On an ornate frame scan (~400k triangles), OrcaSlicer previously reported 2 parts due to a detached 6-face sliver ($V \approx 2.15 \times 10^{-5}\text{ mm}^3$). With post-union filtering, the model opens cleanly as 1 part, manifold, 0 open edges, with watertightness intact and exact self-intersections unchanged at 3005.
    • On thingi10k_1038441, the slicer part count dropped from 3 to 1 and the manifold3d shell count from 11 to 1 (10 boolean micro-slivers pruned), while exact self-intersections fell from 283 to 268.
    • Zero false positives on multi-solid assemblies: Legitimate multi-part models remain untouched (thingi10k_1038439 preserves all 3 bodies, thingi10k_228302 preserves all 8 solid cubes).

Defaults at a glance

Feature Default Switch / Option
Topology kernel Rust (sutura_geom) SUTURA_TOPOLOGY=python (forces Python oracle)
Post-union micro-sliver cleanup on Automatic inside Stage 2 solid rebuild
Flap surface hole fill on --no-flap, SUTURA_FLAP=0
Self-intersections reported, not a failure --si-mode repair / off
Dressing off, suggested only --experimental-dressing

Assets

  • Sutura-x86_64.AppImage: Linux
  • Sutura-v0.8.1.dmg: macOS (unsigned; first launch: right-click → Open)
  • sutura_geom-*.whl + sutura_geom-SHA256SUMS: prebuilt geometry extension including the new topology kernel

Full technical details: CHANGELOG.md


Sutura v0.8.1 — Daha hızlı topoloji, daha temiz katılar

Kısaca: Sutura v0.8.1, sık tekrarlanan üçgen ve köşe kaynatma işlemlerini yerel bir Rust çekirdeğine taşıyarak onarım sürelerini %4 ila %29 oranında hızlandırıyor; hızlandırılan işlemlerde çıktı önceki sürümlerle bayt bayt aynı kalıyor. Katı oluşturma sonrasına eklenen temizlik adımı ise dilimleyicilerde tek parça olması gereken modellerin fazladan parça gibi görünmesine yol açan mikroskobik kıymıkları temizliyor ve gerçek çok parçalı montajlara hiç dokunmuyor.

Neler değişti, sade anlatım

Daha hızlı çekirdek topoloji (Rust motoru). Sutura onarım boyunca sürekli köşe noktalarını birleştirir, mükerrer üçgenleri eler ve ortak kenarları sayar. Bu işlemler artık saf Python/numpy döngüleri yerine doğrudan yerel Rust çekirdeğinde (sutura_geom) yürütülüyor. Tamamen otomatik çalışır; herhangi bir ayar yapman gerekmez. Derlenmiş Rust eklentisi bulunmayan ortamlarda Sutura sessizce saf Python yoluna geçer. Hızlandırılan işlemlerin çıktısı önceki sürümlerle bayt bayt aynıdır; bu sürümde geometriyi değiştiren tek adım, aşağıda açıklanan ve yalnızca iki modeli etkileyen kıymık temizliğidir.

Daha temiz katı model: birleşim sonrası mikroskobik kıymık temizliği. Aşama 2'de, onlarca veya yüzlerce kesişen kabuk boole işlemleriyle birleştirilirken, kesişim sınırları bazen neredeyse sıfır hacimli mikroskobik kıymıklar (genellikle 6 üçgenlik kopuk parçacıklar) koparabilir. Eskiden bu artık parçacıklar çıktı dosyasına sızıyor ve OrcaSlicer gibi dilimleyicilerin aslında tek parça olması gereken baskıyı "2 parça" veya "3 parça" olarak görmesine yol açıyordu. Boole birleşiminin hemen ardına eklenen ikinci bir çöp filtresi, bu hacimsiz ($V \le 0,001\text{ mm}^3$ veya kâğıt inceliğinde) artık kıymıkları temizliyor. En büyük ana gövde koşulsuz olarak korunur ve gerçek çok parçalı katı montajlara asla dokunulmaz.

Non-manifold yüzey indeksleri için hata düzeltmesi. Hata tarayıcısındaki manifold olmayan bölge tespit edicide bir kenar-yüz eşleme hatası bulundu: blok olarak dizilen kenarlar $h \pmod F$ yerine $h // 3$ ile eşleniyordu. Bu durum, JSON raporundaki ve arayüzdeki ısı haritasında hatalı kenarların yanlış komşu üçgenlere atfedilmesine yol açabiliyordu. Yapılan düzeltmeyle yüz eşlemesi düzeltilerek tespit edilen kusurlu bölgelerin doğrudan doğru üçgenleri göstermesi sağlandı. 41 modellik külliyatta ölçülen etki: düzeltme defects raporunu 8 modelde değiştirir ve bu modellerin 7'sinde düzeltilmiş sayılar flap/p_weld tanılama üstverisinde de görünür; hiçbir onarım kararı, çıktı geometrisi, su geçirmezlik hükmü, parça sayısı veya kendisiyle kesişme sayısı değişmez.

Dokümantasyon ve Türkçe terim tutarlılığı. Dokümanlardaki test sayıları güncellenerek tam test paketini (63 Python test modülü ve 92 Rust birim testi) yansıtacak şekilde netleştirildi. Türkçe dokümantasyonda, arayüz seçeneklerinde ve terminal ipuçlarında Dressing (yöntem #16), kıyafet çağrışımını önlemek ve yara bakımındaki koruyucu sargı işlevini tam yansıtmak amacıyla tutarlı olarak "Pansuman (Dressing)" olarak adlandırıldı.


Ölçtüklerimiz

  • Daha hızlı onarım süreleri: 5 temsilci model üzerinde dönüşümlü olarak yapılan hız ölçümlerinde, Rust topoloji çekirdeği uçtan uca belirgin hızlanmalar sağladı:
    • thingi10k_1038441 (yoğun kendisiyle kesişme): 14,79 sn → 10,45 sn (%29,3 daha hızlı)
    • artec_copper-key (yumuşak kavisli tarama): 3,93 sn → 3,48 sn (%11,5 daha hızlı)
    • thingi10k_100281 (ağır kesişmeler): 3,77 sn → 3,34 sn (%11,4 daha hızlı)
    • thingi10k_224108 (kapalı kabuk, Flap + P-HONEST): 5,06 sn → 4,62 sn (%8,7 daha hızlı)
    • thingi10k_145065 (manifold olmayan köşeler): 2,64 sn → 2,54 sn (%4,0 daha hızlı)
  • Bayt bayt geometrik eşitlik (topoloji çekirdeği ölçümü): 41 modellik test külliyatında 41 modelin tamamı hata kodu vermeden (rc=0) kapalı ve su geçirmez (watertight) çıktı. 41 modelin 40'ında SHA256 dosya özeti Python ve Rust motorları arasında bayt bayt aynı kaldı. Farklı çıkan tek model olan `th...
Read more

v0.8.0

Choose a tag to compare

@Krateian Krateian released this 03 Oct 10:44
ab5f405

Sutura v0.8.0 — Flap: holes filled the way the surface wants to go

The short version: Sutura now fills holes by continuing the surface around
them
instead of putting a flat lid on top. Curved scans get curved patches,
your original triangles stay where they were, and badly damaged files still fall
back to the proven repair steps. It is on by default; you do not need to change
anything.

What changed, in plain words

Flap: the new hole filler (on by default). A classic hole fill closes a hole
with a flat lid, which leaves dents and flat spots on curved surfaces. Flap works
more like brushing liquid primer or epoxy over a gap: the patch leaves the rim
with the same slope as the surface around it and flows smoothly across, so a hole
in a sphere gets a piece of sphere, not a coin. Only the patch is new; the
triangles you already had are not moved. If Flap cannot fill a hole safely it
hands it to the old filler, so a file is never worse with Flap than without it.
Turn it off with --no-flap or the Flap surface hole fill checkbox.

How a repair runs now:

your file ─► Flap ─► Stage 1 clean-up ─► (still broken?) Graft ─► Stage 2 solid ─► repaired copy
  • Flap fills holes along the surface.
  • Stage 1 (PyMeshLab) removes broken triangles, untangles bad edges and closes what is left.
  • Graft steps in only when the damage is serious: it builds a fresh shell over the
    damaged areas and grafts it onto the healthy original surface, like a skin graft, so
    engraving and ornament survive.
  • Stage 2 (manifold3d) turns the result into one closed, printable solid.

After every step Sutura re-counts holes and bad edges as if the file had been
re-opened from disk and throws away any step that made things worse. Your
original file is never changed.

What we measured

  • On our 41-model test set (real scans and Thingi10K models), all 41 come out
    closed
    with Flap on by default. Flap stepped in on 13 of them and did not
    make a single one worse
    (no lost watertightness, no extra parts).
  • On one damaged scan (thingi10k_1038441) the largest change to the surface
    dropped from 4.11 mm to 0.60 mm, because the holes are now filled along the
    surface instead of being bridged.
  • On a private, heavily damaged ornamental frame scan (220 shells, thousands of
    open edges), OrcaSlicer now reports the repaired file clean: 0 open edges,
    manifold. Fine ornament is visually unchanged.
  • Flap's core runs in Rust: the Flap step for the whole 41-model set takes about
    7 seconds. If the Rust extension is missing, an identical Python version is
    used.
  • Huge scans no longer hang. Graft now has a size and time limit; six dense
    scans (2–6 million triangles) that used to run past 10 minutes now finish in
    3–6 minutes with an honest "too large, skipped" note in the report.

Also new

  • Self-intersection reporting (--si-mode). Small self-intersections that
    slicers ignore are now reported but no longer count as a failed repair
    (default report). --si-mode repair brings back the strict behaviour, off
    skips the check.
  • Dressing (method #16), opt-in. A "dip-coat" repair that always produces a
    closed model but can soften fine detail. It is off by default; when every
    other step fails, Sutura suggests it (CLI hint, GUI "⚠ Try Dressing" button,
    and now also in the OrcaSlicer plugin panel) and you decide.
  • Fewer leftover scraps. Paper-thin, zero-volume sheets are removed before the
    final solid is built, which cuts self-intersections and stray parts.
  • OrcaSlicer plugin ships the new engine and shows the Dressing suggestion.
  • Docs: README (English and Turkish) has a new plain-language "How Sutura
    repairs a mesh" section; the pipeline is now described as staged rather than
    "two-stage", since there are more than two steps.

Defaults at a glance

Feature Default Switch
Flap surface hole fill on --no-flap, SUTURA_FLAP=0
Self-intersections reported, not a failure --si-mode repair / off
Graft size/time limit by intensity preset Extreme = no face limit
Dressing off, suggested only --experimental-dressing

Assets

  • Sutura-x86_64.AppImage: Linux
  • Sutura-v0.8.0.dmg: macOS (unsigned; first launch: right-click → Open)
  • sutura_geom-*.whl + sutura_geom-SHA256SUMS: prebuilt geometry extension, picked up automatically by the installers

Full technical details: CHANGELOG.md


Sutura v0.8.0 — Flap: delikler yüzeyin gittiği yöne doğru dolduruluyor

Kısaca: Sutura artık delikleri üstüne düz bir kapak koyarak değil,
çevresindeki yüzeyi devam ettirerek dolduruyor. Kavisli taramalar kavisli yama
alıyor, orijinal üçgenlerin yerinde kalıyor ve çok bozuk dosyalar yine kanıtlanmış
onarım adımlarına düşüyor. Varsayılan açık; hiçbir şeyi değiştirmen gerekmiyor.

Neler değişti, sade anlatım

Flap: yeni delik doldurucu (varsayılan açık). Klasik delik doldurma deliği
düz bir kapakla kapatır; bu da kavisli yüzeylerde çukur ve düz lekeler bırakır.
Flap ise bir boşluğun üstüne sıvı astar ya da epoksi sürmek gibi çalışır: yama,
kenardan çevresindeki yüzeyle aynı eğimle çıkar ve deliğin üstünden yumuşakça
akar; böylece bir küredeki delik madeni para gibi düz bir kapak değil, kürenin bir
parçasını alır. Yalnızca yama yenidir; zaten var olan üçgenlerin yerinden
oynamaz. Flap bir deliği güvenle dolduramıyorsa onu eski doldurucuya bırakır;
yani bir dosya Flap ile asla Flap'siz halinden kötü olmaz. Kapatmak için
--no-flap ya da Flap yüzey delik doldurma kutusu.

Onarım artık şöyle ilerliyor:

dosyan ─► Flap ─► Aşama 1 temizlik ─► (hâlâ bozuk mu?) Graft ─► Aşama 2 katı ─► onarılmış kopya
  • Flap delikleri yüzeyi sürdürerek doldurur.
  • Aşama 1 (PyMeshLab) bozuk üçgenleri siler, hatalı kenarları çözer, kalanları kapatır.
  • Graft yalnızca hasar ciddiyse devreye girer: hasarlı bölgelerin üstüne yeni bir
    kabuk kurar ve bunu sağlam orijinal yüzeye deri nakli gibi diker; oyma ve süsleme
    kaybolmaz.
  • Aşama 2 (manifold3d) sonucu tek parça, kapalı, basılabilir bir katıya çevirir.

Sutura her adımdan sonra delikleri ve hatalı kenarları, dosya diskten yeniden
açılmış gibi tekrar sayar ve işleri kötüleştiren adımı çöpe atar. Orijinal
dosyan asla değişmez.

Ölçtüklerimiz

  • 41 modellik test setimizde (gerçek taramalar ve Thingi10K modelleri), Flap
    varsayılan açıkken 41 modelin hepsi kapalı çıkıyor. Flap 13'ünde devreye
    girdi ve hiçbirini kötüleştirmedi (su geçirmezlik kaybı yok, fazladan parça
    yok).
  • Hasarlı bir taramada (thingi10k_1038441) yüzeydeki en büyük değişiklik
    4,11 mm'den 0,60 mm'ye indi; çünkü delikler artık köprülenmek yerine yüzey
    boyunca dolduruluyor.
  • Ağır hasarlı, özel bir süslü çerçeve taramasında (220 kabuk, binlerce açık
    kenar) OrcaSlicer artık onarılmış dosyayı temiz görüyor: 0 açık kenar,
    manifold. İnce süslemelerde gözle görülür bir fark yok.
  • Flap'in çekirdeği Rust ile çalışıyor: 41 modelin tamamında Flap adımı yaklaşık
    7 saniye sürüyor. Rust eklentisi yoksa birebir aynı Python sürümü
    kullanılıyor.
  • Dev taramalar artık takılmıyor. Graft'a boyut ve süre sınırı geldi; eskiden
    10 dakikayı aşan altı yoğun tarama (2–6 milyon üçgen) artık 3–6 dakikada bitiyor
    ve raporda dürüst bir "çok büyük, atlandı" notu yer alıyor.

Diğer yenilikler

  • Kendisiyle kesişme raporlaması (--si-mode). Dilimleyicilerin görmezden
    geldiği küçük kesişmeler artık raporlanıyor ama onarımı başarısız saydırmıyor
    (varsayılan report). --si-mode repair katı davranışı geri getirir, off
    kontrolü atlar.
  • Pansuman (Dressing, yöntem #16), isteğe bağlı. Her zaman kapalı model
    üreten ama ince ayrıntıyı yumuşatabilen bir "daldırma kaplama" onarımı.
    Varsayılan kapalı; diğer tüm adımlar başarısız olursa Sutura onu önerir
    (CLI ipucu, arayüzde "⚠ Dressing ile dene" butonu ve artık OrcaSlicer eklenti
    panelinde de), karar senindir.
  • Daha az artık parça. Kâğıt inceliğinde, hacimsiz levhalar son katı
    kurulmadan önce temizleniyor; kesişmeler ve başıboş parçalar azalıyor.
  • OrcaSlicer eklentisi yeni motoru kullanıyor ve Pansuman önerisini gösteriyor.
  • Dokümanlar: README'ye (İngilizce ve Türkçe) sade bir "Sutura bir modeli
    nasıl onarır" bölümü eklendi; akış artık ikiden fazla adım olduğu için "iki
    aşamalı" değil "aşamalı" olarak anlatılıyor.

Varsayılanlar

Özellik Varsayılan Anahtar
Flap yüzey delik doldurma açık --no-flap, SUTURA_FLAP=0
Kendisiyle kesişme raporlanır, hata sayılmaz --si-mode repair / off
Graft boyut/süre sınırı yoğunluk ön ayarına göre Extreme = yüz sınırı yok
Pansuman (Dressing) kapalı, yalnızca önerilir --experimental-dressing

Dosyalar

  • Sutura-x86_64.AppImage: Linux
  • Sutura-v0.8.0.dmg: macOS (imzasız; ilk açılışta Sağ Tık → Aç)
  • sutura_geom-*.whl + sutura_geom-SHA256SUMS: yükleyicilerin otomatik kullandığı derlenmiş geometri eklentisi

Tüm teknik ayrıntılar: CHANGELOG.md

v0.7.2

Choose a tag to compare

@github-actions github-actions released this 02 Oct 04:02
bca2c2e

Sutura v0.7.2

Feature and performance release introducing the OrcaSlicer plugin v2 with an embedded dock panel for in-slicer mesh repair, alongside significantly faster rotational symmetry detection.

Added

  • OrcaSlicer plugin v2 with interactive dock panel. Directly inspect and repair plate meshes within OrcaSlicer using the newly introduced script-plugin API:
    • Dock panel UI: A dedicated dockable panel on Prepare/Preview tabs providing repair preset selection (Quick, Balanced, Thorough, Extreme; Balanced default), per-object manifold inspection, read-only defect breakdown (holes, non-manifold edges, self-intersections), and one-click Repair actions ("Repair selected" and "Select broken").
    • Proactive notifications: Non-manifold meshes and models with errors repaired on import trigger an OrcaSlicer notification offering a direct "Repair with Sutura" action.
    • World-coordinate geometry handling: Exports preserve the instance transformation matrix (proper triangle winding for mirrored parts), merge multi-part objects, and filter out parameter modifiers/blockers.
    • Non-destructive workflow: Repaired meshes load back as new objects with full undo support; outputs persist under .sutura_work/out/ (retained 7 days).
    • Plugin installation (requires OrcaSlicer nightly / >2.4.2; stable 2.4.x has no plugin system):
      # Ensure Sutura CLI is installed (~/.local/bin/sutura via ./install.sh or ./install-macos.sh)
      mkdir -p ~/.config/OrcaSlicer/orca_plugins/SuturaRepair  # macOS: ~/Library/Application Support/OrcaSlicer/orca_plugins/SuturaRepair
      cp orcaslicer-plugin/sutura_repair_linux_x86_64.py ~/.config/OrcaSlicer/orca_plugins/SuturaRepair/
      Enable and launch the plugin via OrcaSlicer's Plugins dialog.

Changed

  • Rotational symmetry detection is 2–3.7x faster. Vectorised and multithreaded nearest-neighbour (cKDTree) queries in detect_rotational_symmetry_free reduce corpus detection time from 59.5 s to 16.0 s while producing bit-identical results across all real-world and synthetic validation sets.
  • macOS GUI test environment. Test suites explicitly configure QT_PLUGIN_PATH to resolve PySide6 Qt6 plugins in macOS conda environments when Qt5 is co-installed.

Known limitations

  • OrcaSlicer version requirement: The script-plugin engine requires OrcaSlicer nightly builds or releases newer than 2.4.2; stable 2.4.x releases do not include plugin support.
  • Reload mesh placement: OrcaSlicer re-centres imported meshes (or places them directly aligned with the original coordinates). Press A (Arrange) on the plate to separate the repaired object, or Cmd/Ctrl+Z to undo the reload.

Assets

  • Sutura-x86_64.AppImage — Linux
  • Sutura-v0.7.2.dmg — macOS (unsigned; first launch needs right-click → Open)
  • sutura_geom-*.whl + sutura_geom-SHA256SUMS — prebuilt geometry extension wheels used automatically by installers

Full details: CHANGELOG.md

v0.7.1

Choose a tag to compare

@Krateian Krateian released this 01 Oct 02:41
b79792e

Sutura v0.7.1

Maintenance release for 0.7.0. macOS users: the 0.7.0 .dmg does not start — please use this release.

Fixed

  • macOS .app/.dmg failed to start in 0.7.0. PyInstaller bundled the repository-root sutura_engine source-checkout bootstrap instead of the real engine package, so the CLI and the GUI exited with ModuleNotFoundError. The build now packages the real engine, and CI runs the frozen CLI on every build (method list with Graft available, plus a real repair of a broken cube). Source/conda installs and the AppImage were not affected.
  • scipy pin restored to 1.16.x. scipy 1.18 requires Python ≥ 3.12 and broke the Python 3.11 stage-2 environment; Dependabot no longer proposes scipy ≥ 1.17.

Changed

  • Graft (#13) now works in installed builds. The sutura_geom Rust extension ships with every install path:
    • install.sh (both virtualenvs), install-macos.sh (conda env) and the self-updater install a prebuilt abi3 wheel (~0.7 MB; Linux x86_64/aarch64, macOS universal2) from this release, verified against sutura_geom-SHA256SUMS; without a matching wheel they build from rust/sutura-geom when a Rust toolchain is present, otherwise they continue without it (Graft listed unavailable, auto repair falls back to fTetWild).
    • The AppImage and the macOS .app/.dmg bundle the extension.
    • SUTURA_NO_GEOM=1 opts out, SUTURA_GEOM_FROM_SOURCE=1 forces a source build.
  • Release assets (AppImage, .dmg, wheels) are published only for tags.
  • Documentation and the About dialog now describe the product as the Sutura Triage Engine: the two-stage PyMeshLab + manifold3d core with 15 ranked repair methods.

Assets

  • Sutura-x86_64.AppImage — Linux
  • Sutura-v0.7.1.dmg — macOS (unsigned; first launch needs right-click → Open)
  • sutura_geom-*.whl + sutura_geom-SHA256SUMS — used automatically by the installers

Full details: CHANGELOG.md

v0.7.0

Choose a tag to compare

@Krateian Krateian released this 28 Sep 20:17
c79a899

Note: the macOS .dmg of this release does not start (packaging bug). Use v0.7.1 or later.

Breaking / renamed

  • --methods 13 / --methods graft now select Method #13 Graft (shell wrap).
    In v0.6.0, 13 and graft were aliases for manual repeated-element transplant;
    that method is now #12 transplant_plus (accessible via --methods 12 /
    --methods transplant_plus / --methods transplant+). Existing scripts using 13
    must be updated to 12 if manual transplant is intended.
  • Modular Package Structure (sutura_engine). Core geometric passes, triage,
    and diagnostics now reside under sutura_engine. Backward-compatibility shims
    are maintained in sutura/ so existing imports continue to function without disruption.

Added

  • Sutura Triage Engine Package Architecture (sutura_engine). Modular separation
    of concerns across seven dedicated subsystems:
    • sutura_engine.triage: Auto-escalation ladder, intensity presets (Quick, Balanced,
      Thorough, Extreme), and wall-clock fallback budgeting.
    • sutura_engine.diagnosis: Object defect metrics, component inspection, and extensible
      template matching (SCORE_TEMPLATES).
    • sutura_engine.chart: High-performance SHA-256 content-addressed cache under
      ~/.cache/sutura/ with 500 MB LRU eviction, invalidation on version bump, and
      --no-cache / sutura clear-cache controls.
    • sutura_engine.stitch: P-WELD reload-safe seam healing eliminating float32 vertex
      collapse without altering face topologies.
    • sutura_engine.xray: P-HONEST strict verification confirming meshes reload
      strictly watertight from disk before claiming success.
    • sutura_engine.hull: Exterior shell extraction and degenerate multi-component resolution.
    • sutura_engine.cast: Integration with the Rust sutura_geom core for exact predicates
      and accelerated morphology.
  • Extensible Plugin API (sutura_engine.methods). Implemented RepairMethodProtocol
    and register_method allowing custom or experimental repair passes to be registered
    dynamically with custom ranking scoring, execution context (RepairContext), and
    isolated reporting (MethodResult).
  • Method #13 Graft (sutura_engine.graft, legacy sutura/shell_wrap.py).
    Morphology shell-wrap repair tier using generalized-winding signed distance (Cast core)
    and true EDT dilation/erosion to close large openings while keeping healthy triangles
    verbatim. Placed on the auto-triage ladder ahead of fTetWild, with an automatic
    fidelity fallback to fTetWild if healthy detail deviation is detected (fidelity_ok).
  • Method #14 Mirror Complete (sutura_engine.mirror). Symmetry-plane reflection
    and outer-shell fusion for single-sided symmetric scans.
  • Method #15 Wall Thicken (sutura_engine.wall). Opt-in thin-wall thickening
    based on signed-distance field sampling and morphological dilation.
  • Learning Triage (sutura_engine.learning). Anonymous, bounded Bayesian ranking
    bonus (Beta(2, 2) posterior) derived from local (template, method) history in
    ~/.local/share/sutura/triage_learning.json (sutura clear-learning).
  • Auto-escalation Fallback Time Budget. Enforced wall-clock budget
    (min(max(30 s, 2 × baseline), 120 s)) bounding secondary method attempts on
    failing baselines without interrupting active operations.
  • fTetWild Benchmark Comparison with Graft. On scan meshes requiring solidifying,
    Graft produces reload-watertight output with lower runtimes while keeping
    undamaged triangles byte-identical to the input.

Known limitations

  • Graft (#13) extension packaging: Graft (#13) requires the optional Rust
    extension sutura_geom (build from rust/sutura-geom with maturin develop --release).
    The 0.7.0 installers do not bundle it yet; without it Graft is listed as
    unavailable and auto repair uses fTetWild as before. Bundling is planned for
    0.7.1.

Folded from the previously unreleased v0.6.1 batch

Added

  • Per-object auto escalation for multi-object 3MF. Auto escalation for
    multi-object 3MF archives now operates object-by-object instead of at the file
    level. Objects that are already reload-watertight after the baseline repair
    pipeline are left untouched; failing objects independently escalate through
    ranked recommendations (such as #8/#9 closing or #10 proxy rebuild). Each
    object in object_reports now carries its own method_used dictionary
    indicating the method number, name, and source (auto_baseline vs
    auto_escalated).

  • Outer-shell extraction pre-step for overlapping closed components. In
    auto mode, when a model fits the mechanical or dense_scan_heavy_si
    templates and contains multiple closed components that touch or overlap (e.g.
    Rubik-style assemblies or multi-part CAD imports), Sutura extracts the outer
    shell prior to Stage 1. Components are dissolved via a slight dilation
    (5e-4 * bbox_diagonal, ~0.05 mm for a 100 mm object) and boolean union
    with manifold3d, removing internal interfaces and degenerate
    self-intersections before the main repair passes.

  • Method #14 Mirror Complete (sutura/mirror_repair.py). A symmetry-aware
    alternative to Poisson closing for a single-sided scan: detect_mirror_plane
    fits the dominant boundary loop's plane (planarity x dominance
    confidence), mirror_close reflects the visible surface across it (winding
    flipped so the shared boundary edges cancel), welds the seam and dissolves
    overlapping halves into one outer shell with extract_outer_shell; low
    confidence or an invalid stitch falls back to screened-Poisson reconstruction
    (#8). The closing report records mode (mirror/poisson_fallback) and
    mirror_score.

  • Method #15 Wall Thicken (sutura/wall_thickness.py). Opt-in thin-wall
    analysis and repair: an SDF grid (area-weighted surface samples + nearest
    normal sign) yields a heatmap-ready per-vertex thickness with min/median/
    mean, and thicken_to_min offsets thin walls by a morphological dilation
    re-extracted with a dependency-free marching-tetrahedra pass. Excluded from
    the ranking (needs_user_input), selected with --methods 15; CLI
    --wall-min-thickness T sets the target (default 1 % of the bbox diagonal).

  • Learning triage (history.py + methods.rank_methods). Each repair now
    records the per-(template, method) outcome and elapsed time into
    ~/.local/share/sutura/triage_learning.json (anonymous: template and method
    slugs only). rank_methods adds a bounded Beta(2,2) Bayesian bonus
    ([0, 0.05], damped by speed, zero without data) for the templates the
    analysis actually fired. Toggle with learning_triage / SUTURA_LEARNING_TRIAGE
    / --no-learning-triage; reset with sutura clear-learning or the GUI
    Options → General → Reset learning button. No network.

Fixed

  • The untagged automatic method fallback is now time-budgeted. When the
    default pipeline is not strict-watertight and the auto path escalates to the
    ranked methods, the extras run under a wall-clock budget of
    min(max(AUTO_FALLBACK_MIN_S = 30 s, AUTO_FALLBACK_BUDGET_FACTOR = 2.0 × baseline seconds), AUTO_FALLBACK_MAX_S = 120 s). Once the budget is exhausted
    no further method is started — a method already running is not interrupted —
    the best candidate is kept, and the result carries
    method_used.budget_reached, method_used.fallback_budget_s and the
    "fallback budget reached" note (localized EN/TR in the GUI). A baseline that
    already took longer than the 120 s ceiling skips the ranked fallback entirely
    (method_used.fallback_skipped, baseline_s, "fallback skipped: baseline too
    slow"). Explicitly tagged methods (--methods) are never budgeted. The
    real-world corpus regression runs the untagged default path again, and the
    budget-stop/skip behaviour is covered by tests/test_methods.py.

v0.6.0

Choose a tag to compare

@Krateian Krateian released this 27 Sep 23:33
fe41eca

Sutura v0.6.0 — repair method registry and a reload-honest watertight verdict

Highlights

  • Repair method registry (#1–#12). Twelve stable methods in
    sutura/methods.py; --list-methods prints them, --analyze performs a
    cheap per-object analysis with ranked recommendations, and --methods 2,3,5
    tags the methods to try. The GUI gains a Method column in the file list
    and a right-click menu (Analyze, Recommended methods, Use method,
    External engines, Clear tags). Untagged repairs stay auto: the default
    pipeline runs first, and on a non-watertight result the ranked fallback is
    tried.
  • New standalone tiers.
    • Single-side scan closing — #8 poisson_close, #9 flat_back_close
      (sutura/closing.py).
    • Proxy template repair — #10 (sutura/proxy_repair.py).
    • Repeated-element repair — #11 repeat_auto, #12 repeat_manual
      (sutura/repeat_repair.py): segmentation-free rotational / translational /
      helical / lattice detection, similarity and mirror alignment, forward+reverse
      deviation detection, outer-shell extraction for assemblies, and an
      out-of-process CSG bridge (sutura/csg_bridge.py) for Linux/AppImage.
      Verified on the patterned samples: 24-tooth gear (C₂₄, torn tooth
      repaired, zero outside movement), PET bottle cap (C₁₂₀ knurls, localized
      transplant), Rubik's cube (3D lattice, outer-shell repair). Continuous /
      scaling shells (pistachio bowl, Sea Shell Vase) and an organic shell are
      honestly reported as not transplantable.
  • Reload-honest watertight verdict. A mesh is only called watertight when
    the saved file reloads strict-watertight (STL re-welds exactly-coincident
    float32 vertices on load). P-HONEST downgrades a false claim and the
    registry guard uses the same rule; the new P-WELD final pass heals the
    seam by splitting the float32-coincidence vertex groups with a sub-ULP nudge
    (face count unchanged). All seven real-world samples that previously reloaded
    non-manifold now reload strictly watertight.
  • Packaging and docs. csg_bridge.py / repeat_repair.py /
    repeat_picker_render.py ship in every install/updater/AppImage/PyInstaller
    list; new runtime dependencies scipy and trimesh. README/README.tr.md
    document the methods table, the new tiers and the honest verdict; CHANGELOG
    carries the full detail.

Notes

  • The untagged default path runs the ranked fallback when the baseline is not
    watertight; this is intentional (the default pipeline first, byte-identical
    when it already succeeds) and is the main behavioural change of this release.
  • The macOS .dmg is unsigned; the AppImage/GUI cannot self-update in place.

Full detail: CHANGELOG.md.

v0.5.1

Choose a tag to compare

@Krateian Krateian released this 26 Sep 21:38
cc75c30

Sutura v0.5.1: fixes for v0.5.0

A patch release fixing problems found in a full review of the v0.5.0 code and documentation. If you run v0.5.0, update to this version.

Highlights

  • Self-update fix: the in-app updater copied an incomplete list of modules, so triage.py and three other modules could be missing after an update. It now uses one list shared with install.sh, and a new test fails if the two lists ever differ.
  • External engines: resolving an engine binary no longer follows symlinks, so an engine that runs inside a virtualenv starts with that virtualenv's interpreter.
  • CI coverage: the triage, profile, engine, engine-integration, Engines GUI and fTetWild-manager test suites now run on every push and PR.
  • Documentation: corrections in README.md, README.tr.md, docs/cli-gui-parity-notes.md and docs/ATTRIBUTION.md.

Fixed

  • Self-update module list. sutura/updater.py's _copy_python_files and
    _install_linux now copy every module the application imports: triage.py,
    history.py, autorefine.py and mesh_classifier_v2.py were missing, so a
    self-update could leave an installed module at its old version (or absent on
    a fresh install) and silently degrade the engine layer. The lists are now a
    single APP_MODULES constant, and tests/test_updater.py asserts it matches
    install.sh's install list so the two can no longer drift.
  • CI test coverage. The triage (tests/test_triage.py,
    tests/test_triage_profiles.py), external-engine (tests/test_engines.py,
    tests/test_engine_integration.py, tests/test_engines_gui.py) and
    fTetWild-manager (tests/test_ftetwild_manager.py) suites now run in
    .github/workflows/ci.yml; they existed but were never executed by CI.
  • Engine executable resolution. sutura/engines.py's
    resolve_executable no longer follows symlinks, so an engine whose command
    is a virtualenv interpreter (.../venv/bin/python) runs inside that venv
    instead of the base interpreter (which lost the venv's site-packages). This
    only surfaced once tests/test_engine_integration.py ran in CI.
  • Documentation corrections. In README.md / README.tr.md: the Turkish
    preamble sentence that had been split across two sections was rejoined, the
    localized Options tab is Onarım (not Repair), the original-view defect
    RGB (235,60,70) was added to the Turkish text, the ftetwild install|uninstall subcommands are no longer described as read-only, and the
    test-suite list now includes triage. docs/cli-gui-parity-notes.md has a
    missing bullet line break fixed, and docs/ATTRIBUTION.md now states that
    the fTetWild fallback is active by default when installed rather than opt-in.

v0.5.0

Choose a tag to compare

@Krateian Krateian released this 26 Sep 20:04
c8348e1

Sutura v0.5.0 — Triage Engine

This release adds the Triage Engine: repair intensity presets, user profiles, external engines and an fTetWild install/remove manager. It also includes everything from the unreleased v0.4.2 batch.

Highlights

  • Intensity presets: Quick, Balanced (recommended, default), Thorough and Extreme.
    • Set with --intensity, the SUTURA_INTENSITY environment variable, or the intensity config key.
    • Balanced repairs exactly like v0.4.x. The watertight + Hausdorff shape guard is always on.
  • User profiles: named, editable profiles built on a preset.
    • Stored in ~/.config/sutura/profiles.json.
    • Listed with --list-intensities; edited in Options > Repair.
  • External engines: register repair tools you installed yourself with ~/.config/sutura/engines/*.toml.
    • An engine can run at a fixed slot in the pipeline, or you can define a fully custom chain.toml.
    • Every engine result passes the same shape guard before it is used.
    • Check your setup with sutura engines list|check.
  • fTetWild manager: a new Options > Engines tab and sutura ftetwild status|install|uninstall show the size, stream progress and can be cancelled.
    • About 105 MB to download and 538 MB installed on macOS arm64.
  • Deep-repair ladder and dense-output fix: fTetWild boundaries that come back far denser than the input are decimated, and the result is accepted only if it stays watertight and within shape. Thorough and Extreme add an extra step at the density threshold.
  • Tabbed Options window: General, Repair, Experimental, Engines, Updates and Changelog tabs, plus an explicit fTetWild optimisation option.
  • Install and CI:
    • install.sh falls back to a checksum-verified standalone Python 3.11, which fixes Arch Linux.
    • New Linux and macOS end-to-end install workflows.
    • New typical-user repair corpus.
  • Fix: the app no longer aborts when closed while the background update check is running.

Measured on a 50-mesh typical-user corpus: 50/50 strictly watertight, with no timeouts. On a 115-mesh torture corpus: 109/115.


Added

  • Triage Engine intensity presets (--intensity). The post-Stage-1
    effort is grouped into four read-only presets in a new single source of
    truth, sutura/triage.py (INTENSITIES, an IntensitySpec dataclass,
    PRESETS, resolve_intensity). --intensity quick|balanced|thorough|extreme (or SUTURA_INTENSITY, or the intensity
    config key; CLI > env > config > balanced) selects them and the report
    carries triage_intensity (--human an Intensity: line). Stage 1 is
    never affected — the repair mode and --profile keep their behaviour.
    balanced equals the historical repair.py constants (the module
    constants are now derived from it) and is regression-tested byte-identical
    on the real-world samples and typical-corpus meshes (--no-fallback-ftetwild
    runs to remove fTetWild's run-to-run nondeterminism; the fTetWild tier
    itself is covered by the faked-bridge tests). quick disables the
    deep-repair ladder and the fTetWild tier and has an empty decimation
    ladder. thorough raises the fTetWild budget to 600 s, the Hausdorff
    sample count to 400,000 and adds the threshold decimation rung
    (DENSE_RATIO × max(input, DENSE_MIN_FACES)); extreme removes the
    input-size cap, raises the budget to 1,800 s and the samples to 1,000,000,
    and makes one extra fTetWild run with optimize=True when every
    decimation rung of a dense boundary fails, before the undecimated
    fallback. The FTETWILD_MAX_FACES skip is now an explicit
    ftetwild_enabled bool plus an ftetwild_max_faces int-or-None instead
    of the previous 0/300000 double meaning. Measured on thingi10k_73444
    with --intensity thorough (stage 2 active): the optimize=False
    boundary returned 469,144 faces, the 20,000/23,646 rungs failed the
    watertight check and the threshold target of 80,000 succeeded (watertight,
    adopted, shape_changed flagged at 5 % — the shape guard is invariant, not
    a preset field). The CLI exposes --intensity; dry_run reports it; the
    GUI adds a preset combo with per-preset EN/TR tooltips and a Reset to
    recommended
    button on the Options Repair tab (restoring Balanced and the
    default checkboxes), keeps the selection batch-wide and persists it to the
    config. New suites tests/test_triage.py and offscreen
    tests/test_options_dialog.py cases, plus tests/test_repair_mode.py
    intensity cases. The named user-profile override layer is implemented on
    top of this preset table (see the next entry).
  • Named intensity profiles (--intensity <profile>). On top of the four
    read-only presets, users can create, rename, duplicate and delete any
    number of named profiles. A profile is a base preset plus a sparse
    mapping of overrides of the IntensitySpec fields (only the fields the
    user changed are stored); the built-in presets stay read-only. Profiles
    are stored in a separate, schema-versioned
    ~/.config/sutura/profiles.json (its own file, not a config.json key,
    because config.json is rewritten whole and a corrupt profile blob must
    never poison ambient configuration), written atomically via a temp file +
    os.replace. A corrupt, unreadable, wrong-schema or absent file warns on
    stderr and falls back to the built-in presets — it can never crash a
    repair. Profile names must be non-empty, unique case-insensitively, and
    must not shadow a built-in preset name; triage.name_error enforces the
    rules. --intensity (and SUTURA_INTENSITY / the intensity config key)
    accept a preset or a profile name; the new --list-intensities prints the
    presets and the profiles with their effective values and exits. The report
    carries triage_profile_base and triage_overrides when a profile is
    used, in addition to triage_intensity. The Hausdorff sample floor
    (200,000) is enforced in the resolver as well as the GUI: a hand-edited
    profile below it is clamped with a warning. The Options Repair tab's
    combo lists the presets, a separator, then the user profiles, with
    New (from the current selection/editor values), Duplicate,
    Rename and Delete (confirming) buttons and an inline Profile
    settings
    editor exposing every field with typed widgets/ranges (fTetWild
    on/off, max faces with a No limit box, timeout, decimation ladder
    including threshold, optimise retry, deep repair off/local/full, and the
    floored Hausdorff samples); editing a built-in offers Save as new
    profile
    , and Reset to recommended still selects Balanced without
    deleting any profile. The watertight + Hausdorff shape guard is never a
    field. EN/TR strings and hover tooltips cover the new controls. New suite
    tests/test_triage_profiles.py (storage round-trip, corrupt file, name
    rules, resolution precedence, the CLI listing/selection and the offscreen
    GUI create/rename/duplicate/delete flow with a temporary HOME).
  • fTetWild tet optimisation is an explicit option. It stays off by
    default (measured: only the boundary surface is used, and on
    thingi10k_46012 the optimisation pass took 34 s instead of 6 s with the
    same shape fidelity). --ftetwild-optimize, SUTURA_FTETWILD_OPTIMIZE=1
    or the ftetwild_optimize config key turn it on; in the GUI it is the
    Optimise tetrahedra (not recommended) box under the fTetWild fallback on
    the Repair tab, with a note that it can make complex repairs several
    times slower.
  • Options window (GUI). The batch-wide switches moved from the Options
    drop-down into a separate, non-modal window with tabs General (anonymous
    usage history), Repair (fTetWild fallback, + self-intersections),
    Experimental (autorefine, indirect autorefine, join small components,
    edge-tiebreak), Updates and Changelog. It opens from the Options
    button or Ctrl+, / Cmd+,; the button still shows how many options differ
    from their defaults, and the update indicator next to it opens the
    Updates tab. The chk_* widgets are the same objects, so the CLI
    mapping is unchanged.
  • Update settings. The Updates tab has a manual Check for updates
    button, an Automatic updates checkbox (the existing check_for_updates
    key) and, enabled only while it is on, Check on every start (new
    check_on_startup key, default off). updater.should_check: automatic
    updates off never checks; on checks on the existing 7-day interval; on plus
    Check on every start checks at every start. AppImage builds keep their
    download-from-the-releases-page behaviour.
  • Release notes in the GUI. The Changelog tab fetches the GitHub release
    notes lazily in a background thread (4 s timeout, cached in the config
    directory) and falls back to the cache or the bundled CHANGELOG.md
    offline. No network access on headless Qt platforms (tests), and running
    threads are waited for on close.
  • External repair engines and the fTetWild install manager (Triage Engine,
    parts 2 and 3).
    sutura/engines.py (user-installed third-party repair
    binaries configured as ~/.config/sutura/engines/*.toml) and
    sutura/ftetwild_manager.py (inspect/install/remove the optional fTetWild
    extra in the Sutura environment only) are wired into the repair pipeline and
    the interface. Engines are loaded once per run and slotted in at their
    placement (before_stage1, after_stage1, after_stage2,
    replace_ftetwild, final_fallback) or in an explicit chain.toml order;
    load problems only warn (engine_warnings) and never fail a repair.
    Every engine result passes the identical holes/non-manifold + Hausdorff
    guard as fTetWild
    — adopted only when no worse on holes+non-manifold, and
    an adopted far-off result is flagged shape_changed (FTETWILD_MAX_HAUSDORFF_REL)
    rather than rejected; an engine cannot bypass the guard. Each run adds a
    per-engine report entry (name, placement, rc,...
Read more