FCP: collision and inside-outside queries straight from the descriptor

fcp_2910_collision, v2 · package v5.4 (parts="tracked-fit", branch caps-cutfix) · GTX 1650 (4 GB), 12 CPU threads · 1024 layers, N 128, M 64, 2048 samples per ring, min island area 0, cut at 1e-08 energy left or 5 MB · query: 512 ring vertices, rule nearest · generated 2026-10-30 04:32 · v1 (1024 layers, N 64, 12288 atoms; with the dog and elephant)

Summary

querysetFCPbest mesh methodFCP speed-uperror bound (% diag)correctsize
Inside-outside, GPU11 objects, 1024 layers9.6 ns/ptEmbree 50.1 ns/pt5.2×0.32 %≤ 2.2 MB
Collision, GPU11 pairs, 1024 layers7.0 µs/poseFCL 18.2 µs/pose2.6×miss 0.44 % (0.03 % w/o gear), false 0.00 %99.93 %
Collision, GPU, every other sample3.2 µs/poseFCL 18.2 µs/pose5.7×99.84 %
Inside-outside, CPU (v5.5)12 objects (household, figurines, spring, knot), 256 layers24.6 ns/pt (12 thr)Embree 52.4 ns/pt2.1×0.34 %≤ 1.0 MB
Collision, CPU (v5.5)11 pairs, 256 layers24.0 µs/pose (12 thr)FCL 18.4 µs/pose (1 thr)0.8×miss 0.00 %, false 0.05 %99.94 %
Collision, CPU, every other sample6.3 µs/pose (12 thr)FCL 18.4 µs/pose (1 thr)2.9×miss 0.36 %99.92 %
In short. Straight from the descriptor, without decoding, FCP answers inside-outside in 10 ns per point on the GPU, 5.2× faster than Embree, and 2.1× faster on the CPU; any point farther than 0.34 % of the diagonal from the surface is classified right. Collision is approximate with a measured bound: on the GPU it is 2.6× faster than FCL at 99.93 % correct, no contact deeper than 0.44 % of the diagonal missed and no false contact at all. On the CPU it matches FCL only with every other sample (2.9×, misses up to 0.36 %); at the densest sampling it is slower (0.8×), so the collision speed-up is a GPU result. Descriptors are 0.1–2.2 MB. The bounds are empirical (17 distinct objects, 2M points each; 17 distinct pairs × 2000 poses), and the worst cases come from sharp corners (the gear) and pieces that start or end between slices.
Error bound and CPU time against the layers, household objects

CPU, 12 objects: household, two figurines, the spring and the knot (v5.5): error bound (left axis) and time (right axis, log) against the layers; (a) points against Embree, (b) collision against FCL. The GPU results are in sections 2–4.

Question. Can the descriptor answer "is this point inside?" and "do these two placed objects collide?" directly, without decoding to a mesh, faster than the standard and state-of-the-art mesh methods (BVH ray casting, BVH winding numbers, BVH triangle-triangle tests), and how accurately? Eleven watertight objects: six everyday ones (mug, bowl, dino, horse, shoe, dress: 8k–444k faces) and five concave, interleaved or perforated ones (a pan with 334 holes, a trefoil knot, a coil spring, a Menger sponge, a 24-tooth gear). v2 of the study: one encoder setting for every object, chosen by a sweep (section 1); the dog and the elephant of v1 are left out.
The setting. 1024 layers, N 128 harmonics, height degree up to 64, 2048 arc-length samples per ring, every island kept (the package drops tracks below 0.2 % of the largest slice's area), encoded with every atom and cut to all but 1e-8 of the energy (at most 5 MB): 0.08–2.2 MB per object, 25–40 s and at most 1.9 GB to encode. Package v5.4, parts="tracked-fit" (with decision 29's cut fix); the study patches three encoder constants in-process and replaces the selection's padded array (lowmem.py); nothing in the package is changed.
How FCP answers (scripts/fcpq.py). The rings at the layer centres are evaluated once from the stored polynomials (512 vertices each; the ring tables take 0.1–0.3 s per object). A point is inside when the signed count of the parts whose ring at the nearest layer centre contains it is positive (hole parts count −1): one even-odd crossing test per part present at that height, on the edges of the point's y-bin only. The height is read to half a layer (0.05 % of the diagonal or less). Collision: the ring vertices of each object (strided) are mapped into the other's canonical frame and tested; a pose collides when any is inside. All poses in one CUDA launch (or a numba prange). CPU and GPU give the same answers.
Findings.
Caveats. FCL, Coal and PyBullet are timed through their Python bindings, one pose per call (a few µs of overhead per pose); the GPU methods batch all 2000 poses. The Warp collision kernel tests every triangle of A against B's BVH, so it scales with A's faces. FCP's collision samples are the side-surface ring vertices: a penetration shallower than their spacing can be missed. The ring tables cost 0.1–0.3 s per object to build (Embree's BVH: about 1 ms), paid once per object, not per query. GPU: a GTX 1650, which is small.

