circularLayout: static-import forkRoute + register gate after merge
Switch forkRoute/placePinsInState to static imports from their sibling library modules (now on development) and delete the vendored forkRoute fallback; opts may still override for a Worker. Re-register check:circular-layout (script + gates:pre + description) after the merge took development's package.json.
This commit is contained in:
parent
d6be5806a6
commit
8bb4e7a8e6
2 changed files with 14 additions and 53 deletions
|
|
@ -34,10 +34,12 @@
|
|||
* fmt:{ nameMaxPt, photos, rangeBadge, classic4, fork, sideLanes, pageWidthIn,
|
||||
* w, h }, seamYs:[y...], placePinsInState, forkRoute, feats:[...] }
|
||||
*
|
||||
* placePinsInState and forkRoute are INJECTED so this module stays import-light and
|
||||
* Worker-portable; callers in this repo pass the library implementations. A default
|
||||
* import is attempted lazily for Node callers (see resolveDeps).
|
||||
* placePinsInState and forkRoute come from their sibling library modules (static
|
||||
* imports below); a caller may still override either via opts for a Worker that
|
||||
* injects its own. Both are pure ESM, so the static import stays Worker-portable.
|
||||
*/
|
||||
import { placePinsInState as libPlacePinsInState } from './pins-in-state.js';
|
||||
import { forkRoute as libForkRoute } from './fork-leaders.js';
|
||||
|
||||
// ── text advance (lifted verbatim from poster-render.js: CHADV × WFUDGE) ───────
|
||||
// Worker-safe: a static char-advance table, no canvas. Callers may override via
|
||||
|
|
@ -176,55 +178,12 @@ function statesToPx(feats, proj) {
|
|||
}
|
||||
|
||||
// ── dependency resolution (injected; lazy Node fallback) ───────────────────────
|
||||
async function resolveDeps(opts) {
|
||||
let placePinsInState = opts && opts.placePinsInState;
|
||||
let forkRoute = opts && opts.forkRoute;
|
||||
if (!placePinsInState) ({ placePinsInState } = await import('./pins-in-state.js'));
|
||||
if (!forkRoute) {
|
||||
// fork-leaders.js may not be on this branch yet; try, else vendor the fork math.
|
||||
try { ({ forkRoute } = await import('./fork-leaders.js')); }
|
||||
catch { forkRoute = vendoredForkRoute; }
|
||||
}
|
||||
return { placePinsInState, forkRoute };
|
||||
}
|
||||
|
||||
// Vendored fallback copy of forkRoute (the shared fork-leaders reference), used only if
|
||||
// ./fork-leaders.js is not importable in this tree. The library version supersedes
|
||||
// it the moment that file lands on the branch.
|
||||
function vendoredForkRoute({ lane, a, b, dirOut, mode = 'row', forkFrac = 0.5, obstacles = [], r = 0 }) {
|
||||
const STUB = 7;
|
||||
const ff = forkFrac < 0 ? 0 : forkFrac > 1 ? 1 : forkFrac;
|
||||
const midX = (a.x + b.x) / 2;
|
||||
const budget = Math.max(0, (midX - lane.x) * dirOut);
|
||||
const stemLen = mode === 'trunk' ? Math.max(STUB, ff * budget) : STUB;
|
||||
const forkPt = [lane.x + dirOut * stemLen, lane.y];
|
||||
const stem = [[lane.x, lane.y], forkPt];
|
||||
const octiLeg = (from, pin) => {
|
||||
const [fx, fy] = from; const dx = pin.x - fx, dy = pin.y - fy;
|
||||
const diag = Math.min(Math.abs(dx), Math.abs(dy));
|
||||
const sxs = Math.sign(dx) || dirOut, sys = Math.sign(dy) || 0;
|
||||
const corner = [fx + sxs * diag, fy + sys * diag];
|
||||
const pts = [[fx, fy], corner];
|
||||
if (Math.abs(corner[0] - pin.x) > 1e-6 || Math.abs(corner[1] - pin.y) > 1e-6) pts.push([pin.x, pin.y]);
|
||||
return pts;
|
||||
function resolveDeps(opts) {
|
||||
// The library implementations by default; a Worker may inject its own via opts.
|
||||
return {
|
||||
placePinsInState: (opts && opts.placePinsInState) || libPlacePinsInState,
|
||||
forkRoute: (opts && opts.forkRoute) || libForkRoute,
|
||||
};
|
||||
const legA = octiLeg(forkPt, a), legB = octiLeg(forkPt, b);
|
||||
const polyA = [...stem, ...legA.slice(1)], polyB = [...stem, ...legB.slice(1)];
|
||||
const toS = (pts) => { const o = []; for (let i = 0; i < pts.length - 1; i++) o.push([pts[i], pts[i + 1]]); return o; };
|
||||
const seen = new Set(), segs = [];
|
||||
for (const s of [...toS(stem), ...toS(legA), ...toS(legB)]) {
|
||||
const k = `${s[0]}|${s[1]}`, k2 = `${s[1]}|${s[0]}`;
|
||||
if (seen.has(k) || seen.has(k2)) continue; seen.add(k); segs.push(s);
|
||||
}
|
||||
const throughPins = [];
|
||||
if (obstacles.length && r > 0) {
|
||||
const own = new Set([a.id, b.id]);
|
||||
for (const ob of obstacles) {
|
||||
if (own.has(ob.id)) continue;
|
||||
if (polyNearPoint(polyA, ob.x, ob.y, r) || polyNearPoint(polyB, ob.x, ob.y, r)) throughPins.push(ob.id);
|
||||
}
|
||||
}
|
||||
return { stem, legA, legB, polys: [polyA, polyB], segs, octilinear: isOctilinear(polyA) && isOctilinear(polyB), throughPins };
|
||||
}
|
||||
|
||||
// ── clustering (id-stable; CL_R on the given coordinate pair) ──────────────────
|
||||
|
|
@ -256,7 +215,7 @@ export async function layoutCircular({ pins, states, box, mode, opts = {} }) {
|
|||
if (!['sideLanes', 'fourSides', 'pairedRows'].includes(mode)) throw new TypeError(`unknown mode: ${mode}`);
|
||||
const measure = opts.measure || textW;
|
||||
const fmt = opts.fmt || {};
|
||||
const { placePinsInState, forkRoute } = await resolveDeps(opts);
|
||||
const { placePinsInState, forkRoute } = resolveDeps(opts);
|
||||
if (mode === 'fourSides') return fourSides({ pins, box, opts, fmt, measure, placePinsInState });
|
||||
if (mode === 'pairedRows') return pairedRows({ pins, box, opts, fmt, measure, placePinsInState, forkRoute });
|
||||
return sideLanes({ pins, box, opts, fmt, measure });
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@
|
|||
"check:selector-fields:self-test": "node internal/check-selector-fields.mjs --self-test",
|
||||
"check:packet-strings-parity": "node internal/check-packet-strings-parity.mjs",
|
||||
"check:packet-variants": "node internal/check-packet-variants.mjs",
|
||||
"check:circular-layout": "node library/packet/scripts/check-circular-layout.mjs",
|
||||
"check:fork-leaders": "node library/packet/scripts/check-fork-leaders.mjs",
|
||||
"check:pins-in-state": "node library/packet/scripts/check-pins-in-state.mjs",
|
||||
"check:label-key-coverage": "node internal/check-label-key-coverage.mjs",
|
||||
|
|
@ -356,7 +357,7 @@
|
|||
"fix:sri": "node internal/fix-sri.mjs",
|
||||
"gallery": "node internal/generate-gallery.mjs",
|
||||
"gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:script-flags check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:auto-engine check:engine-contract check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:no-silent-engine:self-test check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:region-paint-single-source check:poster-print-fallback check:posters-exportable check:country-names check:reduced-motion check:no-dead-library check:universal-props check:readme-size check:tree-relative check:leaf-hints check:snippet-engine check:snippet-engine:self-test check:embed-base check:link-coverage check:no-public-binds check:shared-reached check:tree-weight check:no-fleet-names:self-test check:pin-hint-floor:self-test check:poster-sizes:self-test check:shell-header:self-test check:preview-flag-dev-only:self-test check:poster-type-registry:self-test check:host-floor:self-test",
|
||||
"gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:pins-in-state check:fork-leaders check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:i18n-never-translated check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:board-map-options check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:india-states check:batch-selection-export check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline check:help-reachable check:gated-not-in-tree check:window-globals-released check:legend-data-contract check:no-internal-role-nouns check:poster-fetch-guard check:people-group-records check:sentence-slots check:popup-href-escaped check:popup-pan-controls check:country-map-refetch check:pane-config-reforward check:viewport-meta check:viewport-meta:self-test check:label-key-coverage check:poster-browser-equiv check:poster-render-pinned check:country-sheet-one-path check:country-sheet-one-path:self-test check:side-lanes check:side-lanes:self-test check:poster-section check:poster-section:self-test check:posterverse-shelf check:host-locale-fetch check:print-frame-reclaim check:click-sheet check:coverage-consistent:chained check:coverage-consistent:poison check:dashboard-filter-a11y check:filter-multivalue check:hover-channel check:kit-options-reachable check:layout-key-namespaced check:legend-mirrors check:map-unmount-race check:no-conflict-markers check:series-isolation check:snapshot-guard check:stale-sweep-claim check:surfaces-read-the-tier check:translator-contacts check:wall-wysiwyg check:wall-print-watch check:wall-no-bg-photos check:wall-pan check:template-i18n check:list-pattern-doc check:shadow-style-labels-unique check:grid-image-loading check:retry-fetch check:panel-sheets check:poster-wall-host-seams check:poster-sections check:poster-sections:self-test check:poster-design-catalogue check:poster-design-catalogue:self-test check:wall-photo-weight check:no-fleet-names check:pin-hint-floor check:poster-sizes check:upg-count check:shell-header check:pg-name-gate check:preview-flag-dev-only check:poster-type-registry check:host-floor",
|
||||
"gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:pins-in-state check:fork-leaders check:circular-layout check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:i18n-never-translated check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:board-map-options check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:india-states check:batch-selection-export check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline check:help-reachable check:gated-not-in-tree check:window-globals-released check:legend-data-contract check:no-internal-role-nouns check:poster-fetch-guard check:people-group-records check:sentence-slots check:popup-href-escaped check:popup-pan-controls check:country-map-refetch check:pane-config-reforward check:viewport-meta check:viewport-meta:self-test check:label-key-coverage check:poster-browser-equiv check:poster-render-pinned check:country-sheet-one-path check:country-sheet-one-path:self-test check:side-lanes check:side-lanes:self-test check:poster-section check:poster-section:self-test check:posterverse-shelf check:host-locale-fetch check:print-frame-reclaim check:click-sheet check:coverage-consistent:chained check:coverage-consistent:poison check:dashboard-filter-a11y check:filter-multivalue check:hover-channel check:kit-options-reachable check:layout-key-namespaced check:legend-mirrors check:map-unmount-race check:no-conflict-markers check:series-isolation check:snapshot-guard check:stale-sweep-claim check:surfaces-read-the-tier check:translator-contacts check:wall-wysiwyg check:wall-print-watch check:wall-no-bg-photos check:wall-pan check:template-i18n check:list-pattern-doc check:shadow-style-labels-unique check:grid-image-loading check:retry-fetch check:panel-sheets check:poster-wall-host-seams check:poster-sections check:poster-sections:self-test check:poster-design-catalogue check:poster-design-catalogue:self-test check:wall-photo-weight check:no-fleet-names check:pin-hint-floor check:poster-sizes check:upg-count check:shell-header check:pg-name-gate check:preview-flag-dev-only check:poster-type-registry check:host-floor",
|
||||
"gen:iso3-to-alpha2": "node library/scripts/generate-iso3-to-alpha2.mjs",
|
||||
"internet:adm1": "node plugin-designs/cities-internet/scripts/build-adm1-internet.mjs",
|
||||
"library-index": "node internal/generate-library-index.mjs",
|
||||
|
|
@ -586,6 +587,7 @@
|
|||
"check:packet-variants": "the poster wall's Prayer List prints the PACKET (not the columnar list), and every variant it offers exists in packet-render's VARIANTS. A variant may drop fixed pages but never people: lightweight keeps every group. By the UPG-100 owner; mutation-tested. Headless; in gates:pre.",
|
||||
"check:pins-in-state": "library/packet/pins-in-state.js places whole-India poster pins so no two overlap AND every pin stays inside its own ADM1 state polygon, at the 60x96in sheet scale (2500x4000px, PIN_R 8). A deterministic repulsion relaxation (imitating poster-render.js, read not called) with the map-rect clamp replaced by a per-state multipolygon clamp: a pin pushed off its land is pulled to the nearest point just inside, holes and multi-part states (islands/coasts) honoured. Runs on the real India 855-pin data when present, else a synthetic 60x96 fixture (floor 200 pins, no vacuous pass); asserts 0 pins outside their state and 0 overlaps that are not NAMED in `infeasible` (a too-small state is reported, never silently stacked), plus a hole/island arm and a poison arm. Node only, headless, deterministic.",
|
||||
"check:fork-leaders": "library/packet/fork-leaders.js routes ONE paired-row leader as an octilinear fork: a stem forks at a 45° chamfer into two short H/V legs, one per pin. Modes row and trunk. Returns polylines + a deduped segs list (one polyline id per fork for crossings). Reports octilinear + throughPins. Pure, px-only, deterministic. The gate asserts both modes octilinear, legs land on pins, 0 crossings on a 220-row set (floor 200), through-pin hit+miss, determinism, and a poison. Node only, headless.",
|
||||
"check:circular-layout": "library/packet/circularLayout.js is the poster circular-logic (ring order + leader routing + numbered four-sides + paired-row forks) as one pure Worker-safe module: numbers in, geometry out (positions/polylines/stats), no SVG. Ported from poster-render.js + the classic4 four-sides reference. Modes sideLanes (today L/R), fourSides (design 1: numbered, no leaders, 0 crossings), pairedRows (design 2: two leaders/row, 0 crossings). forkRoute + placePinsInState are static imports (opts may override for a Worker). Gate asserts on a frozen 855-pin 60x96 fixture: fourSides ring-ordered + 0 dupes + 0 pins-outside-state (floor 800) + side sequence L<T<R<B + geometric monotonicity per side, pairedRows 0 crossings + octilinear + every leader polys.length===2, sideLanes runs octilinear, determinism, a poison, output recounts, and a node time/heap budget. Deterministic; ties by code unit. Node only, headless.",
|
||||
"check:label-key-coverage": "node internal/check-label-key-coverage.mjs",
|
||||
"check:host-locale-fetch": "node internal/check-host-locale-fetch.mjs",
|
||||
"check:packet-a1-structure": "the prayer packet is built to its specified structure, read page by page from each sheet's CONTENT: full detail = map, triangle, life calendar, % evangelical, profiles, bounded set, centered set, wheel, points; lightweight = map, triangle, profiles, wheel, points. Page 1 places a map-only vertical sheet on the packet's own margin and says it is one of the UPG 100 prayer lists only when a count is supplied; page 2 is one inverted triangle whose packets tier and \"- N\" steps appear only where true; the wheel carries the Metacamp Module 1 PDF's colours and opens at nine o'clock; all 28 legend zones resolve, Deaf included.",
|
||||
|
|
|
|||
Loading…
Reference in a new issue