(a, b, c, T, memo, depth)
| 119 | } else if (n === 1) { |
| 120 | // 1→2 bisect: split edge a (longest), unsplit edges b and c stay intact in |
| 121 | // separate children. Median from opposite vertex to a's midpoint: |
| 122 | // m = ½ √(2b² + 2c² − a²) |
| 123 | const m = 0.5 * Math.sqrt(Math.max(0, 2*b*b + 2*c*c - a*a)); |
| 124 | total = simTri(a / 2, b, m, T, memo, depth + 1) |
| 125 | + simTri(a / 2, c, m, T, memo, depth + 1); |
| 126 | } else { |
| 127 | // n === 2: 1→3 fan. Sorted descending → untouched edge is the smallest (c); |
| 128 | // split neighbours are a and b. Median from a's opposite vertex to a's |
| 129 | // midpoint: m = ½ √(2b² + 2c² − a²). |
| 130 | const m = 0.5 * Math.sqrt(Math.max(0, 2*b*b + 2*c*c - a*a)); |
| 131 | total = simTri(c, a / 2, m, T, memo, depth + 1) |
| 132 | + simTri(m, c / 2, b / 2, T, memo, depth + 1) |
| 133 | + simTri(b / 2, c / 2, a / 2, T, memo, depth + 1); |
| 134 | } |
| 135 | |
| 136 | memo.set(key, total); |
| 137 | return total; |
| 138 | } |
| 139 | |
| 140 | // ── Decimation-target recommendation ───────────────────────────────────────── |
| 141 | // |
| 142 | // Estimates the post-decimation triangle count that preserves the texture's |
| 143 | // detail with minimum impact on visual quality. Based on: |
| 144 | // |
| 145 | // target_edge = COARSEN × pixelsPerEdge × pixMm × √(REF_AMP / max(amp, MIN_AMP)) |
| 146 | // N_tri = K_geom × surfaceArea / target_edge² |
| 147 | // |
| 148 | // COARSEN = 1.0 → Nyquist target: triangle edge matches the texture's intrinsic |
| 149 | // detail spacing. Earlier we tried 3× ("aggressive"), but that gave acceptable |
| 150 | // quality only on structured textures (logos, knurling) — on noise / fbm / |
| 151 | // leather textures, 3× coarsen produces visible faceting because every pixel |
| 152 | // of noise IS a feature, not just the high-gradient ones the analyser flags. |
| 153 | // At 1× the user can still drag the max-tri slider down for smaller files; |
| 154 | // the bench (bench-decim-quality.mjs) shows error becomes imperceptible at |
| 155 | // this ratio for both smooth and sharp test textures. |
| 156 | // |
| 157 | // Amplitude scaling: low amplitude needs fewer triangles to faithfully |
| 158 | // represent a gentle relief; high amplitude (heavy displacement) needs more. |
| 159 | const DECIM_COARSEN = 1.0; |
| 160 | const DECIM_REF_AMP = 0.5; |
| 161 | const DECIM_MIN_AMP = 0.1; |
| 162 | const DECIM_MIN_TRI = 10_000; |
| 163 | // Recommendation ceiling. Sized for sensible default file sizes — much |
| 164 | // smaller than HARD_CAP_TRIANGLES (which is the OOM ceiling for what the |
| 165 | // pipeline can survive). Users who want more can drag the Max Triangles |
| 166 | // slider up to its full range (20M); Smart just won't suggest above this |
| 167 | // by itself, since the downstream printable mesh rarely needs more. |
| 168 | const DECIM_MAX_TRI = 2_000_000; |
| 169 |
no test coverage detected