1. Settings: one encoder setting for every object

How the setting was chosen. Every object encoded at 512 and 1024 layers × N 64 and 128 (2048 samples per ring, every island kept), plus two controls at 1024 layers: the package's minimum island area (0.002) and N 255. Each descriptor is cut to all but 1e-8 of the energy (at most 5 MB) and queried at 256, 512 and 1024 ring vertices. The measure is the near-surface points wrong farther than 0.1 % of the diagonal from the surface: a band any approximation of the surface needs, about the size of the meshes' own facets. 2048 layers were left out (encode cost).

Every setting at NT 512 (the cut descriptor: all but 1e-08 of the energy, at most 5 MB)

Wrong = of 1M points near the surface (±2 % diag) or 1M uniform in the box + 10 %, against the mesh. Filter boxes under each header take text or >x, <=x, !0; click a header to sort.

objectlayersNmin areaMBpartsnear wrong
> 0.1 %
> 0.2 %> 0.3 %uniform
> 0.1 %
near wrong
all, %
max err
% diag
near
ns/pt
uniform
ns/pt
encode sencode
peak GB
gear5126400.002466652708931.6780.28113.39.3nan0.5
mug5126400.281240030.1540.15820.57.191.0
bowl5126400.20710010.3130.11425.211.091.1
dino5126400.6245826030.3930.26614.32.771.0
horse5126400.542100000.2500.09510.73.061.0
shoe5126400.84109400060.3760.19414.77.281.1
dress5126400.62311825030.9930.23713.83.491.1
pan_holes5126400.49594818740.4270.36224.98.4111.1
knot5126400.251200000.0500.0189.52.6111.1
spring5126400.18500000.4350.08323.212.291.0
sponge5126400.0596140070.3980.18214.710.921.3
mug51212800.4112110030.1110.19920.57.1101.0
bowl51212800.32710010.2330.12225.211.191.1
dino51212800.8145741030.3750.20614.42.771.0
horse51212800.742120000.1890.14410.83.061.0
shoe51212801.3810961020.2460.23914.87.291.2
dress51212800.98311462030.8870.21614.63.791.1
pan_holes51212800.79593114530.3320.33524.98.4nan0.5
knot51212800.311200000.0390.0269.52.6101.1
spring51212800.30500000.4360.09323.212.291.0
sponge51212800.089600000.3460.08114.810.931.3
gear51212800.002548001040.6490.17112.79.001.0
mug10246400.361300000.1150.08823.98.2331.3
bowl10246400.281200000.2570.07827.712.5331.3
dino10246400.9767131000.2120.27212.53.2251.2
horse10246400.742300000.2000.09710.42.9231.3
shoe10246401.33164340030.3240.19315.17.9311.4
dress10246400.9543190000.6910.19511.13.0321.4
pan_holes10246400.79115259230.3660.32225.18.7331.5
knot10246400.311200000.0430.0149.32.5341.4
spring10246400.23450000.1780.16321.410.6311.2
sponge10246400.0596230070.2980.15415.611.3121.7
gear10246400.002475055308911.6870.27718.312.311.0
mug102412800.531310000.0740.10624.18.2361.3
bowl102412800.461200000.1480.03927.612.5341.3
dino102412801.2867122000.1780.23711.02.6271.3
horse102412801.052320000.1360.18410.52.9251.3
shoe102412802.1816440000.1800.14415.17.9341.6
dress102412801.524310000.5350.11811.23.0341.4
pan_holes102412801.281152110010.2590.29725.28.7371.7
knot102412800.401200000.0330.0099.32.5401.5
spring102412800.42500000.1500.06621.110.5331.2
sponge102412800.089600000.2490.09615.611.3131.9
gear102412800.002554001050.6560.17117.511.911.0
mug10241280.0020.531300000.0770.08224.08.2401.3
mug102425500.591320000.0720.12124.08.2461.4
bowl10241280.0020.461200000.1460.03527.712.5351.3
bowl102425500.621200000.1280.04227.712.5421.4
dino10241280.0021.2759223010.1840.28912.32.7271.3
dino102425501.3567140000.1810.19011.02.6311.4
horse10241280.0021.052140000.1360.13810.12.7251.3
horse102425501.252320000.1180.14110.22.7281.4
shoe10241280.0022.00571987814130.2220.36814.56.2331.3
shoe102425503.1516420000.1450.10715.27.9402.0
dress10241280.0021.524170000.5460.15812.53.5341.4
dress102425502.044310000.4750.12112.53.6381.5
pan_holes10241280.0021.18563111110.2680.30124.98.1351.5
pan_holes102425501.781152311110.2260.30725.28.7422.3
knot10241280.0020.401200000.0320.0129.32.5381.5
knot102425500.411200000.0330.0149.32.5491.6
spring10241280.0020.42500000.1510.02821.010.5331.2
spring102425500.48500000.1490.02221.010.5361.2
sponge10241280.0020.089600000.2450.07715.611.3141.9
sponge102425500.109600000.2450.05115.511.3172.1
gear10241280.0020.002566001050.6630.17317.511.911.0
gear102425500.00221000320.4410.15017.912.111.1

