Terrain from Image
scm-js/plugin-image-to-terrain, installed
by default — File ▸ Import ▸ Terrain from Image…, and on the terrain
palette's and the map's context menus Terrain from Image… (into the marked area when the
Cut / Copy / Paste layer has one) and Terrain from Image into Area…, which first has you
drag the target rectangle on the map (api.ui.pickArea) and then opens the dialog with it
selected. In the dialog, Pick on Map… does the same round trip — the dialog closes, you
drag, it reopens with the picture and every setting kept (the state lives in a Session
object outside the dialog for exactly this reason).
The picture can come from a file, Ctrl+V or Paste (a screenshot on the clipboard), a
drop onto the dialog, or a URL (api.ui.loadImage). Fit places it — stretched, fitted
inside (uncovered cells are left alone) or covering the area — with flips and smooth or
nearest sampling (one pixel per tile for pixel art). Adjust is brightness, contrast,
saturation, hue, gamma, auto-levels and invert, applied before matching and shown in the
Source preview. Every terrain in the list has a key colour — what in the picture it
should match; by default its own tiles' average, changed with the swatch or the eyedropper
(arm it on a row, click the source preview), remembered per tileset.
convert.ts is the pure part and that repository's tests/convert.test.ts pins it: resample
to one sample per target cell, adjustSamples, boxBlur, then match every cell in OKLab
(makeMatcher) — Adaptive fits the picture's lightness and chroma ranges onto the
palette's (gain capped so a flat picture is not stretched into noise) and rescales hue so
the murky tile averages' hue spread counts as much as their lightness spread; Exact is
plain distance to the key colours; Brightness bands makes the ticked terrains, in order,
equal bands from the picture's darkest to its brightest cell (a heightmap). The Weigh
slider moves between lightness and hue. Clean-up follows: majorityFilter (despeckle) and
removeSmallRegions (islands below a size join their commonest neighbour). plugin.ts
is the dialog and the transaction: Isometric paints each lattice diamond in the target
with tx.paintIsom — the diamond's terrain is the majority of the four cells around its
centre (diamondTerrain), and terrains are painted low ground first, rare ones last
(paintOrder), so the brush's one-diamond bleed eats into common ground rather than thin
features — and cliffs and shorelines are generated at every boundary; Tiles stamps flat
pairs with tx.stampTerrain and leaves the ISOM alone (Tools ▸ Repair Map… afterwards
rebuilds it, if you want the isometric brush back). One undo entry either way.