Resources Numerical accuracy Calculation policy

Magnetics add-on: the result drawn as display bodies

Draws a magnet study's field bands, flux lines, bodies and force arrows in the part's view.

How the precision is kept

Everything made here is a display body handed to api.model.addBodies; the file's comment says they are never part of the part's solid. Positions are computed in 64-bit from the 2D mesh and the plane's frame, then rounded to 0.001 mm by addon-bodies.js. Band limits are the color scale's top split in ten.

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.

addons/magnetics/lib/bodies.js PartCraft 0.8.1 32-bit, display only 3 calculations Reviewed

Tests tools/addons-test/browser-magnetics.mjs

Frame of the study's plane

32-bit, display only

The origin and axes of the plane the result is drawn in.

x = (helper × n) ÷ |helper × n|; y = n × x

The helper axis switches at |n_y| ≥ 0.9, so the cross product is not near zero. It only places display bodies.

  • addons/magnetics/lib/bodies.js
  • lines 26 to 33
  • PartCraft 0.8.1
export function frameOf(plane) {
  if (!plane || plane.kind === 'base') return BASE[plane?.name || 'front'] || null;
  if (plane.kind === 'fixed' && Array.isArray(plane.o) && Array.isArray(plane.n)) {   // the same basis as vec.js frameFor: x ⟂ n, y = n × x
    const n = unit(plane.n), helper = Math.abs(n[1]) < 0.9 ? [0, 1, 0] : [1, 0, 0], x = unit(cross(helper, n)), y = cross(n, x);
    return { o: plane.o.slice(), x, y, n };
  }
  return null;
}

Triangle cut to the crop rectangle

32-bit, display only

Cuts a field triangle to the rectangle that is shown.

Each cut point is a linear interpolation along an edge; the division only runs when the edge's ends are on opposite sides of the cut line, so it is not zero.

  • addons/magnetics/lib/bodies.js
  • lines 35 to 53
  • PartCraft 0.8.1
/** Sutherland–Hodgman: a convex polygon cut to the rectangle [x0, y0, x1, y1]. */
export function clipRect(poly, [x0, y0, x1, y1]) {
  let out = poly;
  for (const [inside, cut] of [
    [(p) => p[0] >= x0, (a, b) => [x0, a[1] + (b[1] - a[1]) * (x0 - a[0]) / (b[0] - a[0])]],
    [(p) => p[0] <= x1, (a, b) => [x1, a[1] + (b[1] - a[1]) * (x1 - a[0]) / (b[0] - a[0])]],
    [(p) => p[1] >= y0, (a, b) => [a[0] + (b[0] - a[0]) * (y0 - a[1]) / (b[1] - a[1]), y0]],
    [(p) => p[1] <= y1, (a, b) => [a[0] + (b[0] - a[0]) * (y1 - a[1]) / (b[1] - a[1]), y1]],
  ]) {
    const inp = out; out = [];
    for (let i = 0; i < inp.length; i++) {
      const a = inp[i], b = inp[(i + 1) % inp.length], ia = inside(a), ib = inside(b);
      if (ia) out.push(a);
      if (ia !== ib) out.push(cut(a, b));
    }
    if (!out.length) break;
  }
  return out;
}

Field color bands

32-bit, display only

The color band each triangle falls in.

band = min(9, floor(|B| ÷ top × 10))

top is the given scale top, else the largest |B|, else 1. The band picks a color; its limits top × k ÷ 10 go into the body's name rounded to 3 decimals.

  • addons/magnetics/lib/bodies.js
  • lines 71 to 72
  • PartCraft 0.8.1
const top = top0 > 0 ? top0 : (res.B.mag.reduce((a, b) => (b > a ? b : a), 0) || 1);
const band = new Uint8Array(m); for (let e = 0; e < m; e++) band[e] = Math.min(BANDS - 1, Math.floor(res.B.mag[e] / top * BANDS));

In detail

Method
Bands by floor(|B| ÷ top × 10); triangles cut to the crop rectangle by Sutherland–Hodgman; a plane frame from the sketch plane's normal by two cross products.
Number types
64-bit numbers in plain arrays; |B| from a Float32Array.
Limits
Band names carry their limits rounded to 3 decimals and force arrows their force to 4 significant digits.

Approach: Display only

Computed to draw the screen, often in 32-bit on the graphics processor. No number a person reads, saves or exports comes from it.

Every policy that takes this approach