Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/feedthejoe / test/field.test.js |
| Date | Not specified in source |
| Repository | Janady13/feedthejoe |
| Revision | 0e608a2b515a959bf35d2783428766624400ca3c |
| Branch | main |
| Path or archive member | test/field.test.js |
| Original SHA-256 | 069e50305e613bd1f66e362511d7875d5ffcc9375137ade1bd891fb22c6584de |
| Git blob SHA | c6ea91f5ca6a6fb7420696e18b9acc147ba55e21 |
| Reading-copy SHA-256 | 069e50305e613bd1f66e362511d7875d5ffcc9375137ade1bd891fb22c6584de |
Recovered source transcript
Download complete source text ↓150 lines · 6.9 KB
import test from 'node:test';
import assert from 'node:assert/strict';
import { sampleField, sampleAt, gradientAt } from '../src/core/field/density.js';
import { marchingSquares, chain, contours } from '../src/core/field/contour.js';
const ONE = [{ x: 0.5, y: 0.5, mass: 1 }];
const TWO = [{ x: 0.25, y: 0.5, mass: 1 }, { x: 0.75, y: 0.5, mass: 1 }];
test('field peaks at the point and decays away from it', () => {
const f = sampleField(ONE, { width: 101, height: 101, sigma: 0.1 });
const centre = sampleAt(f, 50, 50);
const edge = sampleAt(f, 2, 2);
assert.ok(centre > edge * 50, `peak ${centre} should dominate edge ${edge}`);
assert.ok(Math.abs(f.max - centre) < 1e-5, 'reported max must be the actual peak');
});
test('field is symmetric about a centred point', () => {
const f = sampleField(ONE, { width: 101, height: 101, sigma: 0.12 });
for (const [a, b] of [[[30, 50], [70, 50]], [[50, 30], [50, 70]], [[30, 30], [70, 70]]]) {
assert.ok(Math.abs(sampleAt(f, ...a) - sampleAt(f, ...b)) < 1e-4,
`symmetry broken between ${a} and ${b}`);
}
});
test('mass is additive: two points sum where they overlap', () => {
const wide = sampleField(TWO, { width: 101, height: 101, sigma: 0.4 });
const single = sampleField([TWO[0]], { width: 101, height: 101, sigma: 0.4 });
const mid = sampleAt(wide, 50, 50);
assert.ok(mid > sampleAt(single, 50, 50), 'two overlapping mounds must exceed one');
});
test('sigma controls whether points merge or stay separate', () => {
// Compare at a fixed fraction of each field's own peak. Comparing at a
// fraction of the RANGE is meaningless here: a very wide sigma makes the
// field almost flat, min and max converge, and relative levels stop
// corresponding to anything on the surface.
const islandsAtHalfPeak = (sigma) => {
const f = sampleField(TWO, { width: 161, height: 161, sigma });
return chain(marchingSquares(f, f.max * 0.5)).length;
};
assert.equal(islandsAtHalfPeak(0.06), 2, 'narrow mounds must stay two separate islands');
assert.equal(islandsAtHalfPeak(0.20), 1, 'wide mounds must merge into one landmass');
// Wider still and the mounds overrun the grid: the contour is then clipped by
// the boundary into open arcs, one per edge, rather than merging further.
// That is a property of sampling a finite window, not of the field, and it is
// why the renderer keeps sigma well below this.
assert.ok(islandsAtHalfPeak(0.5) > 1, 'an overrunning field is clipped, not merged');
});
test('gradient points uphill toward the peak', () => {
const f = sampleField(ONE, { width: 101, height: 101, sigma: 0.15 });
const [gx] = gradientAt(f, 25, 50); // left of centre: uphill is +x
assert.ok(gx > 0, `expected positive x-gradient left of the peak, got ${gx}`);
const [gx2] = gradientAt(f, 75, 50); // right of centre: uphill is -x
assert.ok(gx2 < 0, `expected negative x-gradient right of the peak, got ${gx2}`);
});
test('sampleAt clamps rather than reading outside the buffer', () => {
const f = sampleField(ONE, { width: 40, height: 40, sigma: 0.2 });
for (const [x, y] of [[-99, -99], [999, 999], [-1, 20], [20, 999]]) {
assert.ok(Number.isFinite(sampleAt(f, x, y)), `out-of-bounds ${x},${y} returned non-finite`);
}
});
test('sampleField rejects a degenerate grid', () => {
assert.throws(() => sampleField(ONE, { width: 1, height: 10 }), RangeError);
});
test('marching squares finds a closed ring around a single peak', () => {
const f = sampleField(ONE, { width: 121, height: 121, sigma: 0.15 });
const level = f.min + (f.max - f.min) * 0.5;
const paths = chain(marchingSquares(f, level));
assert.equal(paths.length, 1, 'one peak at one level must give exactly one contour');
const ring = paths[0];
const head = ring[0];
const tail = ring[ring.length - 1];
assert.ok(Math.hypot(head[0] - tail[0], head[1] - tail[1]) < 1.5,
'a contour around an interior peak must close on itself');
// Every vertex should sit at roughly the same radius from the peak.
const radii = ring.map(([x, y]) => Math.hypot(x - 60, y - 60));
const mean = radii.reduce((s, r) => s + r, 0) / radii.length;
const worst = Math.max(...radii.map((r) => Math.abs(r - mean)));
assert.ok(worst < mean * 0.06,
`an isocontour of a radial field must be circular; worst deviation ${worst.toFixed(2)} of ${mean.toFixed(2)}`);
});
test('a level above the maximum yields nothing, below the minimum yields nothing', () => {
const f = sampleField(ONE, { width: 60, height: 60, sigma: 0.1 });
assert.equal(marchingSquares(f, f.max + 1).length, 0);
assert.equal(marchingSquares(f, f.min - 1).length, 0);
});
test('two separated peaks give two separate rings', () => {
const f = sampleField(TWO, { width: 161, height: 161, sigma: 0.07 });
const paths = chain(marchingSquares(f, f.min + (f.max - f.min) * 0.5));
assert.equal(paths.length, 2, 'two well-separated peaks must give two contours');
});
test('chaining joins segments instead of leaving loose ticks', () => {
const f = sampleField(ONE, { width: 121, height: 121, sigma: 0.15 });
const segs = marchingSquares(f, f.min + (f.max - f.min) * 0.4);
const paths = chain(segs);
const vertices = paths.reduce((n, p) => n + p.length, 0);
assert.ok(paths.length < segs.length / 10,
`chaining must collapse ${segs.length} segments into few paths, got ${paths.length}`);
assert.ok(vertices >= segs.length, 'chaining must not lose vertices');
});
test('contours are nested and ordered low to high', () => {
const f = sampleField(ONE, { width: 121, height: 121, sigma: 0.15 });
const cs = contours(f, 8);
assert.ok(cs.length >= 6);
for (let i = 1; i < cs.length; i += 1) {
assert.ok(cs[i].level > cs[i - 1].level, 'levels must increase');
}
// Higher level -> tighter ring around the peak.
const radius = (c) => {
const r = c.paths[0];
return r.reduce((s, [x, y]) => s + Math.hypot(x - 60, y - 60), 0) / r.length;
};
assert.ok(radius(cs[cs.length - 1]) < radius(cs[0]),
'contours must shrink toward the summit');
});
test('contour output is deterministic', () => {
const f = sampleField(TWO, { width: 90, height: 70, sigma: 0.09 });
assert.equal(JSON.stringify(contours(f, 10)), JSON.stringify(contours(f, 10)));
});
test('saddle cells do not produce self-crossing contours', () => {
// Four peaks in a square make a saddle at the centre — the case that is
// ambiguous in marching squares and produces crossed lines if resolved wrong.
const quad = [
{ x: 0.3, y: 0.3, mass: 1 }, { x: 0.7, y: 0.3, mass: 1 },
{ x: 0.3, y: 0.7, mass: 1 }, { x: 0.7, y: 0.7, mass: 1 },
];
const f = sampleField(quad, { width: 141, height: 141, sigma: 0.12 });
for (let i = 1; i < 12; i += 1) {
const segs = marchingSquares(f, f.min + (f.max - f.min) * (i / 12));
for (const [x1, y1, x2, y2] of segs) {
assert.ok(Number.isFinite(x1 + y1 + x2 + y2), 'segment had a non-finite endpoint');
// No segment may span more than one cell diagonal.
assert.ok(Math.hypot(x2 - x1, y2 - y1) < 1.5, 'segment jumped across cells');
}
}
});