The query: ring vertices NT and the z rule, at 1024 layers, N 128

table: inside at the two neighbouring layer centres decides where they agree, else each part's two rings blended at the query height; nearest: the ring set of the nearest layer centre, one parity per part (height error at most half a layer).

objectNTrulenear wrong
> 0.1 %
> 0.2 %uniform
> 0.1 %
near wrong
all, %
near
ns/pt
uniform
ns/pt
mug256table1000.09017.86.3
mug256nearest1000.1478.93.5
mug512table0000.08023.98.1
mug512nearest0000.13912.14.6
mug1024table0000.07926.48.8
mug1024nearest0000.13713.45.1
bowl256table0000.18220.18.8
bowl256nearest0000.25610.24.5
bowl512table0000.14427.712.5
bowl512nearest0000.22013.96.3
bowl1024table0000.14130.613.8
bowl1024nearest0000.21515.47.0
dino256table10100.19610.83.0
dino256nearest13200.3316.31.6
dino512table10100.18910.92.6
dino512nearest13200.3257.41.7
dino1024table10100.18813.82.8
dino1024nearest13200.3239.21.8
horse256table3000.1547.82.3
horse256nearest4100.2445.01.5
horse512table2000.1329.72.5
horse512nearest4100.2246.51.6
horse1024table2000.12912.22.7
horse1024nearest4100.2227.91.8
shoe256table7020.22113.67.5
shoe256nearest7020.2898.74.8
shoe512table3000.18215.27.9
shoe512nearest3000.25810.45.2
shoe1024table3000.18117.48.6
shoe1024nearest3000.25612.05.7
dress256table13000.58310.53.3
dress256nearest8000.6135.71.8
dress512table3000.53311.13.0
dress512nearest2000.5707.42.0
dress1024table3000.52914.53.5
dress1024nearest2000.5709.62.2
pan_holes256table331310.29120.37.6
pan_holes256nearest301110.42811.14.8
pan_holes512table311310.25525.28.7
pan_holes512nearest281110.39514.15.8
pan_holes1024table311310.25730.99.9
pan_holes1024nearest291110.39317.36.6
knot256table0000.0338.32.5
knot256nearest0000.1225.31.5
knot512table0000.0339.32.5
knot512nearest0000.1226.21.4
knot1024table0000.0339.82.3
knot1024nearest0000.1226.81.5
spring256table0000.16313.06.7
spring256nearest0000.2926.43.6
spring512table0000.14621.110.5
spring512nearest0000.29210.65.5
spring1024table0000.14334.617.2
spring1024nearest0000.29117.38.9
sponge256table0000.25015.111.1
sponge256nearest0000.25010.27.4
sponge512table0000.25115.611.3
sponge512nearest0000.25111.37.9
sponge1024table0000.25215.811.4
sponge1024nearest0000.25211.68.1
gear256table37447346301.16612.58.3
gear256nearest37447346301.1666.34.2
gear512table54701050.65717.511.9
gear512nearest54701050.6578.86.0
gear1024table1700410.62620.613.7
gear1024nearest1700410.62610.36.9

