Horse decoded from one FCP encode (tracked-fit, 256 layers) at 16 to 8192 atoms and the full 55446 atoms, beside the ground truth, with Chamfer and p95 errors

Approximate collision and inside-outside on the CPU, straight from the descriptor

Package v5.5 (commit 7ccd2a39, parts="tracked-fit", interp="poly"). 12 objects (household: mug, mug ace, bowl, bowl serving, bowl dog, flower pot, shoe, jogger; figurines: dino, horse; synthetic: spring, knot; 4.6k–77k faces; the household objects and figurines from Google Scanned Objects), encoded at 128, 256 and 512 layers (N 128, M 64, 2048 samples per ring, small islands and one-layer pieces kept where the package drops them, every atom, cut at 1e-8 of the energy) and queried without decoding: a point is inside when the rings at its nearest layer centre contain it (512 vertices per ring); two placed objects collide when a ring vertex of one lies inside the other. CPU: Intel(R) Core(TM) i5-10400F CPU @ 2.90GHz, FCP in numba with 12 threads and with 1. References on the same CPU: Embree (Open3D RaycastingScene occupancy, its own threads) for points, FCL (BVH, triangle-triangle, one call per pose through python-fcl, 1 thread) for collision. The error bound is the largest distance from the true surface at which an answer was still wrong: over 1M points within ±2 % of the diagonal of the surface plus 1M uniform points per object, and over 2000 poses near contact per pair (11 pairs; the true depth or gap of every pose from the meshes). Beyond it, every answer was right.

layerssize, MB median (max)points: error bound, % diagpoints: wrong, % (±2 % band) points: FCP, ns/point (12 threads)points: FCP, ns/point (1 thread)points: Embree, ns/point collision: deepest miss, % diagcollision: widest false hit, % diagcollision: correct, % of poses collision: FCP, µs/pose (12 threads)collision: FCP, µs/pose (1 thread)collision: FCL, µs/pose
1280.20 (0.55)0.551.2527.921252.40.150.1799.8622.712718.4
2560.27 (0.99)0.340.6424.620052.40.000.0599.9424.013218.4
5120.37 (1.76)0.290.3725.920452.40.000.0399.9524.514218.4

Points: FCP answers in 24.6–27.9 ns per point with 12 threads, 1.9× faster than Embree on the same CPU, at every layer count. The error bound is 0.29–0.55 % of the diagonal; its worst case comes from a handful of points at sharp details and pieces that start or end between two slices (below), and it falls with the layers; the share of wrong answers near the surface falls 1.25 → 0.37 %. Collision: no missed contact deeper than 0.15 % of the diagonal and no false one wider than 0.17 % at 128 layers (0.05 % from 256 layers); 99.86–99.95 % of poses correct. On the CPU, FCP is not faster than FCL at this sampling: 22.7–24.5 µs per pose with 12 threads and 127–142 µs with 1, against 18.4 µs for FCL on one thread; with every other sample FCP takes 6.2–7.0 µs (99.86–99.92 % correct, misses up to 0.62 %). FCP's collision speed-up is a GPU result (the poses in one batch).

Error bound (left axis, % of the diagonal) and CPU time (right axis, log) against
the number of layers, for inside-outside points (a) and collision (b), FCP against Embree and FCL.
Error bound (left axis) and CPU time (right axis, log) against the layers; times are geometric means over the 12 objects or 11 pairs, bounds the worst case.
Wrong inside-outside answers on the mug, the dino and the jogger at 256 layers
Inside-outside (256 layers): every point answered wrongly farther than 0.1 % of the diagonal from the surface (of 2M per object), coloured by that distance. They sit where a piece starts or ends between two slices: the mug's cavity floor and handle joints, the dino's claws, toes and crest, the jogger's ridged sole.
Three wrong collision answers at 256 layers, zoomed on the contact
Collision (256 layers, densest sampling): the worst wrong pose of three pairs, zoomed on the contact (ringed). A false contact is an FCP sample found inside the other object while the meshes are a few hundredths of a percent of the diagonal apart; the missed contact is a touch 0.0035 % deep that falls between the samples.

Solid properties straight from the descriptor

