circularLayout gate: close two tautological checks (5B P2/P3)
P2: test fourSides numbering against GEOMETRY per side (L cy↓, R y↑, T x↑, B x↓) with a de-overlap drift tolerance, not label-emission order (which numbering walks, so it could never fail). P3: assert every pairedRows leader carries polys.length===2. Both mutation-tested: a reversed side sort and a dropped polys each make the gate FAIL.
This commit is contained in:
parent
00db24ed0c
commit
7f272a4cd4
1 changed files with 22 additions and 7 deletions
|
|
@ -128,15 +128,27 @@ if (four.stats.placed < 800) fails.push(`fourSides: only ${four.stats.placed} pi
|
|||
for (const n of nums) if (n <= lo) seqOk = false;
|
||||
if (nums.length) lo = Math.max(lo, ...nums);
|
||||
}
|
||||
// monotone WITHIN a side = numbers increase in LABEL EMISSION order (ring order),
|
||||
// which is the real placement order (four.pins is input order, so it cannot test this).
|
||||
let monoOk = true;
|
||||
// monotone WITHIN a side = tested against GEOMETRY, not the emission arrays (which
|
||||
// numbering walks, so an emission-order check cannot fail — studio P2). Per classic4:
|
||||
// L cy DESC (num↑ ⇒ screen y DOWN), R cy ASC (y UP), T cx ASC (x UP), B cx DESC (x DOWN).
|
||||
// Pins are post-spread, so allow a drift tolerance = 60% of the side's mean step; a
|
||||
// reversed sort (studio's poison) still fails, normal de-overlap drift does not.
|
||||
const AXIS = { L: ['y', -1], R: ['y', +1], T: ['x', +1], B: ['x', -1] };
|
||||
let monoOk = true, monoWhy = '';
|
||||
for (const sd of SEQ) {
|
||||
const seq = four.labels.filter((l) => l.side === sd).map((l) => l.num);
|
||||
for (let i = 1; i < seq.length; i++) if (seq[i] <= seq[i - 1]) { monoOk = false; break; }
|
||||
const ps = four.pins.filter((p) => p.side === sd).sort((a, b) => a.num - b.num);
|
||||
if (ps.length < 3) continue;
|
||||
const [axis, dir] = AXIS[sd];
|
||||
const coords = ps.map((p) => p[axis] * dir); // expected non-decreasing
|
||||
const span = coords[coords.length - 1] - coords[0];
|
||||
const tol = 0.6 * Math.abs(span) / (coords.length - 1); // 60% of mean step
|
||||
let inversions = 0;
|
||||
for (let i = 1; i < coords.length; i++) if (coords[i] < coords[i - 1] - tol) inversions++;
|
||||
// a correct side has only small local drift inversions; a reversed sort inverts ~everything
|
||||
if (inversions > coords.length * 0.2) { monoOk = false; monoWhy = `${sd}:${inversions}/${coords.length}`; }
|
||||
}
|
||||
if (!seqOk) fails.push('fourSides: numbers not in side sequence L<T<R<B');
|
||||
if (!monoOk) fails.push('fourSides: numbers not monotone within a side');
|
||||
if (!monoOk) fails.push(`fourSides: numbering not geometrically monotone within a side (${monoWhy})`);
|
||||
// (4) RECOUNT outside-state from the OUTPUT (pins vs state rings), not from stats.
|
||||
const outFx = recountOutside(four.pins, feats);
|
||||
if (outFx !== 0) fails.push(`fourSides: recount found ${outFx} pins outside their state (stats said ${four.stats.pinsOutsideState})`);
|
||||
|
|
@ -163,7 +175,10 @@ if (paired.stats.placed < 800) fails.push(`pairedRows: only ${paired.stats.place
|
|||
if (xr !== 0) fails.push(`pairedRows: recount found ${xr} crossings (stats said ${paired.stats.crossings})`);
|
||||
const outPr = recountOutside(paired.pins, feats);
|
||||
if (outPr !== 0) fails.push(`pairedRows: recount found ${outPr} pins outside their state`);
|
||||
note.push(`pairedRows check: crossings(recount) ${xr}, outside(recount) ${outPr}`);
|
||||
// (P3) every leader must actually carry its two polylines — a dropped polys passed before.
|
||||
const badPolys = paired.leaders.filter((l) => !Array.isArray(l.polys) || l.polys.length !== 2).length;
|
||||
if (badPolys !== 0) fails.push(`pairedRows: ${badPolys} leaders missing polys.length===2`);
|
||||
note.push(`pairedRows check: crossings(recount) ${xr}, outside(recount) ${outPr}, leaders-with-2-polys ${paired.leaders.length - badPolys}/${paired.leaders.length}`);
|
||||
}
|
||||
|
||||
// ── 3. sideLanes ──────────────────────────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Reference in a new issue