2. Everyday objects

2a. Inside-outside point queries

Near the surface (±2 % of the diagonal), 1M points

methoddevicemug
ns/pt
bowl
ns/pt
dino
ns/pt
shoe
ns/pt
horse
ns/pt
dress
ns/pt
geo-mean
ns/pt
× vs
Open3D
build ms
(mean)
wrong vs
mesh %
wrong vs
decoded %
FCP table (CUDA)gpu12.1147.4910.46.527.799.365.71890.29
FCP table (CPU)cpu28.226.228.435.721.422.826.72.01890.29
Open3D occupancy (Embree)cpu56.155.548.45540.567.553.21.00.7020.00
Warp BVH winding number (CUDA)gpu1131602182621548272260.28.250.00
libigl fast winding numbercpu8637146099425475976980.131.90.00

Uniform in the box padded by 10 %, 1M points

methoddevicemug
ns/pt
bowl
ns/pt
dino
ns/pt
shoe
ns/pt
horse
ns/pt
dress
ns/pt
geo-mean
ns/pt
× vs
Open3D
build ms
(mean)
wrong vs
mesh %
wrong vs
decoded %
FCP table (CUDA)gpu4.76.3925.891.8423.38.71890.04
FCP table (CPU)cpu23.115.921.224.323.319.721.11.41890.04
Open3D occupancy (Embree)cpu32.832.820.932.422.435.628.91.00.7020.00
libigl fast winding numbercpu3772681153851712672430.131.90.00
Warp BVH winding number (CUDA)gpu1202422212471611.03e+032530.18.250.00

Small batches: near the surface, 10k points

methoddevicemug
ns/pt
bowl
ns/pt
dino
ns/pt
shoe
ns/pt
horse
ns/pt
dress
ns/pt
geo-mean
ns/pt
× vs
Open3D
build ms
(mean)
wrong vs
mesh %
wrong vs
decoded %
FCP table (CUDA)gpu13.817.214.215.710.913.614.15.81890.280.38
FCP table (CPU)cpu25.224.824.434.918.260042.41.91890.280.38
Open3D occupancy (Embree)cpu85.391.178.487.263.990.282.11.00.7020.000.43
Warp BVH winding number (CUDA)gpu1251542112501628672290.48.250.000.43
trimesh contains (Embree)cpu2.08e+031.92e+031.71e+032.22e+031.52e+032.9e+032.01e+030.080.70.000.43
libigl fast winding numbercpu1.41e+031.57e+031.83e+032.3e+031.39e+031.49e+042.4e+030.031.90.000.43

Where FCP is wrong (near-surface points in a thin slab; red = FCP disagrees with the mesh)

2b. Mesh-mesh collision

methoddevicemug-bowl
µs/pose
dino-horse
µs/pose
shoe-horse
µs/pose
mug-mug
µs/pose
dress-horse
µs/pose
geo-mean
µs/pose
× vs
Coal
build ms
(mean)
accuracy
vs mesh %
missedfalseaccuracy vs
decoded %
FCP stride 4x4 (CUDA)gpu3.961.21.3231.081.829.7401.599.7129099.74
FCP stride 2x2 (CUDA)gpu5.342.252.53.551.912.896.1401.599.8911099.92
FCP stride 4x4 (CPU)cpu2.685.315.222.983.783.844.6401.599.7129099.74
FCP stride 1x1 (CUDA)gpu8.748.037.265.435.766.932.5401.599.964099.97
FCP stride 2x2 (CPU)cpu8.2716.1135.4410.9101.8401.599.8911099.92
FCL (BVH, tri-tri)cpu23.511.513.120.112.815.61.1358.2100.000099.93
Coal (BVH, tri-tri)cpu25.412.814.723.515.117.61.0260.4100.000099.93
Warp (GPU BVH, tri-tri)gpu2115.427.319.8224330.54.236100.000099.93
FCP stride 1x1 (CPU)cpu2853.537.220.135.433.10.5401.599.964099.97
PyBullet (V-HACD + GJK)cpu3371571843571592230.10.0611998.70013098.65

