scmJS docs

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.