The 12 objects of the collision section; CPU: Intel(R) Core(TM) i5-10400F CPU @ 2.90GHz, one thread. Volume from the stored atoms in one contraction (no rings, no grid); convex hull from the rings (prototype, not in the package). Volume error against the source mesh's volume (volume rows), its convex hull's volume (hull rows), or the reference bound's volume (box and sphere rows; the sphere's from its radius; trimesh's minimum sphere is up to 0.4 % too large and slower, so the exact ball is the reference); the boxes and the sphere are taken from the vertices of the descriptor's hull (M = 64), and their FCP time includes that hull; the hull gap from 20k points on the true hull to the descriptor's hull. The 6144-atom runs from points ran at a load average of about 7: their times are upper bounds.

propertylayersatomsdirections MFCP, ms median (range)reference on the source meshreference, ms median (range)decode + reference, ms medianFCP faster than the referencevolume error, % median (worst)hull surface gap, % diag: median of means (max)farthest mesh vertex outside the bound, % diag (max)
volume (encoded from the mesh)1281024–0.028 (0.019–0.046)exact mesh volume (divergence theorem, numba)0.044 (0.014–0.235)30910 / 12-0.47 (-0.88)––
volume (encoded from the mesh)1282048–0.038 (0.023–0.061)exact mesh volume (tetrahedron sum, numba)0.052 (0.027–0.235)–8 / 12-0.31 (-0.74)––
volume (encoded from the mesh)1286144–0.042 (0.024–0.063)exact mesh volume (divergence theorem, numba)0.044 (0.014–0.235)3546 / 12-0.17 (-0.74)––
volume (encoded from the mesh)2566144–0.044 (0.025–0.084)exact mesh volume (divergence theorem, numba)0.044 (0.014–0.235)14013 / 12-0.13 (-0.71)––
volume (encoded from 20k points)1282048–0.032 (0.020–0.057)exact mesh volume (tetrahedron sum, numba)0.052 (0.027–0.235)–9 / 12-0.16 (+1.76)––
volume (encoded from 20k points)1286144–0.041 (0.020–0.069)exact mesh volume (tetrahedron sum, numba)0.052 (0.027–0.235)–8 / 12-0.15 (+1.82)––
volume (encoded from 20k points)2566144–0.046 (0.019–0.061)exact mesh volume (tetrahedron sum, numba)0.052 (0.027–0.235)–9 / 12-0.16 (+1.87)––
convex hull1281024643.8 (2.6–13.9)qhull on the mesh's vertices4.0 (2.1–16.3)1166 / 12-0.95 (-3.88)0.166 (1.31)–
convex hull1286144644.0 (2.3–13.9)qhull on the mesh's vertices4.0 (2.1–16.3)1077 / 12-1.04 (-3.78)0.131 (1.21)–
convex hull12861441285.8 (4.8–26.7)qhull on the mesh's vertices4.0 (2.1–16.3)1071 / 12-0.66 (-3.52)0.115 (1.14)–
convex hull25661441287.9 (6.2–51.5)qhull on the mesh's vertices4.0 (2.1–16.3)4070 / 12-0.46 (-3.51)0.110 (1.14)–
axis-aligned box1282048642.7 (2.0–13.9)the mesh vertices' min / max0.3 (0.1–1.8)–0 / 12+0.06 (-2.16)–0.52
axis-aligned box1286144642.7 (2.0–13.8)the mesh vertices' min / max0.3 (0.1–1.8)–0 / 12-0.20 (-2.13)–0.52
axis-aligned box2566144644.5 (3.5–26.0)the mesh vertices' min / max0.3 (0.1–1.8)–0 / 12-0.34 (-2.28)–0.53
oriented box1282048646.3 (5.1–18.6)minimum-volume OBB (trimesh)79.7 (20.4–150.9)–12 / 12+0.27 (-2.32)–0.49
oriented box1286144646.4 (5.1–18.6)minimum-volume OBB (trimesh)79.7 (20.4–150.9)–12 / 12+0.06 (-2.27)–0.40
oriented box25661446410.5 (7.3–33.9)minimum-volume OBB (trimesh)79.7 (20.4–150.9)–12 / 12+0.28 (-2.28)–0.39
smallest sphere1282048642.8 (2.0–14.2)exact smallest ball (qhull, then the minimum ball)3.9 (2.1–18.2)–10 / 12+0.53 (+2.21)–0.15
smallest sphere1286144642.8 (2.0–14.2)exact smallest ball (qhull, then the minimum ball)3.9 (2.1–18.2)–10 / 12+0.66 (+2.25)–0.13
smallest sphere2566144644.6 (3.5–26.4)exact smallest ball (qhull, then the minimum ball)3.9 (2.1–18.2)–3 / 12+0.11 (-1.71)–0.34