mug-bowl: 8454 / 14030 faces, 66 % of 2000 poses collide; dino-horse: 19690 / 13840 faces, 32 % of 2000 poses collide; shoe-horse: 24314 / 13840 faces, 39 % of 2000 poses collide; mug-mug: 8454 / 8454 faces, 62 % of 2000 poses collide; dress-horse: 444020 / 13840 faces, 31 % of 2000 poses collide

How wrong are the misses

Example poses (decoded meshes for display; blue = A fixed, orange = B placed)

3. Concave, interleaved and perforated shapes

The shapes. Real: pan_holes (ObjectFolder 73, a perforated pan, genus 334). Synthetic (make_shapes.py): a trefoil knot tube (several rings per layer, crossings on every ray), a coil spring of 8 turns (16 rings per layer, a deep open concavity), a level-2 Menger sponge (square holes along every axis, genus 72) and a 24-tooth gear with a bore (sharp corners: harmonics far beyond N). Pairs: each with itself, pan–mug and knot–spring. The BVH methods slow down most here (Warp up to 680 ns per point on the spring, libigl up to 1.8 µs on the gear and pan); FCP stays at 6–14 ns.

3a. Inside-outside point queries

Near the surface (±2 % of the diagonal), 1M points

methoddevicepan_holes
ns/pt
knot
ns/pt
spring
ns/pt
sponge
ns/pt
gear
ns/pt
geo-mean
ns/pt
× vs
Open3D
build ms
(mean)
wrong vs
mesh %
wrong vs
decoded %
FCP table (CUDA)gpu14.26.1210.611.38.829.844.73050.35
FCP table (CPU)cpu32.427.522.448.715.627.31.73050.35
Open3D occupancy (Embree)cpu48.850.457.949.831.346.71.00.290.00
Warp BVH winding number (CUDA)gpu16929368313342.21800.32.340.00
libigl fast winding numbercpu1.55e+034585631.5e+031.76e+031.01e+030.010.70.00

Uniform in the box padded by 10 %, 1M points

methoddevicepan_holes
ns/pt
knot
ns/pt
spring
ns/pt
sponge
ns/pt
gear
ns/pt
geo-mean
ns/pt
× vs
Open3D
build ms
(mean)
wrong vs
mesh %
wrong vs
decoded %
FCP table (CUDA)gpu6.081.595.648.646.024.916.63050.08
FCP table (CPU)cpu21.422.421.734.212.621.41.53050.08
Open3D occupancy (Embree)cpu31.425.739.245.324.932.41.00.290.00
Warp BVH winding number (CUDA)gpu14637859910539.41690.22.340.00
libigl fast winding numbercpu1.07e+031572371.37e+031.5e+036060.110.70.00

Small batches: near the surface, 10k points

methoddevicepan_holes
ns/pt
knot
ns/pt
spring
ns/pt
sponge
ns/pt
gear
ns/pt
geo-mean
ns/pt
× vs
Open3D
build ms
(mean)
wrong vs
mesh %
wrong vs
decoded %
FCP table (CUDA)gpu209.351215.911.513.35.63050.340.46
FCP table (CPU)cpu3618.420.137.715.623.93.13050.340.46
Open3D occupancy (Embree)cpu77.881.893.280.349.574.91.00.290.000.50
Warp BVH winding number (CUDA)gpu19527663218443.41930.42.340.000.50
trimesh contains (Embree)cpu2.16e+031.66e+032.57e+032.31e+031.08e+031.87e+030.0220.000.50
libigl fast winding numbercpu2.66e+031.91e+033.37e+031.6e+031.85e+032.19e+030.010.70.000.50

Where FCP is wrong (near-surface points in a thin slab; red = FCP disagrees with the mesh)

3b. Mesh-mesh collision

