- commit
- aed55112b078949541db0b74d399dcf06962f36b
- parent
- 868380043b805bdeae10ae580f99f98cbf8c52e0
- author
- Dami <dami.etoile@gmail.com>
| M | src/ | 39 | ++++++++++++++---------------- |
| M | src/ | 8 | +++--- |
| M | tests/ | 20 | +++++++++++++++ |
| M | tests/ | 2 | ++ |
@@ -267,35 +267,32 @@ function automataPhase(system, frame) {
267267 function drawAutomataCue(ctx, action, cx, cy, cell, colors) {268268 if (!action) return;269269 ctx.save();270- ctx.strokeStyle = colors.TICK;271270 ctx.fillStyle = colors.TICK;272- ctx.lineWidth = Math.max(2, cell * 0.035);273- ctx.lineCap = "round";274271 if (action.action === "wait") {275- const width = cell * 0.16;276- for (const offset of [-width, 0, width]) {272+ const gap = Math.max(1.5, cell * 0.035);273+ const height = Math.max(2, cell * 0.08);274+ for (const offset of [-gap, gap]) {277275 ctx.beginPath();278- ctx.moveTo(cx + offset, cy - cell * 0.11);279- ctx.lineTo(cx + offset, cy + cell * 0.11);280- ctx.stroke();276+ ctx.rect(cx + offset - Math.max(1, cell * 0.012), cy - height / 2, Math.max(2, cell * 0.024), height);277+ ctx.fill();281278 }282279 } else {283280 const dx = action.dx || 0;284281 const dy = action.dy || 0;285- const tipX = cx + dx * cell * 0.2;286- const tipY = cy + dy * cell * 0.2;287- const tailX = cx - dx * cell * 0.14;288- const tailY = cy - dy * cell * 0.14;289- const wingX = dy * cell * 0.1;290- const wingY = dx * cell * 0.1;282+ const size = Math.min(10, cell * 0.13);283+ const tipX = cx + dx * size / 2;284+ const tipY = cy + dy * size / 2;285+ const tailX = cx - dx * size / 2;286+ const tailY = cy - dy * size / 2;287+ const wingX = dy * size / 2;288+ const wingY = dx * size / 2;291289 ctx.beginPath();292290 ctx.moveTo(tailX, tailY);291+ ctx.lineTo(tailX + wingX, tailY + wingY);293292 ctx.lineTo(tipX, tipY);294- ctx.moveTo(tipX, tipY);295- ctx.lineTo(tipX - dx * cell * 0.1 + wingX, tipY - dy * cell * 0.1 + wingY);296- ctx.moveTo(tipX, tipY);297- ctx.lineTo(tipX - dx * cell * 0.1 - wingX, tipY - dy * cell * 0.1 - wingY);298- ctx.stroke();293+ ctx.lineTo(tailX - wingX, tailY - wingY);294+ ctx.closePath();295+ ctx.fill();299296 }300297 ctx.restore();301298 }@@ -329,8 +326,8 @@ function drawAutomataIndicators(ctx, state, geometry, colors, model, frame) {
329326 drawAutomataCue(330327 ctx,331328 action,332- cellX + geometry.cell * 0.31,333- cellY - geometry.cell * 0.27,329+ cellX,330+ action.action === "wait" ? cellY + geometry.cell * 0.435 : cellY,334331 geometry.cell,335332 colors,336333 );@@ -266,13 +266,13 @@ export function drawAutomataStrip(
266266 const current = patternIndex === phase;267267 const next = patternIndex === (phase + 1) % system.pattern.length;268268 ctx.globalAlpha = current || next ? 1 : 0.4;269- ctx.fillStyle = current ? "#C6F432" : "#191816";269+ ctx.fillStyle = next ? "#C6F432" : "#191816";270270 ctx.strokeStyle = current || next ? "#C6F432" : "#4A4640";271- ctx.lineWidth = current ? 2 : 1;271+ ctx.lineWidth = next ? 2 : 1;272272 ctx.fillRect(beatX, rowY + 1, 22, 22);273- if (!current) ctx.strokeRect(beatX + 0.5, rowY + 1.5, 21, 21);273+ if (!next) ctx.strokeRect(beatX + 0.5, rowY + 1.5, 21, 21);274274 const cue = automataCue(action);275- ctx.fillStyle = current ? "#0F0E0D" : next ? "#0F0E0D" : "#8E877C";275+ ctx.fillStyle = next ? "#0F0E0D" : current ? "#F2EEE6" : "#8E877C";276276 ctx.font = `${current ? "800" : "600"} 14px Recursive, monospace`;277277 ctx.textAlign = "center";278278 ctx.fillText(cue, beatX + 11, rowY + 13);@@ -169,3 +169,23 @@ test('drawBoard animates sentence bars from frozen cells and renders frame overl
169169 assert.ok(ctx.calls.some(call => call[0] === 'fillText' && call[1] === 'MIAU'));170170 assert.ok(ctx.calls.some(call => call[0] === 'fillRect' && call[1] === 20 && call[2] === 10 && call[3] === 200 && call[4] === 150));171171 });172+173+test('drawBoard renders automaton wait as two centered bottom-gutter bars', () => {174+ const ctx = context();175+ const automatonState = {176+ ...state,177+ objects: [...state.objects, { id: 'bell', kind: 'object', noun: 'BELL', x: 1, y: 1 }],178+ automata: [{ id: 'bell-clock', subject: 'BELL', property: 'TICK', phase: 0, active: true, pattern: [{ action: 'wait' }] }],179+ };180+ drawBoard(ctx, automatonState, {181+ x: 20,182+ y: 10,183+ cell: 50,184+ game: { properties: object => object.id === 'bell' ? ['TICK'] : [] },185+ });186+ const cueFills = ctx.calls.filter(call => call[0] === 'fill' && call[1] === '#C6F432');187+ assert.equal(cueFills.length, 2);188+ const cueRects = ctx.calls.filter(call => call[0] === 'rect' && call[3] <= 2 && call[4] <= 5);189+ assert.equal(cueRects.length, 2);190+ assert.ok(cueRects.every(call => call[2] > 10 + 50 * 1.5), 'wait bars belong in the bottom gutter');191+});@@ -92,6 +92,8 @@ test('drawAutomataStrip uses one actor tile, directional beat cells, and a sente
9292 assert.equal(text.includes('ACTIVE'), false);9393 const beatRects = ctx.calls.filter((call) => call[0] === 'fillRect' && call[3] === 22);9494 assert.equal(beatRects.length, 3);95+ assert.equal(beatRects[0][5], '#191816', 'just-played beat stays an outlined trail');96+ assert.equal(beatRects[1][5], '#C6F432', 'next beat is the filled target');9597 assert.equal(result.height, 24);9698 });9799