Convex hull straight from the descriptor

Package v5.31 (commit db3e6ceb; the working tree only raises MAX_ATOMS to 12288; defaults: parts="tracked-fit", interp="poly"), the 12 objects of the collision section, encoded at 128 layers (1024 or 6144 atoms) and at 256 layers (6144 atoms). The hull is built from the descriptor without a decode grid, a mesh or qhull: the solid rings are evaluated from the Fourier and Chebyshev coefficients (holes are inside the solid and never touch the hull), each part's end rings are held to the part's outer edges, and per level (a layer centre or a part edge) one numba kernel takes the 2D hull of the ring samples and its support point in M directions. In each direction a hull vertex must lie on the upper concave envelope of (height, support) over the levels; neighbouring directions' envelopes are joined by height and the end levels capped, which gives a closed triangle surface in O(levels × M). Reference: scipy's qhull on the source mesh's vertices, and the route through the decoded mesh (fcp.decode, then qhull). CPU: Intel(R) Core(TM) i5-10400F CPU @ 2.90GHz, one thread for both (median of 21 runs; the decode route of 7). Distances in % of the source mesh's diagonal, from 20k points on the source mesh's hull to the descriptor's hull; volume relative to the source mesh's hull.

layersatomsdirections MFCP hull, ms (median)qhull on the mesh, ms (median) decode + qhull, ms (median)FCP faster than qhullspeed-up, median (min–max) volume / true hull, median (min)gap, mean % diaggap, max % diag non-convex dent, max % diaghulls with no over-shared edge
1281024643.84.01166 / 120.98 (0.45–1.89)0.991 (0.961)0.1621.310.597 / 12
1286144644.04.01077 / 121.06 (0.43–2.36)0.990 (0.962)0.1281.210.718 / 12
12861441285.84.01071 / 120.60 (0.26–1.05)0.993 (0.965)0.1111.140.357 / 12
25661441287.94.04070 / 120.44 (0.15–0.89)0.995 (0.965)0.1041.140.245 / 12