methoddevicespring-spring
µs/pose
knot-knot
µs/pose
sponge-sponge
µs/pose
gear-gear
µs/pose
pan_holes-mug
µs/pose
knot-spring
µs/pose
geo-mean
µs/pose
× vs
Coal
build ms
(mean)
accuracy
vs mesh %
missedfalseaccuracy vs
decoded %
FCP stride 4x4 (CUDA)gpu3.041.34.641.441.922.992.3110.0560.299.5455099.62
FCP stride 4x4 (CPU)cpu0.6443.777.630.8025.461.942.329.9560.299.5455099.62
FCP stride 2x2 (CUDA)gpu3.682.496.811.993.474.33.516.6560.299.8024099.87
FCP stride 1x1 (CUDA)gpu4.478.217.52.8810.27.067.143.2560.299.9111099.95
FCP stride 2x2 (CPU)cpu1.7913.722.82.6215.477.353.1560.299.8024099.87
FCL (BVH, tri-tri)cpu24.515.833.615.320.918.820.71.1175.6100.000099.89
Coal (BVH, tri-tri)cpu2616.542.316.226.219.823.11.0130.6100.000099.89
Warp (GPU BVH, tri-tri)gpu46.723.213.811.444.929.824.70.93.414100.000099.89
FCP stride 1x1 (CPU)cpu6.350.174.19.5246.524.625.20.9560.299.9111099.95
PyBullet (V-HACD + GJK)cpu4911837152962942833420.10.0551898.73015298.62

spring-spring: 76796 / 76796 faces, 77 % of 2000 poses collide; knot-knot: 28800 / 28800 faces, 52 % of 2000 poses collide; sponge-sponge: 1300 / 1300 faces, 75 % of 2000 poses collide; gear-gear: 992 / 992 faces, 76 % of 2000 poses collide; pan_holes-mug: 16092 / 8454 faces, 54 % of 2000 poses collide; knot-spring: 28800 / 76796 faces, 59 % of 2000 poses collide

How wrong are the misses

Example poses (decoded meshes for display; blue = A fixed, orange = B placed)

4. How far from the surface can an answer be wrong?

A threshold you can rely on. For every setting, ε is the largest distance from the true surface at which an answer was still wrong: over 11 objects × 2M points (±2 % of the diagonal of the surface, and uniform in the box) for points, and over 11 pairs × 2000 poses near contact for collisions (the true depth of every colliding pose and the gap of every free one, from the meshes). Beyond ε every answer was right. Empirical, not a proof: it holds on these objects.
layersNNTmax MBpoints ε
% diag
points ε
w/o gear
missed contact ε
sampling 1
sampling 2sampling 4missed ε s1
w/o gear
false hit ε
sampling 1
near
ns/pt (median)
µs/pose s1
(median)
128321280.160.730.680.620.632.550.300.244.73.83
128322560.160.680.680.620.622.550.300.446.14.32
128325120.160.680.680.620.622.550.300.448.05.25
128641280.290.670.670.621.042.310.300.244.73.81
128642560.290.670.670.470.622.310.300.246.14.30
128645120.290.670.670.470.622.310.300.248.14.77
1281281280.490.710.640.621.042.310.300.244.73.83
1281282560.490.640.640.441.042.310.300.246.14.31
1281285120.490.640.640.440.622.310.300.248.64.78
256321280.280.740.510.620.632.550.270.064.54.17
256322560.280.600.500.620.632.550.270.446.04.98
256325120.280.570.500.620.622.550.270.449.05.20
256641280.510.620.360.620.622.310.080.054.54.19
256642560.510.360.360.470.622.310.080.056.04.99
256645120.510.360.360.470.622.310.080.059.15.22
2561281280.860.720.400.621.042.310.080.054.54.17
2561282560.860.370.370.081.042.310.080.056.14.91
2561285120.860.370.370.440.622.310.080.059.15.21
512321280.480.730.460.621.042.550.270.065.05.11
512322560.480.600.530.620.632.550.270.446.35.17
512325120.480.570.530.620.632.550.270.449.75.90
512641280.840.620.370.620.622.310.030.034.75.11
512642560.840.370.370.470.622.310.030.036.35.19
512645120.840.370.370.470.622.310.030.0310.15.93
5121281281.380.720.380.621.042.310.050.034.75.11
5121282561.380.380.380.440.622.310.050.036.35.19
5121285121.380.380.380.440.622.310.050.0310.15.95
1024321280.770.730.480.620.632.550.270.065.86.24
1024322560.770.600.480.620.632.550.270.446.66.30
1024325120.770.570.480.620.632.550.270.4410.96.54
1024641281.330.620.340.620.622.310.030.025.26.27
1024642561.330.350.340.470.622.310.030.026.66.32
1024645121.330.340.340.470.622.310.030.0210.86.58
10241281282.180.720.350.621.042.310.050.007.16.25
10241282562.180.320.320.440.622.310.050.007.96.32
10241285122.180.320.320.440.622.310.030.0010.76.62

