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