Drives one joint through its travel and records positions, speeds, accelerations, clashes and clearances at each step.
How the precision is kept
The joint value, the placements and the tracked positions are 64-bit numbers. Velocity and acceleration are finite differences of the recorded positions, so they depend on the number of steps; the study's own notes say so. Clearances are measured by manifold-3d on solids rebuilt from each part's mesh as a Float32Array, so the reported gaps start from 32-bit vertex coordinates and are distances between facets. The CSV export rounds every number to 5 decimals.
Open item. Build the clearance solids from 64-bit vertex coordinates and write the CSV with full precision.
Calculations under this policy
6 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.
motion.jsPartCraft 0.8.1 32-bit in part6 calculationsReviewed
Tests tools/partcraft-test/test-motion.mjs
Motion laws
64-bit
How far along its travel the joint is at each fraction of the time.
linear: s; ease: s²(3 − 2s); sine: ½ − ½ cos(πs); round trip: 2s or 2 − 2s; value = from + (to − from) × law(k ÷ steps)
The step fraction k ÷ steps is formed from integers at each step, not accumulated. The sine law uses Math.PI; at s = 1 it returns exactly 1 because cos(π) evaluates to −1.
motion.js
lines 27 to 32
PartCraft 0.8.1
export const LAWS = {
linear: (s) => s,
ease: (s) => s * s * (3 - 2 * s), // smoothstep: speed 0 at both ends, 1.5 × the average in the middle
sine: (s) => 0.5 - 0.5 * Math.cos(Math.PI * s), // speed 0 at both ends, π/2 × the average in the middle
'round-trip': (s) => (s < 0.5 ? 2 * s : 2 - 2 * s), // from → to → from at constant speed
};
Default tracked point
32-bit in part
The middle of a part's bounding box, tracked when no point is given.
center = (min + max) ÷ 2 per axis
The box is taken from the part's mesh positions, which are a Float32Array, so the default tracked point has 32-bit precision. A point given by the person is used as given.
motion.js
lines 45 to 52
PartCraft 0.8.1
/** The middle of a part's mesh box, in the part's own coordinates. */
function boxCentre(build) {
const p = build?.mesh?.positions;
if (!p || !p.length) return [0, 0, 0];
const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];
for (let i = 0; i < p.length; i += 3) for (let k = 0; k < 3; k++) { const v = p[i + k]; if (v < lo[k]) lo[k] = v; if (v > hi[k]) hi[k] = v; }
return [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2];
}
Solids for the clearance check
32-bit in part
Rebuilds each part as a solid so the gap between parts can be measured at every step.
Each solid is made from new Float32Array(mesh.positions), and the gap is then read from manifold-3d's minGap on the transformed solids. The reported clearance is therefore a distance between facets with 32-bit vertex coordinates.
motion.js
lines 91 to 93
PartCraft 0.8.1
const mesh = new M.Mesh({ numProp: 3, vertProperties: new Float32Array(b.mesh.positions), triVerts: new Uint32Array(b.mesh.indices) });
mesh.merge();
base.set(i.id, T(new M.Manifold(mesh)));
Velocity and acceleration
64-bit
The rate of the joint value and each tracked point's velocity, speed and acceleration.
Central differences of positions, one-sided at the two ends for velocity. The second difference subtracts nearly equal positions and divides by Δt², so with many steps its rounding grows as the step shrinks. The first and last rows keep an acceleration of zero.
motion.js
lines 129 to 140
PartCraft 0.8.1
const dt = duration / steps;
for (let k = 0; k <= steps; k++) {
const r = rows[k], p = rows[Math.max(0, k - 1)], n = rows[Math.min(steps, k + 1)];
const span = (n.time - p.time) || 1;
r.rate = (n.value - p.value) / span;
r.tracks.forEach((tr, i) => {
const a = p.tracks[i].pos, b = n.tracks[i].pos;
tr.vel = [0, 1, 2].map((q) => (b[q] - a[q]) / span);
tr.speed = Math.hypot(...tr.vel);
if (k > 0 && k < steps) tr.acc = [0, 1, 2].map((q) => (p.tracks[i].pos[q] - 2 * tr.pos[q] + n.tracks[i].pos[q]) / (dt * dt));
});
}
Peak speed, peak acceleration and travel
64-bit
The largest speed and acceleration of each tracked point and the length of its path.
travel = Σ |p_k − p_(k−1)|
The peaks are the largest values among the recorded steps. The travel is the sum of straight chords between steps, which is shorter than a curved path.
Formats each time, value, position, speed and gap written to the CSV file.
text = value.toFixed(5), trailing zeros removed
Every exported number is rounded to 5 decimals in its own unit, so small values keep few significant digits and large ones lose the digits beyond the fifth decimal.
Joint value from a motion law of the step fraction; placement by assembly.js; central differences inside the range and one-sided at the ends; path length as the sum of chords; minimum gap by manifold-3d's minGap.
Number types
64-bit numbers for values, positions and derivatives; Float32Array for the vertices of the solids used for clearance.
Tolerances
A pair is listed when its gap is under the search length minus 1e-9. Steps limited to 400, pairs to 60, tracks to 12.
Limits
Peak speed and acceleration are the largest sampled values. The acceleration of the first and last step is left at zero, although a comment in the code says it copies the neighbor's. The path length under-reads a curved path between steps.
Approach: 32-bit step, flagged
One step stores or exchanges 32-bit values that reach a number a person reads. It is listed here with its open item, and the step is either moved to 64-bit or the app will warn where the number is read.