Sampling s: collision samples every s/128 of the height and every s·NT/128-th ring vertex. Raw: bound_points.csv, bound_collide.csv (per object / pair); scripts/bound.py.

5. 512 layers against 1024 layers

setFCP queryunittime
1024 layers
time
512 layers
wrong % / accuracy %
1024 layers
wrong % / accuracy %
512 layers
everydaypoints near, (CUDA)ns/pt9.369.540.2910.497
everydaypoints near, (CPU)ns/pt26.7575.880.2910.497
everydaypoints uniform, (CUDA)ns/pt3.303.290.0390.064
everydaypoints uniform, (CPU)ns/pt21.0540.260.0390.064
everydaycollision, sampling 1x1 (CPU)µs/pose33.0775.6899.96099.950
everydaycollision, sampling 1x1 (CUDA)µs/pose6.935.9799.96099.950
everydaycollision, sampling 2x2 (CPU)µs/pose10.0524.4599.89099.900
everydaycollision, sampling 2x2 (CUDA)µs/pose2.892.9299.89099.900
everydaycollision, sampling 4x4 (CPU)µs/pose3.849.5899.71099.730
everydaycollision, sampling 4x4 (CUDA)µs/pose1.821.6499.71099.730
complexpoints near, (CUDA)ns/pt9.849.430.3480.499
complexpoints near, (CPU)ns/pt27.3275.120.3480.499
complexpoints uniform, (CUDA)ns/pt4.916.010.0800.116
complexpoints uniform, (CPU)ns/pt21.4053.530.0800.116
complexcollision, sampling 1x1 (CPU)µs/pose25.1847.5599.90899.892
complexcollision, sampling 1x1 (CUDA)µs/pose7.146.6999.90899.892
complexcollision, sampling 2x2 (CPU)µs/pose7.3517.9199.80099.750
complexcollision, sampling 2x2 (CUDA)µs/pose3.513.5499.80099.750
complexcollision, sampling 4x4 (CPU)µs/pose2.327.7599.54299.567
complexcollision, sampling 4x4 (CUDA)µs/pose2.312.2699.54299.567

Points: wrong % of 1M points against the mesh (all distances); collision: agreement with the exact mesh test. Raw: points*_L512.csv, collide*_L512.csv.

6. Where the remaining errors are

What is left, at the chosen setting. Each slice figure: the mesh slice (black), the descriptor's rings at that layer (blue), and the near-surface points answered wrongly from ±2 layers around it, coloured by their distance to the true surface (darker is farther; filled: false inside, open: false outside), with the two densest groups zoomed (the bar is 0.1 % of the diagonal). Three kinds remain: The dog of v1 (a scanned plush toy whose whole surface is 0.1–0.3 %-of-the-diagonal bumps) is in v1 and slices/3d_cmp_dog.png.

3d dino

3d pan holes

dino

gear

pan holes

shoe

7. Found on the way

In the package (reported, not changed here): In this study's query (fixed): blending each part's rings between two layer centres morphed unrelated shapes where two tracks merge (the dropped track stopped half way, the continuing one changed shape): slabs one layer thick read empty, up to 1.8 % of the diagonal deep. The nearest-centre rule has no such case. Ground truth and meshes were checked: every mesh watertight with consistent winding; Open3D's 3-ray occupancy and libigl's winding number agree on every point.

Files

Scripts in ../scripts: fcpq.py (the descriptor queries), sweep.py and rule_cmp.py (section 1), bound.py (section 4), points_bench.py and collide_bench.py (FCP_SET=complex for section 3), slice_errors.py and render3d.py (section 5), lowmem.py (the encoder's selection without its padded array), common.py (the setting), make_shapes.py, renders.py, report.py. Raw numbers: sweep.csv, rule_cmp.csv, points*.csv, collide*.csv; logs alongside. Earlier: v1/, a6144/, a2048/.