2026-10-29 04:03 · worktree ~/code/fcp_devel_v6, branch v6 (decision 27;
lobe parts in progress) · scripts in fcp_2910_v6/scripts, the shared helpers (objects, measures, renders) in
fcp_2810_partition/scripts · the partition study that led here:
fcp_2810_partition/report
encode(atoms="auto") stops at
twice the noise level read at the top height degrees of the long parts (the atoms are orthonormal: noise spreads evenly over
them; median |score| / 0.6745); a prefix like any other. The format and the decoder are v5.31's; meshes encode to the same
bytes. 287 tests pass (6 new).scripts/eval_v6.py: the ten meshes and per object four clouds of fcp_2710_tests/pointclouds;
reference the clean mesh; ratios: geometric mean over the objects; green / red: 2 % better / worse (normals 5 %); "auto":
the atoms="auto" descriptor against v5.31's 6144.
| input | parts v5.31 → v6 | Chamfer % v5.31 256 | × v6 256 | Chamfer % v5.31 1024 | × v6 1024 | Chamfer % v5.31 6144 | × v6 6144 | >30° × 256 | >30° × 1024 | >30° × 6144 | auto: bytes × (vs 6144) | auto: Chamfer × (vs 6144) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| mesh | 134 → 134 | 0.358 | 1.000 | 0.232 | 1.000 | 0.199 | 1.000 | 1.000 | 1.000 | 1.000 | 0.75 | 1.003 |
| cloud 10k | 135 → 108 | 0.438 | 0.981 | 0.258 | 0.998 | 0.212 | 1.007 | 0.991 | 0.995 | 0.999 | 0.65 | 1.007 |
| sparse cloud 2048 | 184 → 143 | 0.803 | 0.978 | 0.517 | 0.999 | 0.488 | 0.997 | 0.984 | 0.989 | 0.996 | 0.56 | 1.006 |
| noisy mesh, 10k samples | 190 → 146 | 0.623 | 0.987 | 0.541 | 0.995 | 0.538 | 0.997 | 1.012 | 0.997 | 0.997 | 0.65 | 0.997 |
| per-point noise 0.6 %, 10k | 279 → 213 | 0.998 | 0.945 | 0.453 | 0.994 | 0.380 | 1.003 | 1.002 | 0.989 | 0.997 | 0.70 | 1.006 |
scripts/rd_split.py (swapped in for the
package's cut rule); CPU NumPy, 0.2-1 s per object, not optimized. Tracks are not dropped by it (a ring's energy includes its
position, which is not the geometric cost of leaving it out): v6's short-track filter stays.Speed (1024 atoms, best of 3; projections once per track in float32, each level only re-sums): mug / elephant / dino / horse / dress / shoe encode on CUDA in 62 / 77 / 69 / 68 / 171 / 89 ms with the package's rule, 187 / 110 / 142 / 172 / 255 / 153 ms with rdT256 (CPU 117-586 → 199-670 ms); the same parts and values as the first version.
scripts/scans_rd.py: the bench's scans, loaded and repaired as api/bench/scans.py does, each
scored against itself; ratio rdT256 / v6.
251 scans with both (of 251; 0 did not encode or load).
| atoms | Chamfer % median v6 | rdT256 | ratio (geo. mean) | scans better | worst ratio | >30° % median v6 | rdT256 | ratio (geo. mean) |
|---|---|---|---|---|---|---|---|---|
| 256 | 0.360 | 0.308 | 0.863 | 98 % | 1.01 | 27.7 | 19.7 | 0.647 |
| 1024 | 0.246 | 0.236 | 0.953 | 93 % | 1.03 | 14.9 | 12.9 | 0.792 |
| 6144 | 0.204 | 0.207 | 1.008 | 35 % | 1.16 | 6.4 | 6.7 | 1.022 |
Parts 4778 → 4939; encode (6144 atoms, CUDA) median 71 → 170 ms.
The cloud kinds of fcp_2710_tests/pointclouds at 10k points (per-point noise encoded with
noise=); ratio rdT256 / v6.
| cloud (10k) | parts v6 → rdT256 | Chamfer × 256 | Chamfer × 1024 | Chamfer × 6144 | objects better 256 | objects better 1024 | objects better 6144 | >30° × 256 | >30° × 1024 | >30° × 6144 |
|---|---|---|---|---|---|---|---|---|---|---|
| per-point noise 0.12 % | 112 → 162 | 0.883 | 0.944 | 0.996 | 100 % | 100 % | 70 % | 0.626 | 0.788 | 0.859 |
| per-point noise 0.6 % | 213 → 271 | 0.795 | 0.925 | 1.003 | 100 % | 100 % | 50 % | 0.816 | 0.758 | 0.893 |
| per-point noise 1.2 % | 165 → 209 | 0.901 | 0.987 | 1.000 | 100 % | 50 % | 70 % | 0.881 | 0.858 | 0.959 |
| noisy mesh samples 0.6 % | 146 → 194 | 0.943 | 0.995 | 0.999 | 90 % | 70 % | 50 % | 0.853 | 0.839 | 0.985 |
| noisy mesh samples 1 % | 142 → 194 | 0.909 | 1.002 | 0.999 | 90 % | 50 % | 70 % | 0.793 | 0.793 | 0.918 |
| density gradient | 151 → 191 | 0.885 | 0.951 | 1.007 | 100 % | 90 % | 70 % | 0.662 | 0.799 | 0.903 |
scripts/monotonicity.py: each descriptor encoded once at 6144 atoms, decoded at its prefixes 16 ... 6144;
a step "worse" when the error grows by more than 0.5 % from the previous count; ten objects, 9 steps each.
| input | cut rule | Chamfer: steps worse | worst | angle error: steps worse | worst |
|---|---|---|---|---|---|
| meshes | cut rule of v6 (tracked-fit) | 1 of 90 | +8 % | 4 of 90 | +72 % |
| meshes | rate-distortion (rdT256) | 0 of 90 | +0 % | 4 of 90 | +8 % |
| clean clouds, 10k points | cut rule of v6 (tracked-fit) | 2 of 90 | +10 % | 12 of 90 | +50 % |
| clean clouds, 10k points | rate-distortion (rdT256) | 1 of 90 | +36 % | 13 of 90 | +17 % |
| clouds with 0.6 % per-point noise, 10k | cut rule of v6 (tracked-fit) | 9 of 90 | +150 % | 18 of 90 | +234 % |
| clouds with 0.6 % per-point noise, 10k | rate-distortion (rdT256) | 9 of 90 | +80 % | 11 of 90 | +225 % |
| input | parts v5.31 → v6 | Chamfer % v5.31 256 | × v6 256 | Chamfer % v5.31 1024 | × v6 1024 | Chamfer % v5.31 6144 | × v6 6144 | >30° × 256 | >30° × 1024 | >30° × 6144 | auto: bytes × (vs 6144) | auto: Chamfer × (vs 6144) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| mesh | 134 → 156 | 0.358 | 0.924 | 0.232 | 0.969 | 0.199 | 1.002 | 0.586 | 0.885 | 1.033 | ||
| cloud 10k | 135 → 161 | 0.438 | 0.854 | 0.258 | 0.941 | 0.212 | 0.999 | 0.599 | 0.786 | 0.880 | ||
| sparse cloud 2048 | 184 → 214 | 0.803 | 0.843 | 0.517 | 0.974 | 0.488 | 1.000 | 0.777 | 0.833 | 0.897 | ||
| noisy mesh, 10k samples | 190 → 194 | 0.623 | 0.931 | 0.541 | 0.991 | 0.538 | 0.996 | 0.863 | 0.836 | 0.982 | ||
| per-point noise 0.6 %, 10k | 279 → 271 | 0.998 | 0.751 | 0.453 | 0.919 | 0.380 | 1.005 | 0.817 | 0.749 | 0.891 |
| input | parts v5.31 → v6 | Chamfer % v5.31 256 | × v6 256 | Chamfer % v5.31 1024 | × v6 1024 | Chamfer % v5.31 6144 | × v6 6144 | >30° × 256 | >30° × 1024 | >30° × 6144 | auto: bytes × (vs 6144) | auto: Chamfer × (vs 6144) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| mesh | 134 → 231 | 0.358 | 0.995 | 0.232 | 0.959 | 0.199 | 0.998 | 0.759 | 0.902 | 0.988 | ||
| cloud 10k | 135 → 223 | 0.438 | 0.909 | 0.258 | 0.929 | 0.212 | 0.994 | 0.668 | 0.787 | 0.855 | ||
| sparse cloud 2048 | 184 → 304 | 0.803 | 0.893 | 0.517 | 0.956 | 0.488 | 0.988 | 0.852 | 0.828 | 0.877 | ||
| noisy mesh, 10k samples | 190 → 264 | 0.623 | 0.954 | 0.541 | 0.984 | 0.538 | 0.997 | 0.913 | 0.799 | 0.951 | ||
| per-point noise 0.6 %, 10k | 279 → 343 | 0.998 | 0.804 | 0.453 | 0.900 | 0.380 | 0.997 | 0.821 | 0.690 | 0.850 |
scripts/drop_short.py: ratios to the package without it. v6 takes 3 layers / 5 %: 4 / 10 % loses real
detail at 6144, 2 / 5 % keeps more blobs.
| cloud | drop (layers, area) | parts | Chamfer × 256 | Chamfer × 1024 | Chamfer × 6144 |
|---|---|---|---|---|---|
| cloud 10k | drop3-5% | 135 → 107 | 0.981 | 0.998 | 1.007 |
| cloud 10k | drop2-5% | 135 → 118 | 0.986 | 0.997 | 1.002 |
| cloud 10k | drop4-10% | 135 → 103 | 0.979 | 1.001 | 1.013 |
| sparse cloud 2048 | drop3-5% | 184 → 142 | 0.976 | 0.999 | 0.998 |
| sparse cloud 2048 | drop2-5% | 184 → 153 | 0.981 | 0.999 | 0.998 |
| sparse cloud 2048 | drop4-10% | 184 → 127 | 0.966 | 0.996 | 1.000 |
| noisy mesh, 10k samples | drop3-5% | 190 → 142 | 0.987 | 0.996 | 0.997 |
| noisy mesh, 10k samples | drop2-5% | 190 → 155 | 0.988 | 0.997 | 0.999 |
| noisy mesh, 10k samples | drop4-10% | 190 → 121 | 0.978 | 0.994 | 0.994 |
| per-point noise 0.6 %, 10k | drop3-5% | 279 → 206 | 0.940 | 0.993 | 1.007 |
| per-point noise 0.6 %, 10k | drop2-5% | 279 → 234 | 0.966 | 0.997 | 1.002 |
| per-point noise 0.6 %, 10k | drop4-10% | 279 → 191 | 0.921 | 0.991 | 1.011 |
scripts/noise_floor.py: the descriptor cut where the atoms fall under c times the level, against the
6144 prefix. It does not denoise (the slicers' noise is smooth along the height and sits in the low degrees too); it is a
stopping point: v6 takes c = 2.
| input | stop at | atoms (mean) | Chamfer × (vs 6144) | >30° × (vs 6144) |
|---|---|---|---|---|
| mesh | 2 × the noise level | 3704 | 1.003 | 1.025 |
| mesh | 3 × the noise level | 3109 | 1.009 | 1.034 |
| mesh | 4 × the noise level | 2736 | 1.015 | 1.098 |
| cloud 10k | 2 × the noise level | 4034 | 1.002 | 1.001 |
| cloud 10k | 3 × the noise level | 3416 | 1.006 | 1.012 |
| cloud 10k | 4 × the noise level | 3026 | 1.011 | 1.034 |
| sparse cloud 2048 | 2 × the noise level | 3862 | 1.008 | 1.021 |
| sparse cloud 2048 | 3 × the noise level | 3195 | 1.020 | 1.053 |
| sparse cloud 2048 | 4 × the noise level | 2763 | 1.031 | 1.087 |
| noisy mesh, 10k samples | 2 × the noise level | 4354 | 1.000 | 1.085 |
| noisy mesh, 10k samples | 3 × the noise level | 3732 | 1.001 | 1.117 |
| noisy mesh, 10k samples | 4 × the noise level | 3276 | 1.004 | 1.163 |
| per-point noise 0.6 %, 10k | 2 × the noise level | 4568 | 1.003 | 1.017 |
| per-point noise 0.6 %, 10k | 3 × the noise level | 4215 | 1.006 | 1.024 |
| per-point noise 0.6 %, 10k | 4 × the noise level | 3981 | 1.010 | 1.043 |
v6-lobes, experimental)fcp/_lobes.py: a robust fit along the height locates lobes (each round keeps at
least the better half of the layers); each ring is cut on its own outline, walked off the lobe's tip and down to the fillets;
the body is closed across the gap by its own wall (the ring either side of the gap in polar form, a cubic in angle, matched to
both ends); a peeled run is extended along the moving cut until nothing sticks out; a run goes to the track it touches, and is
not peeled when it touches none (free lobe parts added 6 parts on the elephant and the horse for nothing).
Results (ten meshes, on against off): the mug's body is one part through the lower handle root, and the mug is better at
every budget (256 atoms: Chamfer 0.264 against 0.282, normals over 30° 7.7 % against 12.7 %; with its upper root a part of its
own, 0.246 and 4.0 %); the dino slightly better; the dress, shoe and horse worse at 256 / 1024 atoms; overall Chamfer 1.019 /
1.014 / 0.998 times, normals 0.97 / 1.02 / 1.00 at 256 / 1024 / 6144. The boundary flags cost 100-500 ms per decode on the
host (the CUDA decode is 30-40 ms without them). Not in v6. 287 tests pass on the branch.
Why the dress, shoe and horse lose. Their lobes are where a leg or an arm joins the body (the dress at 41-49: a leg's
track ends at 40 and its remnant is peeled and given back to it); the body is cut at those joints anyway, so the peel only adds
an overlapping part. The decisive test (second commit on the branch): keep a run only when the cut rule then makes fewer
parts of the body and of the track its lobe joins. No run on the ten meshes passes: mug 6 → 7, dress 11 → 13 and 10 → 10,
horse 8 → 9 and 4 → 4, shoe 12 → 12, dino 3 → 3, 6 → 6, 7 → 7 (the other runs touch no track). Peeling a lobe never makes
the body's parts longer; the mug's gain at small budgets came from where the cuts fall and one more part. Conclusion:
lobe parts do not make parts bigger; left on the branch as a record.
Speed (1024 atoms, best of 3): v6 encodes the mug / dino / shoe in 62 / 75 / 91 ms on CUDA (118 / 129 / 168 on the CPU)
and decodes in 13 / 16 / 17 ms (45 / 56 / 58); with lobes, encode about 1 s and decode 252 / 71 / 174 ms (host NumPy: the robust
fit, the cuts, the boundary flags on every decode).scripts/lobes_check.py: the v6 package with lobes on and off.
| object | parts off → on | Chamfer % off 256 | on 256 | Chamfer % off 1024 | on 1024 | Chamfer % off 6144 | on 6144 | >30° % off 256 | on 256 | >30° % off 1024 | on 1024 | >30° % off 6144 | on 6144 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| battery_stepped | 4 → 4 | 0.259 | 0.259 | 0.259 | 0.259 | 0.259 | 0.259 | 3.0 | 3.0 | 3.0 | 3.0 | 3.0 | 3.0 |
| elephant | 27 → 27 | 0.785 | 0.785 | 0.372 | 0.372 | 0.233 | 0.233 | 59.6 | 59.6 | 42.4 | 42.4 | 16.5 | 16.5 |
| bowl_of | 5 → 5 | 0.209 | 0.209 | 0.198 | 0.198 | 0.197 | 0.197 | 4.8 | 4.8 | 0.3 | 0.3 | 0.4 | 0.4 |
| bowl_gso | 6 → 6 | 0.218 | 0.218 | 0.199 | 0.199 | 0.198 | 0.198 | 7.5 | 7.5 | 1.1 | 1.1 | 1.0 | 1.0 |
| mug | 8 → 9 | 0.282 | 0.264 | 0.225 | 0.224 | 0.221 | 0.221 | 12.7 | 7.7 | 1.0 | 1.0 | 0.6 | 0.6 |
| dino | 25 → 25 | 0.404 | 0.399 | 0.194 | 0.195 | 0.134 | 0.134 | 37.4 | 35.6 | 18.0 | 18.4 | 7.3 | 7.0 |
| open_box | 2 → 2 | 0.257 | 0.257 | 0.257 | 0.257 | 0.257 | 0.257 | 1.0 | 1.0 | 1.0 | 1.0 | 1.1 | 1.1 |
| dress | 17 → 19 | 0.352 | 0.420 | 0.185 | 0.209 | 0.155 | 0.153 | 42.7 | 48.9 | 31.5 | 34.8 | 26.8 | 26.1 |
| shoe | 22 → 22 | 0.440 | 0.462 | 0.251 | 0.253 | 0.190 | 0.190 | 33.4 | 35.5 | 26.1 | 26.5 | 7.6 | 8.0 |
| horse | 18 → 19 | 0.374 | 0.389 | 0.185 | 0.187 | 0.142 | 0.142 | 27.8 | 29.0 | 11.0 | 11.2 | 2.9 | 2.7 |
fcp_2810_partition.