The aperture was the tray's rule cut across the two cells the goal block leaves by, with three identical ticks under it. Three marks of one size have to be read as a group and then interpreted, and the middle one — the only one carrying any meaning — looked exactly like the two that merely marked a boundary. So the rule turns a right angle and goes down at each side of the gap. A rule that stops reads as a rule that ran out; a rule that turns reads as a wall built up to an opening on purpose. Between the jambs, where the middle tick was, an arrowhead pointing out of the tray in the goal block's own colour, which is the one thing on the board that says which piece the door is for and says it without a word. The arrowhead is a new op rather than a fill, because it has to be that colour and everything in the scene is greyscale until the threshold has run. It is painted in the overlay pass for the reason the maker's mark is: it is a flat mark whose whole job is to be the same shape every time, and a screen put through something this small returns gravel. It is drawn as a stack of whole-unit runs and not as a polygon — the rest of this board is square pixels, and a smooth hypotenuse beside them is a picture from a different machine. The figure is fitted to what there is between the tray's rule and the name under the plate, rather than sized off the cell alone. That strip is the one part of the plate that does not grow with the board.
- commit
- 14c2a9d15ffb83ce5574c3fd4814a90e63a9de37
- parent
- 702e7a8fa3e6a7c818151045e906e1d36eea9a48
- author
- Dami <dami.etoile@gmail.com>
| M | src/ | 8 | ++++ |
| M | src/ | 14 | ++++++ |
| M | src/ | 68 | +++++++++++++++++++++++++++--- |
@@ -902,6 +902,14 @@ pub fn play(
902902 cell,903903 family.goal_x,904904 tray.y + tray.h,905+ // What the doorway has to itself: the tray's rule down to the top of the906+ // name set under the plate.907+ plate.y908+ + plate.h909+ - float.round(int.to_float(b.pad_bottom) *. 0.34)910+ - float.round(typeface.cap_height(int.to_float(cell) *. 0.44, Display))911+ - { tray.y + tray.h },912+ theme.piece_colour(theme, 4),905913 ),906914 placed,907915 [@@ -23,6 +23,7 @@ import {
2323 Trophy,2424 GoldLine,2525 DevLogo,26+ Exit,2627 Clip,2728 Unclip,2829 Display,@@ -597,6 +598,17 @@ function paintTrophy(ctx, cx, cy, s, inkCss, gold) {
597598 // itself, so what is left of the middle is a slot rather than a hole — and the598599 // short inner stroke, the one that makes it a delta rather than a triangle,599600 // cuts that slot off-centre.601+// The arrowhead under the aperture, drawn as a stack of whole-unit runs rather602+// than as a polygon. Everything else on this board is made of square pixels, and603+// a smooth hypotenuse beside them reads as a picture from a different machine.604+function paintExit(ctx, decal) {605+ ctx.fillStyle = css(decal.ink);606+ const rows = (decal.w + 1) / 2;607+ for (let i = 0; i < rows; i++) {608+ ctx.fillRect(decal.x + i, decal.y + i, decal.w - 2 * i, 1);609+ }610+}611+600612 function paintDevLogo(ctx, decal, inkCss) {601613 const r = decal.size / 2;602614 ctx.save();@@ -824,6 +836,8 @@ function paintOverlays(frame, overlays, inkCss, paperCss, goldCss) {
824836 withClip(typeCtx, clip, 1, () => paintGoldLine(typeCtx, op, goldCss));825837 } else if (op instanceof DevLogo) {826838 withClip(typeCtx, clip, 1, () => paintDevLogo(typeCtx, op, inkCss));839+ } else if (op instanceof Exit) {840+ withClip(typeCtx, clip, 1, () => paintExit(typeCtx, op));827841 }828842 }829843 typeCtx.globalAlpha = 1;@@ -160,6 +160,13 @@ pub type Op {
160160 /// through a shape whose whole job is to be constant, and at fourteen pixels161161 /// a screen of any ruling turns a ring into four dots.162162 DevLogo(x: Float, y: Float, size: Float, ink: Rgb)163+ /// The arrowhead under the aperture, pointing out of the tray, in the goal164+ /// block's colour. Painted after the threshold for the reason the maker's mark165+ /// is: it is a flat mark whose job is to be the same shape every time, and a166+ /// screen put through something this small returns gravel. `w` is its width in167+ /// units and is odd, which makes its height exactly `(w + 1) / 2` — it is a168+ /// stack of runs, each two units narrower than the one above.169+ Exit(x: Int, y: Int, w: Int, ink: Rgb)163170 /// Sprites and type are composited *after* the threshold, so they do not164171 /// respect anything drawn over them in the scene — a scrolled list ran165172 /// straight through the menu bar. Anything queued while a clip is set carries@@ -260,14 +267,61 @@ pub fn tray(r: Rect) -> List(Op) {
260267 ]261268 }262269 263-/// The tray's rule, cut where the block is meant to leave, with three ticks264-/// under it marking the two cells of the aperture.265-pub fn aperture(board: Rect, cell: Int, goal_x: Int, tray_bottom: Int) -> List(Op) {270+/// The way out.271+///272+/// The tray's rule reaches the two cells the goal block leaves by and turns273+/// down, so the gap is a doorway with jambs rather than an absence. A rule that274+/// merely stops reads as a rule that ran out; a rule that turns a right angle275+/// reads as a wall that has been built up to an opening on purpose. Between the276+/// jambs, an arrowhead pointing out of the tray, printed in the goal block's own277+/// colour — which is the one thing on the board that says which piece the door278+/// is for, and it says it without a word.279+///280+/// It was three ticks under the rule before, evenly spaced across the gap. Three281+/// marks of one size have to be read as a group and then interpreted; the middle282+/// one carried the whole meaning and looked exactly like the two that only283+/// marked a boundary.284+///285+/// `room` is what there is between the tray's rule and the name set under the286+/// plate. The figure is fitted into it rather than sized off the cell alone,287+/// because the space below the tray is the one part of the plate that does not288+/// grow with the board.289+pub fn aperture(290+ board: Rect,291+ cell: Int,292+ goal_x: Int,293+ tray_bottom: Int,294+ room: Int,295+ ink: Result(Rgb, Nil),296+) -> List(Op) {266297 let x = board.x + goal_x * cell267- [268- Fill(x + 2, tray_bottom, cell * 2 - 4, 1, paper_tone),269- ..list.map([0, 1, 2], fn(i) { rule(x + i * cell, tray_bottom + 2, 1, 3) })270- ]298+ let w = cell * 2299+300+ // The head is a stack of runs two units narrower each time, so its height301+ // follows from its width and only one of the two is a decision. The jambs go302+ // a unit past it: a frame that stops level with what it frames stops being a303+ // frame.304+ let high =305+ int.max(2, int.min({ room - 3 } / 2, float.round(int.to_float(cell) *. 0.26)))306+ let head = int.min(high * 2 - 1, w - 6)307+ let deep = high + 2308+309+ let mark = case ink {310+ Ok(colour) -> [311+ Exit(x: x + w / 2 - head / 2, y: tray_bottom + 2, w: head, ink: colour),312+ ]313+ // No tints in this theme, so the door is read from its frame alone.314+ Error(_) -> []315+ }316+317+ list.flatten([318+ [319+ Fill(x, tray_bottom, w, 1, paper_tone),320+ rule(x - 1, tray_bottom, 1, deep),321+ rule(x + w, tray_bottom, 1, deep),322+ ],323+ mark,324+ ])271325 }272326 273327 /// A plain label, at its natural size.