Time: at 128 layers and M = 64 the descriptor's hull takes 2.3–13.9 ms, against 2.1–16.3 ms for qhull on these meshes (2.3k–38k vertices): about even, faster on 7 of 12 (mug, bowl, bowl serving, shoe, jogger, spring, knot), at most 2.36×. The gain is in not having the mesh: the decode route is 69–195 ms, 13–66× slower. qhull's cost grows with the hull it outputs, and the descriptor's hull has more vertices than the mesh's (the rings are dense where the mesh is coarse), so a large source mesh is where the descriptor wins outright (a 222k-vertex dress outside this set: 3.5 ms against 39 ms); the knot and spring, with many parts per level, are the slowest descriptor hulls. M = 128 and 256 layers cost 1.5× and 2.0× the time (medians). Accuracy: the volume is 0.962–0.998 of the true hull at 128 layers, the mean gap 0.05–0.29 % of the diagonal, the largest 1.21 % (mug). Nearly all of it is the encoding, not the hull: the decoded mesh's hull (qhull) is as far from the true hull (mean 0.02–0.25 %, max up to 1.15 %), and the descriptor's hull is within a mean 0.02–0.07 % of it (max 0.18–0.72 %, the spring and knot at the top: 64 directions are too few there; M = 128 lowers their max gap, knot 0.58 → 0.15 %, spring 0.74 → 0.33 % (decoded mesh's hull: 0.09, 0.24 %)). A flat top or bottom also moves: a part ends at a layer edge, so a cap sits up to half a layer from the true one (the mug's top: 0.19 % of the diagonal). 256 layers lower the gap only where the layers were the limit (horse, mug ace, spring); elsewhere it is the rings' own error. Defects: the surface is convex only up to the M directions (dents up to 0.71 % of the diagonal at M = 64, on the spring); and where one pair of support points lies on the envelopes of two non-neighbouring directions, the bands on both sides use that edge: no open edge on any of the 48 hulls, but 0–11 edges shared by four triangles (a fold, not a hole; the volume is unaffected). A final qhull over the descriptor hull's own vertices would remove both (1–6 ms on 500–2400 such vertices in the first trials), giving up most of the gain. Prototype, not in the package.

object (128 layers, 6144 atoms, M = 64)mesh verticesatoms heldFCP hull, ms qhull, msdecode + qhull, msFCP hull verticestrue hull verticesvolume / true gap, mean %gap, max %decoded mesh's hull: gap, mean %its gap, max % FCP hull to decoded hull, mean %
mug422755153.95.018918197920.9920.1371.210.1281.150.040
mug ace561261444.32.8819836100.9980.0540.340.0350.330.025
bowl701725622.35.5149294511510.9620.2850.960.2530.950.034
bowl serving297529452.94.0195318813560.9770.1570.720.1320.710.037
bowl dog230822463.12.910321819530.9920.1770.870.1690.840.045
flower pot388445423.53.26918558810.9920.1350.940.1140.870.046
shoe1213361444.14.113011336010.9810.1400.600.1230.540.032
jogger1004161443.64.010810556830.9840.1280.700.1030.700.031
dino984561444.92.1983482130.9860.1030.520.1160.650.045
horse692261444.92.21035603500.9880.0820.550.0580.580.040
spring38400614413.916.3176276536380.9970.0800.740.0230.240.066
knot1440061447.78.6105465823130.9930.0630.580.0200.090.046
Six objects: the source mesh inside the descriptor's hull (top) and the descriptor's hull coloured by its distance to the mesh's hull (bottom)
Top: the source mesh (grey) inside the hull built from the descriptor (orange edges). Bottom: the descriptor's hull, each triangle coloured by the distance of its centre to the source mesh's hull (0–0.5 % of the diagonal); 128 layers, 6144 atoms, M = 64. The brightest triangles are the flat tops (caps) of the mug and the dog bowl: long fans whose centres lie below the true rim.

Volume straight from the descriptor

Package v5.31 (commit db3e6ceb; the working tree only raises MAX_ATOMS), defaults (parts="tracked-fit", interp="poly", the gwn point slicer), 128 layers, 2048 atoms; the 12 objects of the collision section. The exact volume is the source mesh's (tetrahedra over its faces). From each mesh, 20k area-uniform surface points (seed 3) with the true normals; every point method gets the same points. FCP's volume is one contraction of the stored atoms: a ring's area is π Σk k (ax,kby,k − bx,kay,k) in its Fourier coefficients, and the height atoms are orthonormal on a part's layer centres, so the sum of the ring areas over the layers is the same expression in the atom vectors: V = dz · ∏scale · π Σparts sign · |Σk k (⟨ax,k, by,k⟩ − ⟨bx,k, ay,k⟩)| (the midpoint rule over the layers; no ring is evaluated, no grid, no mesh; one numba loop). Standard route from points: screened Poisson reconstruction (Open3D 0.20, depth 8), then its mesh's volume, with the true normals (the best case) and with normals estimated from the points (what a scan gives). CPU: Intel i5-10400F; FCP's encode on numba's 12 threads and on a GTX 1650; Poisson on one thread (its threaded run was slower here). Times are medians over the objects.

methodvolume error, median |%|max |%|objects within 1 % building the representation, msthe volume, µs
exact: tetrahedron sum over the source meshexactexact12 / 12–51.9
FCP from the mesh (encode 2048 atoms, 128 layers)0.310.7412 / 12124 (87 ms on CUDA)38.0
FCP from 20k points (same encode, no normals)0.251.7611 / 122791 (981 ms on CUDA)32.2
FCP from the mesh (128 layers, 6144 atoms)0.170.7412 / 12111 (63 ms on CUDA)48.8
FCP from 20k points (128 layers, 6144 atoms)0.281.8211 / 122430 (914 ms on CUDA)41.4
FCP from the mesh (256 layers, 6144 atoms)0.130.7112 / 12199 (126 ms on CUDA)48.4
FCP from 20k points (256 layers, 6144 atoms)0.241.8711 / 124237 (1430 ms on CUDA)45.6
screened Poisson (depth 8) from 20k points, true normals0.220.9012 / 121484 (1 thread)6306.4
screened Poisson from 20k points, normals estimated (30 neighbours, tangent-plane orientation)71.62151.842 / 122291 (1 thread, normals included)7526.9

Accuracy: FCP is within 0.74 % of the exact volume from the mesh (median 0.31 %) and within 1.76 % from the points (median 0.25 %), without normals; Poisson with the true normals is as close (median 0.22 %, max 0.90 %). With estimated normals Poisson is off by more than 5 % on 10 of 12 objects (bowl +152 %, bowl serving +145 %, mug +141 %, bowl dog +125 %, flower pot +109 %, mug ace +85 %, shoe +58 %, jogger +19 %, dino +6 %, spring -20 %): on the thin-walled ones the orientation flips across a wall and the surface closes over the opening (renders). More atoms, more layers: 6144 atoms at 128 layers and at 256 layers move the error little: from the mesh the median falls 0.31 → 0.17 → 0.13 % but the worst stays 0.71 %; from the points it is 0.25 / 0.28 / 0.24 %. The per-object errors keep their sign and size across the settings (mugs and bowls 0.2–0.7 % low, the spring from points 1.8 % high): a bias of the encoding and of the point slicer, not a shortage of atoms. The 6144-atom runs had other jobs on the machine (load average about 7, against about 0 for the 2048 run): their times in the first table are upper bounds. Time: once encoded, FCP's volume takes 20–57 µs, about the cost of the tetrahedron sum over the source mesh itself (27–235 µs, numba): faster on 9 of 12 objects, the gain growing with the mesh (spring, 77k faces: 5.1×); the volume of a Poisson mesh takes 3.3–12.7 ms (NumPy). The cost from points is the encode: 2.8 s on the CPU and 1.0 s on CUDA (median), against 1.5 s for Poisson on one thread; the descriptor, once made, also answers the other queries of this report. Caveats: clean, complete, uniformly sampled points; Poisson's meshes are not watertight by Open3D's strict test (the volume is taken as is, its sign ignored); learned reconstruction is not compared.

objectmesh facesFCP from mesh, error % (128 layers, 2048 atoms)FCP from points, error % (128, 2048) from mesh (128, 6144)from points (128, 6144)from mesh (256, 6144)from points (256, 6144) Poisson, true normals, error %Poisson, estimated normals, error %exact volume, µs FCP volume, µs (128, 2048)FCP encode from mesh, ms (CPU; 128, 2048)FCP encode from points, ms (CPU; 128, 2048) FCP encode from points, ms (CUDA; 128, 2048)Poisson, true normals, msPoisson, estimated normals, ms
mug8454-0.53-0.40-0.52-0.40-0.52-0.430.44140.96272982252395114692280
mug ace11224-0.090.00-0.09-0.00-0.010.040.5884.9234351473620131714992276
bowl14030-0.39-0.18-0.39-0.18-0.42-0.190.76151.84793299284099613232076
bowl serving5946-0.49-0.30-0.48-0.30-0.48-0.300.90145.353420117225695611971965
bowl dog4612-0.74-0.37-0.74-0.37-0.71-0.380.44124.9933241173017106914692302
flower pot7768-0.44-0.35-0.44-0.34-0.48-0.330.30108.7144271623251123015312333
shoe24314-0.280.20-0.250.26-0.19-0.080.1058.327939197218978811122889
jogger20090-0.09-0.15-0.06-0.12-0.08-0.13-0.0218.816632121234290127412195
dino19690-0.330.02-0.040.17-0.070.180.156.096057127274296531102815
horse13840-0.10-0.04-0.050.01-0.020.030.120.184446101202471838013940
spring76796-0.101.76-0.041.82-0.021.870.10-20.04235463023092124517784709
knot288000.01-0.310.01-0.300.00-0.29-0.09-0.0886322153629113511132028
Six objects: the original mesh, FCP decoded from the mesh and from 20k points, Poisson with true and with estimated normals, each with its volume error
The surfaces behind the volumes: the original mesh, the FCP descriptor decoded (from the mesh and from 20k points; the volume itself is computed from the atoms, not from this mesh), and Poisson's reconstruction with the true and with estimated normals; under each, the volume error against the exact volume (FCP at 128 layers, 2048 atoms).