SEED MAP shared wheel rim: the table's OWN sky (planets-only, canonical signs) rings the tessellation — one frame for all six gamers — TDD
The Voronoi felt gains a stripped sky-wheel rim drawn from the ROOM's own sky
(Room.sky_chart) — identical for every gamer, updating toward the shared map —
with the tessellation sized into the wheel's freed hub. Roadmap step 21, Step
2's coordinate frame.
- Room.convened_at + Room.sky_chart (migration 0019); pick_roles stamps
convened_at at gate-close (stamp only — no HTTP on the transition)
- epic.table_sky: lazy planets-only chart via PySwiss at the null location
(geocentric longitudes need only the convened TIME; houses/ASC/MC need a
birth LOCATION a virtual table lacks → omitted), cached on Room.sky_chart;
legacy rooms key off created_at; seated-gamer gated, 502 on PySwiss down
- SkyWheel.drawRim(svg, data): pure static renderer — canonical asc=0 frame,
signs ring + planet glyphs only, NO element ring / centre disc / houses /
axes / aspects / tooltips; never writes the interactive wheel's singleton
state; returns {size, cx, cy, r, hubR} so the felt sizes the map into the hub
- _seed_map_overlay.html: rim draws on open; map svg shrinks to 2×hubR +
.voronoi-map--rimmed clip; lazy table-sky fetch on open; preload-then-repaint
so a cold-cache open doesn't strand the zodiac glyphs; ResizeObserver on the
col (not the self-sized map svg)
- _sky.scss: stacked centred svgs in .seed-map-col; .seed-wheel pointer-events
none; circle clip on the rimmed map
- room_sky_json ctx in _role_select_context; rootvars: --sixUser/--octUser
nudged within the Trs ramp (parallel palette tune)
- drawRim Jasmine suite (R1–R6: signs+planets, strip, hub geometry, static
placement, singleton untouched, signs-only fallback) in both spec copies;
epic TableSkyViewTest + convened_at stamp + seed-overlay rim ITs; FT rim
assertions (12 signs, 3 planets, no stripped/located layers, hub sizing)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1033,3 +1033,116 @@ describe("SkyWheel — angle (ASC/MC) click tooltips", () => {
|
||||
expect(marsGroup.classList.contains("nw-planet--active")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
// ── SkyWheel.drawRim — the SEED MAP's shared wheel rim ──────────────────────
|
||||
//
|
||||
// The STRIPPED wheel ringing the Voronoi tessellation (roadmap step 21,
|
||||
// Step 2): the ROOM's own sky — planets only, since a virtual table has a
|
||||
// convened TIME but no birth LOCATION (so houses/ASC/MC never exist on the
|
||||
// shared rim) — around the canonical signs ring (asc=0 frame, identical for
|
||||
// all six gamers). drawRim is a PURE renderer: no tooltips, no cycle, no
|
||||
// transitions, and CRUCIALLY no singleton writes — the interactive saved
|
||||
// wheel on the same page (_svg/_currentData/#id_sky_tooltip) must survive a
|
||||
// rim draw untouched.
|
||||
|
||||
describe("SkyWheel — drawRim (SEED MAP shared rim)", () => {
|
||||
|
||||
const ROOM_SKY = {
|
||||
planets: {
|
||||
Sun: { sign: "Pisces", degree: 338.4, retrograde: false },
|
||||
Moon: { sign: "Capricorn", degree: 295.1, retrograde: false },
|
||||
Mercury: { sign: "Aquarius", degree: 312.8, retrograde: true },
|
||||
},
|
||||
aspects: [],
|
||||
};
|
||||
|
||||
let rimSvg, skySvg, tooltipEl;
|
||||
|
||||
beforeEach(() => {
|
||||
rimSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
rimSvg.setAttribute("id", "id_seed_wheel_svg");
|
||||
rimSvg.setAttribute("width", "400");
|
||||
rimSvg.setAttribute("height", "400");
|
||||
rimSvg.style.width = "400px";
|
||||
rimSvg.style.height = "400px";
|
||||
document.body.appendChild(rimSvg);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rimSvg.remove();
|
||||
if (skySvg) { SkyWheel.clear(); skySvg.remove(); skySvg = null; }
|
||||
if (tooltipEl) { tooltipEl.remove(); tooltipEl = null; }
|
||||
});
|
||||
|
||||
it("R1: renders the canonical sign ring + the room sky's planet glyphs", () => {
|
||||
SkyWheel.drawRim(rimSvg, ROOM_SKY);
|
||||
|
||||
expect(rimSvg.querySelectorAll(".nw-sign-group").length).toBe(12);
|
||||
expect(rimSvg.querySelectorAll(".nw-planet-group").length).toBe(3);
|
||||
expect(rimSvg.querySelector(".nw-outer-ring")).not.toBeNull();
|
||||
// Mercury's retrograde badge carries over to the rim.
|
||||
expect(rimSvg.querySelector("[data-planet='Mercury'] .nw-rx")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("R2: strips the element ring, centre disc, aspect web, houses and axes", () => {
|
||||
SkyWheel.drawRim(rimSvg, ROOM_SKY);
|
||||
|
||||
["nw-elements", "nw-inner-disc", "nw-aspects", "nw-houses", "nw-axes"]
|
||||
.forEach((cls) => {
|
||||
expect(rimSvg.querySelectorAll("." + cls).length)
|
||||
.toBe(0, `expected no .${cls} on the shared rim`);
|
||||
});
|
||||
});
|
||||
|
||||
it("R3: returns the hub geometry the tessellation slots into", () => {
|
||||
const geo = SkyWheel.drawRim(rimSvg, ROOM_SKY);
|
||||
|
||||
expect(geo.size).toBe(400);
|
||||
expect(geo.cx).toBe(200);
|
||||
expect(geo.cy).toBe(200);
|
||||
expect(geo.r).toBeCloseTo(400 * 0.46, 6);
|
||||
// The freed hub: just inside the planet band (planetR 0.59 − glyph 0.05).
|
||||
expect(geo.hubR).toBeCloseTo(400 * 0.46 * 0.52, 6);
|
||||
});
|
||||
|
||||
it("R4: places planets statically at their canonical (asc=0) angles — no entry transition", () => {
|
||||
SkyWheel.drawRim(rimSvg, ROOM_SKY);
|
||||
|
||||
const sun = rimSvg.querySelector("[data-planet='Sun'] circle");
|
||||
const a = (-(338.4) - 180) * Math.PI / 180; // _toAngle(338.4, asc=0)
|
||||
const r = 400 * 0.46;
|
||||
expect(parseFloat(sun.getAttribute("cx"))).toBeCloseTo(200 + r * 0.59 * Math.cos(a), 1);
|
||||
expect(parseFloat(sun.getAttribute("cy"))).toBeCloseTo(200 + r * 0.59 * Math.sin(a), 1);
|
||||
});
|
||||
|
||||
it("R5: leaves the interactive singleton untouched — the sky felt's wheel keeps its svg + tooltip", () => {
|
||||
skySvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
skySvg.setAttribute("id", "id_sky_svg");
|
||||
skySvg.style.width = "400px";
|
||||
skySvg.style.height = "400px";
|
||||
document.body.appendChild(skySvg);
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
SkyWheel.draw(skySvg, CONJUNCTION_CHART);
|
||||
|
||||
SkyWheel.drawRim(rimSvg, ROOM_SKY);
|
||||
|
||||
// The tooltip controls draw() injected survive the rim draw.
|
||||
expect(tooltipEl.querySelector(".nw-tt-prv")).not.toBeNull();
|
||||
// The singleton still points at the INTERACTIVE svg: clear() empties
|
||||
// it, not the rim.
|
||||
SkyWheel.clear();
|
||||
expect(skySvg.children.length).toBe(0);
|
||||
expect(rimSvg.querySelectorAll(".nw-sign-group").length).toBe(12);
|
||||
});
|
||||
|
||||
it("R6: renders the signs-only canonical frame when the room sky is absent", () => {
|
||||
const geo = SkyWheel.drawRim(rimSvg, null);
|
||||
|
||||
expect(rimSvg.querySelectorAll(".nw-sign-group").length).toBe(12);
|
||||
expect(rimSvg.querySelectorAll(".nw-planet-group").length).toBe(0);
|
||||
expect(geo.hubR).toBeCloseTo(400 * 0.46 * 0.52, 6);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user