A 2,000px radiograph written into an article rendered at whatever width it happened to be — a wall of greyscale in the middle of a sentence, four megabytes to draw it, and no way to look at it properly. Every image in prose is now a 256px thumbnail with the author's label under it, and a click gives it the screen. The viewer puts what is known about the figure beside it: its title, what it shows, and where it came from. `media_assets` gains `source` and `source_url` for that — a citation belongs to the file, because the same figure used in three articles is cited the same way in all three, and a licence that turns out to be wrong is one row to fix rather than three paragraphs to find. Asked for when the figure is opened, not when the page is drawn. And `overlay`: the regions an educator has marked, as vector shapes in normalised coordinates on the unit square, so one drawing is correct in a thumbnail, in the viewer and on a projector. Off until the learner turns it on — marks shown before they have looked answer the question for them. Vectors rather than a second burnt-in picture, for four reasons written down in docs/image-overlays.md. The tool that draws them is next; this is the storage, the contract and the reader's half. On a narrow screen the description stacks above the image rather than beside it, where it can be read before scrolling to the picture. Also here: `classify_question_difficulty`, which labelled all 2,924 questions in batches of twenty-five against a written rubric — 622 easy, 1,634 medium, 668 hard, no failed batches. The column had been NULL on every row since it existed. Migration l2c3d4e5f6a7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
60 lines
2.4 KiB
Markdown
60 lines
2.4 KiB
Markdown
# Image overlays
|
||
|
||
An educator can mark regions on an image — the lead lines on a knee film, the
|
||
level of a narrowing, the border of a lesion — and a learner sees them only when
|
||
they ask. This is the contract for how those marks are stored and drawn.
|
||
|
||
## Why vectors and not a second picture
|
||
|
||
The obvious implementation is a second PNG with the marks burnt in, shown
|
||
instead of the original. It fails four ways: it cannot be turned off (which is
|
||
the whole point — the learner should look first and check second), it cannot be
|
||
corrected without redrawing, it doubles the storage for every marked image, and
|
||
it is correct at exactly one size. Vectors have none of those problems and draw
|
||
as crisply on a phone as on a monitor.
|
||
|
||
## The shape of the data
|
||
|
||
`media_assets.overlay`, JSON, null when nothing is marked:
|
||
|
||
```json
|
||
{
|
||
"shapes": [
|
||
{
|
||
"kind": "path",
|
||
"points": [[0.31, 0.52], [0.34, 0.51], [0.38, 0.53]],
|
||
"color": "#5eead4",
|
||
"width": 0.006,
|
||
"label": "Dense metaphyseal bands"
|
||
},
|
||
{ "kind": "rect", "x": 0.12, "y": 0.4, "w": 0.2, "h": 0.15, "color": "#5eead4" },
|
||
{ "kind": "ellipse", "cx": 0.5, "cy": 0.5, "rx": 0.1, "ry": 0.08, "color": "#f0a53d" },
|
||
{ "kind": "arrow", "points": [[0.2, 0.2], [0.4, 0.35]], "color": "#5eead4" }
|
||
]
|
||
}
|
||
```
|
||
|
||
**Every coordinate is normalised to 0–1** against the image's own width and
|
||
height — never pixels. That is what makes one drawing correct in a 256px
|
||
thumbnail, in the viewer, and on a projector. `width` is normalised too, against
|
||
the image width, so a stroke stays the same relative weight.
|
||
|
||
Four kinds, and no more without a reason: `path` (freehand or polyline, the one
|
||
that traces an anatomical edge), `rect`, `ellipse`, `arrow` (a `path` of exactly
|
||
two points, drawn with a head). `label` is optional on any shape and is what a
|
||
screen reader is given.
|
||
|
||
## Drawing it
|
||
|
||
An SVG with `viewBox="0 0 1 1"` and `preserveAspectRatio="none"`, absolutely
|
||
positioned over the image at the same size. Because the viewBox is the unit
|
||
square, the stored numbers are the SVG's own coordinates and no conversion is
|
||
needed at any size.
|
||
|
||
## Rules
|
||
|
||
- **Off by default.** The learner sees the image, and turns the overlay on.
|
||
Marks shown before the learner has looked answer the question for them.
|
||
- **The original is never modified.** Nothing here writes to the image file.
|
||
- **An overlay is not a caption.** What the marks *mean* belongs in the
|
||
description; the overlay says where.
|