Resources Numerical accuracy Calculation policy

Scale feature for printing

Scales a body by a factor per axis about a chosen point, including a shrinkage allowance given as a percent.

How the precision is kept

The factors are 64-bit numbers taken from the feature, or from the shrinkage percent through shrinkFactor in scale-factors.js. The scale is stored as a 4 × 4 matrix whose translation is a × (1 − f), and each point is then one product and one addition per axis.

Calculations under this policy

3 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.

print-features.js PartCraft 0.8.1 64-bit 3 calculations Reviewed

Tests tools/partcraft-test/print-features08.mjs

Scale factors

64-bit

The three factors a scale feature multiplies by, from the feature or from its shrinkage allowance.

With a shrinkage allowance each factor comes from shrinkFactor (scale-factors.js) and falls back to 1 when that returns nothing; otherwise the stored factors are copied unchanged.

  • print-features.js
  • lines 35 to 39
  • PartCraft 0.8.1
export function scaleFactors(f) {
  const s = f.shrink;
  if (s && Array.isArray(s.pct)) return s.pct.map((p) => shrinkFactor(p, s.mode) ?? 1);
  return f.factors.slice();
}

Scale matrix about a fixed point

64-bit

The 4 × 4 matrix that scales a body about the center, minimum corner, base center or origin of its box.

a = (min + max) ÷ 2 (center); translation = a × (1 − f)

The center is one addition and a halving per axis. The translation multiplies a by 1 − f; for a factor between 0.5 and 2 that subtraction is exact in binary floating point, so only the product rounds.

  • print-features.js
  • lines 41 to 47
  • PartCraft 0.8.1
export function scaleMatrix(box, f) {
  const fs = scaleFactors(f);
  const a = f.about === 'origin' ? [0, 0, 0] : f.about === 'min' ? box.min.slice()
    : f.about === 'base' ? [(box.min[0] + box.max[0]) / 2, (box.min[1] + box.max[1]) / 2, box.min[2]] : [0, 1, 2].map((i) => (box.min[i] + box.max[i]) / 2);
  // x' = f·(x − a) + a  →  column-major 4×4 with the translation a·(1 − f)
  return [fs[0], 0, 0, 0, 0, fs[1], 0, 0, 0, 0, fs[2], 0, a[0] * (1 - fs[0]), a[1] * (1 - fs[1]), a[2] * (1 - fs[2]), 1];
}

Scaled triangles

64-bit

Applies the scale to every corner of a body's triangles, with the box found from the triangles themselves.

x′ = f_x·x + t_x; y′ = f_y·y + t_y; z′ = f_z·z + t_z

One 64-bit product and one addition per coordinate, using only the diagonal and the translation of the matrix. The box is the plain minimum and maximum of the corners.

  • print-features.js
  • lines 50 to 56
  • PartCraft 0.8.1
export function scaleTris(tris, f) {
  const min = [Infinity, Infinity, Infinity], max = [-Infinity, -Infinity, -Infinity];
  for (const tr of tris) for (const p of corners(tr)) for (let i = 0; i < 3; i++) { if (p[i] < min[i]) min[i] = p[i]; if (p[i] > max[i]) max[i] = p[i]; }
  const m = scaleMatrix({ min, max }, f);
  const at = (p) => [m[0] * p[0] + m[12], m[5] * p[1] + m[13], m[10] * p[2] + m[14]];
  return tris.map((tr) => (Array.isArray(tr) ? tr.map(at) : { ...tr, p: tr.p.map(at) }));
}

In detail

Method
x′ = f·(x − a) + a written as x′ = f·x + a·(1 − f); a is the box center, its minimum corner, the center of its base or the origin.
Number types
64-bit numbers in plain arrays.
Tolerances
A factor read from a file is accepted between 1e-6 and 1e6; a shrinkage percent from 0 to under 50.
Limits
The fixed point is not reproduced bit for bit: a point at a becomes f·a + a·(1 − f), two rounded products added.

Approach: 64-bit, direct

Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.

Every policy that takes this approach