b53e23fe4d
CI / changes (pull_request) Successful in 5s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Has been skipped
CI / conformance (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
The dark and light covers move up beside the generator as the shipped assets; the six exploratory variants and the contact sheet go. Trims the generator to match: VARIANTS is now only the two covers, and with the retired variants gone so are the knobs nothing reaches any more — the wordmark caption, the branded palette, the whole-board height fit and the non-brand tile face. Left behind, they would read as supported options that no output exercises. `node build-cover.mjs` now reproduces exactly the two committed PNGs.
65 lines
3.1 KiB
Markdown
65 lines
3.1 KiB
Markdown
# VK community cover
|
||
|
||
The cover art for the VK community (`1920×768`). The community avatar — the «Э» tile — is
|
||
uploaded separately; this is only the backdrop behind it.
|
||
|
||
| File | What |
|
||
|------|------|
|
||
| `cover-night-spot.png` | the dark cover — board in a warm spotlight |
|
||
| `cover-day-spot.png` | the light cover — same camera, same crop, daylight |
|
||
| `build-cover.mjs` | the generator both are rendered by |
|
||
|
||
`build-cover.mjs` is a small software renderer that draws an Эрудит board in perspective with
|
||
the opening `ГРАМОТНОСТЬ / СОПЕРНИК / ЭРУДИЦИЯ / УМ` already played, and writes the two PNGs
|
||
next to itself.
|
||
|
||
```sh
|
||
cd assets/vk/cover
|
||
node build-cover.mjs # both covers
|
||
node build-cover.mjs night-spot # one of them
|
||
FAST=1 node build-cover.mjs # 1x instead of 2x supersampling, for quick iteration
|
||
ZONES=1 node build-cover.mjs # overlay the VK keep-outs on the render
|
||
LAYOUT=cross node build-cover.mjs # the alternative word arrangement
|
||
```
|
||
|
||
It needs no install: skia-canvas comes from the render sidecar's `node_modules`, and the
|
||
letters are set in `Spectral-Bold.ttf` from the icon brand folder.
|
||
|
||
## The position
|
||
|
||
`ГРАМОТНОСТЬ` opens through the centre square (its `Т` on H8) and the other three words hook
|
||
onto a shared letter. `assertLegal` re-reads the finished grid and fails the build if the
|
||
centre is uncovered, if a declared word is missing, or if any maximal run of two or more
|
||
letters is a word nobody played — so the board on the cover is always a position the engine
|
||
would accept. All four words are in the Эрудит dictionary; tile values come from
|
||
`scrabble-solver` `rules.Erudit()`.
|
||
|
||
The default `ladder` arrangement puts both long words horizontally. The `cross` arrangement
|
||
hangs `ЭРУДИЦИЯ` down the left instead, which looks more symmetric but collides with the VK
|
||
mobile avatar (below).
|
||
|
||
## VK keep-outs
|
||
|
||
Measured off VK's own template (`../VK_group_cover_template.fig`), in cover pixels:
|
||
|
||
| Zone | Area | Why |
|
||
|------|------|-----|
|
||
| Top crop | `y < 130` | trimmed by some clients — nothing important there |
|
||
| Mobile avatar | circle, centre `(283, 818)`, r `283` | the community picture sits over the bottom-left in the VK app |
|
||
|
||
`ZONES=1` paints both so a composition can be checked against them.
|
||
|
||
## Rendering technique
|
||
|
||
The camera has pitch only — no roll, no yaw — so a board-space horizontal line always maps to
|
||
a screen-space horizontal line. That makes the perspective warp an exact per-scanline remap of
|
||
a flat texture rather than a general homography: the board surface and the tile faces are each
|
||
drawn once, flat, at high resolution, then replayed band by band (`warpPlane`). Tile thickness
|
||
is filled as real 3D quads between the two planes, so the tiles have visible edges.
|
||
|
||
Two things that are easy to get wrong here and cost a hundredfold in render time:
|
||
`ctx.filter = 'blur(...)'` applies per drawing call, so many blurred shapes must be collected
|
||
into one offscreen layer and blurred once (`blurred`); and `drawImage` re-snapshots a live
|
||
`Canvas` source on every call, so a texture sampled hundreds of times must be baked into an
|
||
`Image` first (`freeze`).
|