settlemaker 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +162 -277
- package/dist/output/geojson-builder.d.ts +1 -1
- package/dist/output/geojson-builder.js +1 -1
- package/dist/settlemaker.browser.js +1 -1
- package/docs/README.md +54 -0
- package/docs/api.md +175 -0
- package/docs/artwork-integration.md +104 -0
- package/docs/current-symbol-library.md +62 -42
- package/docs/development.md +102 -0
- package/docs/examples/gallery/coastal-city.png +0 -0
- package/docs/examples/gallery/coastal-city.svg +4253 -0
- package/docs/examples/gallery/coastal-village.png +0 -0
- package/docs/examples/gallery/coastal-village.svg +1211 -0
- package/docs/examples/gallery/copperline-city.png +0 -0
- package/docs/examples/gallery/copperline-city.svg +1139 -0
- package/docs/examples/gallery/copperline-village.png +0 -0
- package/docs/examples/gallery/copperline-village.svg +2382 -0
- package/docs/examples/gallery/desert-city.png +0 -0
- package/docs/examples/gallery/desert-city.svg +3509 -0
- package/docs/examples/gallery/desert-village.png +0 -0
- package/docs/examples/gallery/desert-village.svg +455 -0
- package/docs/examples/gallery/fixtures.json +449 -0
- package/docs/examples/gallery/temperate-city-night.png +0 -0
- package/docs/examples/gallery/temperate-city-night.svg +3511 -0
- package/docs/examples/gallery/temperate-city.png +0 -0
- package/docs/examples/gallery/temperate-city.svg +3511 -0
- package/docs/examples/gallery/temperate-village.png +0 -0
- package/docs/examples/gallery/temperate-village.svg +3227 -0
- package/docs/examples/gallery/tropical-city.png +0 -0
- package/docs/examples/gallery/tropical-city.svg +3508 -0
- package/docs/examples/gallery/tropical-village.png +0 -0
- package/docs/examples/gallery/tropical-village.svg +3761 -0
- package/docs/examples/gallery/tundra-city.png +0 -0
- package/docs/examples/gallery/tundra-city.svg +3509 -0
- package/docs/examples/gallery/tundra-village.png +0 -0
- package/docs/examples/gallery/tundra-village.svg +1549 -0
- package/docs/fmg-embed-zoom.md +54 -0
- package/docs/gallery.md +139 -0
- package/docs/geojson.md +188 -0
- package/docs/getting-started.md +157 -0
- package/docs/landscape-character-review.md +55 -0
- package/docs/releases/3.0.1.md +18 -0
- package/docs/scene-schema.md +160 -0
- package/docs/schema-v3.md +38 -0
- package/docs/skins.md +52 -0
- package/docs/test-urls.md +39 -0
- package/docs/url-api.md +252 -0
- package/docs/water-context-v1.md +384 -0
- package/package.json +15 -8
- package/scripts/check-docs.mjs +106 -0
- package/scripts/generate-copperline.mjs +255 -0
- package/scripts/generate-examples.mjs +54 -0
- package/scripts/render-copperline.mjs +36 -0
- package/src/output/geojson-builder.ts +1 -1
- package/symbols/copperline/README.md +10 -3
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# City scene and rendering contract
|
|
2
|
+
|
|
3
|
+
`Scene` version **2** is the semantic rendering representation of the **city**
|
|
4
|
+
planner. Villages use `VillageModel` and `renderVillage`; they do not pass through
|
|
5
|
+
`buildScene`. Use [GeoJSON](geojson.md) for exported feature data from both planners,
|
|
6
|
+
and [skins](skins.md) for portable artwork that works in both.
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
City: Model → buildScene → Scene v2 → assembleSvg → SVG
|
|
10
|
+
Village: VillageModel → renderVillage → SVG
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The scene stores geometry, semantic kinds, saved glyph selections and material
|
|
14
|
+
variant indices. It does not contain SVG fragment bodies or a complete theme.
|
|
15
|
+
`assembleSvg` supplies the artwork and appearance. The complete declarations live
|
|
16
|
+
in [src/scene/scene.ts](../src/scene/scene.ts).
|
|
17
|
+
|
|
18
|
+
## Render an existing city model
|
|
19
|
+
|
|
20
|
+
This complete example uses the high-level city generator to retain its coastal
|
|
21
|
+
shift and degradation behaviour, then renders the saved model in another palette:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
import { generateFromBurg, buildScene, assembleSvg, PALETTES } from 'settlemaker';
|
|
25
|
+
|
|
26
|
+
const burg = {
|
|
27
|
+
name: 'Thornwall', population: 5000, biome: 'temperate',
|
|
28
|
+
port: false, citadel: true, walls: true, plaza: true,
|
|
29
|
+
temple: true, shanty: false, capital: false,
|
|
30
|
+
roadBearings: [20, 145, 270],
|
|
31
|
+
};
|
|
32
|
+
const result = generateFromBurg(burg, { seed: 42 });
|
|
33
|
+
const scene = buildScene(result.model, { shift: result.originShift, padding: 20 });
|
|
34
|
+
const svg = assembleSvg(scene, { palette: PALETTES.night });
|
|
35
|
+
console.log(scene.version, svg.length);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`generateSvg(model, options)` is a convenience wrapper around those two stages.
|
|
39
|
+
When rerendering a model from `generateFromBurg`, pass its `originShift` if you
|
|
40
|
+
want the same output coordinate frame. When generating SVG and GeoJSON separately,
|
|
41
|
+
use the same shift and padding for both.
|
|
42
|
+
|
|
43
|
+
For direct control over city generation:
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { mapToGenerationParams, Model, generateSvg, generateGeoJson } from 'settlemaker';
|
|
47
|
+
|
|
48
|
+
// burg is the complete object from the preceding example.
|
|
49
|
+
const params = mapToGenerationParams(burg, 42);
|
|
50
|
+
const model = new Model(params).generate();
|
|
51
|
+
const svg = generateSvg(model);
|
|
52
|
+
const geojson = generateGeoJson(model);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`GenerationParams` is a TypeScript interface. It cannot be called with `new`.
|
|
56
|
+
This direct path does not reproduce the two-pass coastal output translation
|
|
57
|
+
performed by `generateFromBurg`; use that high-level function when you want it.
|
|
58
|
+
|
|
59
|
+
## Scene fields
|
|
60
|
+
|
|
61
|
+
| Field | Meaning |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `version` | Literal 2. |
|
|
64
|
+
| `seed`, `population`, optional `name`, `biome` | Generating context. |
|
|
65
|
+
| `bounds` | Local output bounds. |
|
|
66
|
+
| `metersPerUnit` | Optional city scale estimate for rendering, not a survey. |
|
|
67
|
+
| `buildingCapacity` | Optional ordinary-building budget accounting. |
|
|
68
|
+
| `layers` | The geometry and instance arrays described below. |
|
|
69
|
+
|
|
70
|
+
| Layer | Contents |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| `water` | `{rings, synthetic}`; even-odd output rings retain islands. |
|
|
73
|
+
| `fields` | Polygon `ring`, `angleDeg`, optional native `glyph` and `hatch`. Pattern pitch is independent of parcel size. |
|
|
74
|
+
| `furrows` | Deprecated array; current scenes leave it empty and use field direction instead. |
|
|
75
|
+
| `greens` | Polygon rings with optional planned paths and path width. |
|
|
76
|
+
| `vegetation` | Position, glyph/legacy `kind`, scale and rotation. |
|
|
77
|
+
| `symbols` | Glyph `id`, position, scale, optional `scaleY`, `buildingId`, `materialVariant`, rotation, and `zBand`. |
|
|
78
|
+
| `roads` | Paths with `kind: 'artery' | 'road' | 'alley'` and optional width. |
|
|
79
|
+
| `buildings` | Polygon, semantic ward `kind`, `landmark`, optional `id` and `glyphBacked`. |
|
|
80
|
+
| `piers` | Polygon rings. |
|
|
81
|
+
| `walls` | Polylines, towers, gates and `large`; optional curtain/rubble/palisade material. Gates retain route IDs. |
|
|
82
|
+
| `bridges` | Optional on v2 scenes; bank-to-bank path, width and ID. |
|
|
83
|
+
|
|
84
|
+
An instance's scale gives its art-box size in scene units; artwork coordinates
|
|
85
|
+
come from the selected glyph's view box and anchor. Preserve non-square boxes and
|
|
86
|
+
`scaleY`. Native field tiles may have a 32-unit box, structure slots commonly 64,
|
|
87
|
+
and downloadable authoring assets can use other dimensions. There is no universal
|
|
88
|
+
2×2 authoring rule for current glyphs.
|
|
89
|
+
|
|
90
|
+
## Appearance and artwork
|
|
91
|
+
|
|
92
|
+
`assembleSvg(scene, options)` accepts `palette`, partial `theme`, `skin`,
|
|
93
|
+
`skinBiome`, `assetSet`, `symbols` and `clipId`. `generateSvg` also accepts scene
|
|
94
|
+
`padding` and `shift`.
|
|
95
|
+
|
|
96
|
+
The default asset set is **`SETTLEMENT_SET`**, containing the current native biome
|
|
97
|
+
artwork. `REFINED_SET` and `SCHEMATIC_SET` remain explicit lower-level alternatives.
|
|
98
|
+
`assetSetFor` returns the current default. `paletteForBiome` currently supplies
|
|
99
|
+
its palette fallback; the artwork and material selection provide much of the
|
|
100
|
+
visible biome variation. Do not assume each biome corresponds to a different
|
|
101
|
+
named city palette.
|
|
102
|
+
|
|
103
|
+
For an artist-facing integration, prefer `createSkin` and the [skin contract](skins.md).
|
|
104
|
+
It validates portable JSON, allowed SVG fragments and exact slot names without
|
|
105
|
+
requiring changes to the asset lookup code. An explicit city `assetSet` overrides
|
|
106
|
+
a skin's artwork. Skin colours/biomes and renderer overrides follow the precedence
|
|
107
|
+
in the skin guide.
|
|
108
|
+
|
|
109
|
+
`AssetSet` remains a lower-level API for trusted code. It has a name, legacy
|
|
110
|
+
semantic `symbols`, optional `patterns`, `glyphs`, placement `manifest` and
|
|
111
|
+
`refined` styling flag. Its legacy symbols use a unit box, while `glyphs` carry
|
|
112
|
+
explicit view boxes and anchors. Do not apply the legacy unit-box convention to
|
|
113
|
+
portable skin glyphs.
|
|
114
|
+
|
|
115
|
+
## SVG styling and geometry
|
|
116
|
+
|
|
117
|
+
Current SVGs combine group CSS, material custom properties and presentation
|
|
118
|
+
attributes inside native or procedural artwork. They are not wholly styled by
|
|
119
|
+
one CSS block, and changing a group fill does not recolour every roof or tree.
|
|
120
|
+
Use documented theme/token controls instead of rewriting arbitrary SVG attributes.
|
|
121
|
+
|
|
122
|
+
City groups include fields, greens, water, roads, buildings, landmarks, shadows,
|
|
123
|
+
placed symbols/marks, vegetation and walls. Some groups are omitted when empty;
|
|
124
|
+
procedural artwork can add nested groups, masks and patterns. These group names
|
|
125
|
+
are not the village renderer's DOM contract. For city building identity, emitted
|
|
126
|
+
`data-building-id` attributes connect applicable polygons/symbols to GeoJSON IDs.
|
|
127
|
+
Use GeoJSON for semantic feature queries rather than inferring a feature solely
|
|
128
|
+
from its colour or DOM position.
|
|
129
|
+
|
|
130
|
+
`symbols: false` hides placed city symbols and overlay marks. It leaves vegetation
|
|
131
|
+
and actual building polygons available. It is not a universal switch to remove
|
|
132
|
+
all embedded artwork.
|
|
133
|
+
|
|
134
|
+
## Embedding SVG
|
|
135
|
+
|
|
136
|
+
The `data-bg="paper"` rectangle is used by the tile cropper. Preserve it when
|
|
137
|
+
post-processing. Village roots also declare `data-px-per-metre`; use it to retain
|
|
138
|
+
their physical scale through tiling.
|
|
139
|
+
|
|
140
|
+
Generated SVG IDs are scoped to a document. When displaying several maps, prefer
|
|
141
|
+
separate `<img>` documents (including SVG Blob URLs), `<object>` elements or
|
|
142
|
+
iframes. A unique city `clipId` only changes selected clipping/pattern IDs; it
|
|
143
|
+
does not namespace every glyph, CSS selector or village ID. It is insufficient
|
|
144
|
+
by itself to make multiple inline maps independent. A custom inline compositor
|
|
145
|
+
must consistently namespace IDs, references and applicable styles.
|
|
146
|
+
|
|
147
|
+
SVG output is self-contained for artwork. A browser host is responsible for
|
|
148
|
+
responsive sizing, pan/zoom, downloads and visible errors. Those behaviours are
|
|
149
|
+
not implemented by importing the generator alone.
|
|
150
|
+
|
|
151
|
+
## Evolution and saved scenes
|
|
152
|
+
|
|
153
|
+
Optional fields such as `layers.bridges`, field `glyph`, wall `material` and symbol
|
|
154
|
+
`materialVariant` extend Scene v2. Consumers should handle their absence in older
|
|
155
|
+
saved scenes. Breaking changes to existing field meaning or structure require a
|
|
156
|
+
scene version change.
|
|
157
|
+
|
|
158
|
+
A saved scene is not a complete reproduction package: retain the generator
|
|
159
|
+
version, skin definition and renderer options too. A `SettlementSkin` runtime
|
|
160
|
+
handle must be recreated from JSON in the module instance doing the rendering.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Historical GeoJSON schema v3
|
|
2
|
+
|
|
3
|
+
This page records the city-output transition introduced in release 0.4.0.
|
|
4
|
+
**Current output uses schema 4.** Use the [current GeoJSON reference](geojson.md)
|
|
5
|
+
for new integrations, including village coordinates, road widths and diagnostics.
|
|
6
|
+
Do not gate current output on `schema_version === 3`.
|
|
7
|
+
|
|
8
|
+
## What changed in v3
|
|
9
|
+
|
|
10
|
+
City output gained POI Point features, building and street IDs, ID-prefix
|
|
11
|
+
metadata, and the hamlet/town POI density marker. Existing city properties such
|
|
12
|
+
as `wardType` and `streetType` retained camelCase; additions such as `building_id`
|
|
13
|
+
and `poi_id` used snake_case. That mixture remains deliberate in current output.
|
|
14
|
+
|
|
15
|
+
Later city releases added symbol-backed floating wells, markets and mills, then
|
|
16
|
+
capacity accounting and other optional schema-4 fields. Later village releases
|
|
17
|
+
introduced a different planner and its own feature properties. The historic
|
|
18
|
+
city-only POI and flat-street descriptions must not be applied to every village.
|
|
19
|
+
|
|
20
|
+
## Stable-ID contract
|
|
21
|
+
|
|
22
|
+
IDs are deterministic for identical inputs and a fixed generator version.
|
|
23
|
+
They are opaque and are not guaranteed stable across upgrades. See
|
|
24
|
+
[current identity and persistence](geojson.md#ids-and-persistence).
|
|
25
|
+
|
|
26
|
+
## Village road cross-sections
|
|
27
|
+
|
|
28
|
+
The documentation formerly appended here now lives in the
|
|
29
|
+
[current village road section](geojson.md#village-road-cross-sections), including
|
|
30
|
+
corridor widths, painted widths, setbacks, bridge decks and route provenance.
|
|
31
|
+
|
|
32
|
+
## Migration for consumers
|
|
33
|
+
|
|
34
|
+
For 3.0.x, check schema **4**, inspect `properties.layer` and geometry types,
|
|
35
|
+
and accept optional fields your application does not need. Branch for the
|
|
36
|
+
[village/city differences](geojson.md), especially POIs, greens, gate features,
|
|
37
|
+
coordinates and IDs. The package version, scene version and URL envelope version
|
|
38
|
+
are separate discriminators; see the [documentation index](README.md#contract-versions).
|
package/docs/skins.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Authoring settlement skins
|
|
2
2
|
|
|
3
|
+
This is the portable **skin format 1** contract for SettleMaker 3.0.x. Start here
|
|
4
|
+
to create a personalised setting; the older `AssetSet` API is for trusted city
|
|
5
|
+
rendering code and is not required to distribute a skin.
|
|
6
|
+
|
|
3
7
|
A skin is a versioned JSON document containing SVG artwork, material colours,
|
|
4
8
|
and optional named biomes. The same skin works with villages and cities in Node
|
|
5
9
|
and the browser. Load it once with `createSkin`, then pass it per request:
|
|
@@ -7,6 +11,13 @@ and the browser. Load it once with `createSkin`, then pass it per request:
|
|
|
7
11
|
```ts
|
|
8
12
|
import { createSkin, generateSettlement } from 'settlemaker';
|
|
9
13
|
|
|
14
|
+
const burg = {
|
|
15
|
+
name: 'Moonfall', population: 300,
|
|
16
|
+
port: false, citadel: false, walls: false, plaza: true,
|
|
17
|
+
temple: true, shanty: false, capital: false,
|
|
18
|
+
roadBearings: [18, 142, 267],
|
|
19
|
+
};
|
|
20
|
+
|
|
10
21
|
// Browser example. Applications choose where to store/host their skin files.
|
|
11
22
|
const response = await fetch('/skins/moon-glass.skin.json');
|
|
12
23
|
if (!response.ok) throw new Error('Could not load skin');
|
|
@@ -22,6 +33,9 @@ In Node, pass `JSON.parse(await readFile(path, 'utf8'))` to `createSkin`.
|
|
|
22
33
|
There is no global registry, automatic network access, or executable plugin code.
|
|
23
34
|
Keep the original JSON to save or distribute the skin; the returned skin is an
|
|
24
35
|
immutable runtime handle. Invalid definitions throw an error naming the property.
|
|
36
|
+
Create the handle in the same module instance that generates or renders the map.
|
|
37
|
+
For a worker, another independently loaded bundle, or saved data, transfer the
|
|
38
|
+
definition JSON and call `createSkin` there. There is no runtime-handle wire format.
|
|
25
39
|
|
|
26
40
|
Start with [Moon Glass](examples/moon-glass.skin.json), a small working example
|
|
27
41
|
that replaces snowy dwellings with crystal homes and colours their surroundings.
|
|
@@ -157,3 +171,41 @@ Skins are currently a library API. A hosting application can add a picker or loa
|
|
|
157
171
|
its own curated files; the URL codec does not serialize skin artwork or fetch skin
|
|
158
172
|
URLs. Distribute your original artwork with your chosen licence information. For
|
|
159
173
|
artwork derived from the bundled symbols, consult [symbols/LICENSE](../symbols/LICENSE).
|
|
174
|
+
|
|
175
|
+
## Build and review your own skin
|
|
176
|
+
|
|
177
|
+
1. Choose a base biome whose terrain behaviour fits the setting. Begin with a
|
|
178
|
+
partial skin so you can evaluate the style before drawing a complete set.
|
|
179
|
+
2. Inspect `SKIN_SLOTS` and the current source SVG for each replacement. Retain the
|
|
180
|
+
slot's view box, orientation and painted extents. Export the inner shapes and,
|
|
181
|
+
for structures, a matching silhouette.
|
|
182
|
+
3. Put replacements under their exact IDs. Biome variants are separate entries:
|
|
183
|
+
a generic house replacement does not automatically replace every snowy or
|
|
184
|
+
desert house. Cover each category that appears in your target biomes.
|
|
185
|
+
4. Add tokens and the separate `village`/`city` theme overrides. Render both engines
|
|
186
|
+
with the same loaded skin and try more than one seed and population.
|
|
187
|
+
5. Validate using `createSkin`, then inspect SVG and raster output for seams,
|
|
188
|
+
clipping, shadows and overlap. Schema validation alone cannot establish visual
|
|
189
|
+
compatibility with a placement footprint.
|
|
190
|
+
6. Distribute the JSON with a README, preview images and your artwork's license and
|
|
191
|
+
credits in adjacent files. Do not add unsupported executable hooks or invent
|
|
192
|
+
top-level schema fields for license text. Record the generator version used for
|
|
193
|
+
testing and the biomes/categories you intentionally cover.
|
|
194
|
+
|
|
195
|
+
The [gallery](gallery.md) demonstrates real library output. Its
|
|
196
|
+
[generator script](../scripts/generate-examples.mjs) shows complete inputs for both
|
|
197
|
+
planners. For original alternative artwork and regeneration commands, inspect
|
|
198
|
+
[Copperline](../symbols/copperline/README.md). Its GPL-3.0-only terms are separate
|
|
199
|
+
from the default collections' rendered-output exception.
|
|
200
|
+
|
|
201
|
+
## Common mistakes
|
|
202
|
+
|
|
203
|
+
| Symptom | Check |
|
|
204
|
+
| --- | --- |
|
|
205
|
+
| Some default buildings remain | Replacement IDs must match exact runtime slots, including biome variants. Partial skins deliberately inherit omissions. |
|
|
206
|
+
| A named biome renders as temperate | Pass the loaded skin on that generation request and use a defined key/alias. Unknown names retain the normal fallback. |
|
|
207
|
+
| A replacement overlaps its neighbour | Check painted extents, not just whether the drawing fits inside the SVG view box. Placement footprints cannot be changed by skin format 1. |
|
|
208
|
+
| The old shadow remains or the replacement has none | Supply a matching `sil`; an omitted silhouette means no shadow for that replaced glyph. |
|
|
209
|
+
| A worker rejects a skin handle | Send the original JSON and recreate the handle inside that worker's module instance. |
|
|
210
|
+
| A road or bridge ignores an SVG replacement | Some features are procedural geometry, outside the slot catalogue. Use the exposed renderer controls where available. |
|
|
211
|
+
| An iframe ignores a skin URL | The URL codec has no skin parameter. Load skins through your application's library integration. |
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Reproducible example URLs
|
|
2
|
+
|
|
3
|
+
The [current gallery](gallery.md) is the public example set for 3.0.x. Its images
|
|
4
|
+
are generated with `generateSettlement`, so population ≤1,000 uses the village
|
|
5
|
+
planner instead of forcing small inputs through the city generator.
|
|
6
|
+
|
|
7
|
+
Each entry includes its population, seed, full SVG and (where supported) a hosted
|
|
8
|
+
preview URL. [fixtures.json](examples/gallery/fixtures.json) records the complete
|
|
9
|
+
burg inputs, generator version and skin choice. Use those inputs with your pinned
|
|
10
|
+
npm package for exact local reproduction; a hosted URL uses the website's deployed
|
|
11
|
+
build and can change appearance when that site updates.
|
|
12
|
+
|
|
13
|
+
## Generate links
|
|
14
|
+
|
|
15
|
+
Use the public `encodeBurgParam` helper described in the [URL contract](url-api.md).
|
|
16
|
+
Supply the complete input and an explicit seed. Route IDs and measured water
|
|
17
|
+
require the compressed input tier; the flat tier cannot preserve them.
|
|
18
|
+
|
|
19
|
+
Copperline examples are direct library calls. The URL protocol does not transport
|
|
20
|
+
skins, so the gallery links those examples to their generated SVG and skin JSON
|
|
21
|
+
instead of presenting a URL that would silently show default artwork.
|
|
22
|
+
|
|
23
|
+
## Regenerate and review
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm ci
|
|
27
|
+
npm run docs:images
|
|
28
|
+
npm run docs:check
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The image command regenerates PNG and SVG files together and rewrites the gallery
|
|
32
|
+
and fixture manifest. Review the actual maps before committing them. PNGs use the
|
|
33
|
+
SVG's supplied CSS-variable fallbacks for rasterizer compatibility.
|
|
34
|
+
|
|
35
|
+
The generator script is [scripts/generate-examples.mjs](../scripts/generate-examples.mjs).
|
|
36
|
+
Older development review fixtures in `scripts/generate-test-urls.ts` and previous
|
|
37
|
+
image files are historical aids, not the current documentation contract or a list
|
|
38
|
+
of unresolved defects. Current behaviour and limits are documented in the
|
|
39
|
+
[API](api.md), [GeoJSON reference](geojson.md) and [water contract](water-context-v1.md).
|
package/docs/url-api.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# URL adapter contract
|
|
2
|
+
|
|
3
|
+
This guide describes the **3.0.x library's URL codec** and the hosting contract
|
|
4
|
+
for a renderer at `https://settlemaker.com/fmg`. The URL envelope version remains
|
|
5
|
+
**1**. The npm library and hosted website have separate release cycles; a package
|
|
6
|
+
publication alone does not establish which generator a website is running.
|
|
7
|
+
|
|
8
|
+
Use the [library API](api.md) when your application generates maps itself. Use a
|
|
9
|
+
URL adapter when a host such as Azgaar's Fantasy Map Generator embeds a separately
|
|
10
|
+
deployed renderer. There is no HTTP JSON generation endpoint or iframe result
|
|
11
|
+
message protocol in this contract.
|
|
12
|
+
|
|
13
|
+
## Endpoints and responsibility
|
|
14
|
+
|
|
15
|
+
| Path | Role |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `/fmg` | Embedded renderer receiving a burg URL. |
|
|
18
|
+
| `/` | Human-facing application; do not use it as the burg-rendering endpoint. |
|
|
19
|
+
| `/symbols` | Artwork browsing, outside the input protocol. |
|
|
20
|
+
|
|
21
|
+
The host supplies the URL and owns its surrounding UI. The renderer parses it,
|
|
22
|
+
generates SVG and displays success, warnings or a visible error. Hosts cannot
|
|
23
|
+
read a cross-origin iframe's model, GeoJSON or JavaScript result. Direct library
|
|
24
|
+
consumers can read those results without an iframe.
|
|
25
|
+
|
|
26
|
+
## Build a complete link
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
import { encodeBurgParam } from 'settlemaker';
|
|
30
|
+
|
|
31
|
+
const burg = {
|
|
32
|
+
name: 'Ashford', population: 300, biome: 'temperate',
|
|
33
|
+
port: false, citadel: false, walls: false, plaza: true,
|
|
34
|
+
temple: true, shanty: false, capital: false,
|
|
35
|
+
roadBearings: [
|
|
36
|
+
{ bearing_deg: 36, kind: 'main', route_id: 'r17', through: true },
|
|
37
|
+
{ bearing_deg: 209, kind: 'main', route_id: 'r17', through: true },
|
|
38
|
+
{ bearing_deg: 117, kind: 'town', route_id: 'r23' },
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
const url = new URL('https://settlemaker.com/fmg');
|
|
42
|
+
url.searchParams.set('i', await encodeBurgParam(burg, 42));
|
|
43
|
+
console.log(url.href); // use as the iframe src or external-open link
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The codec uses Web Platform globals, including
|
|
47
|
+
`CompressionStream('deflate-raw')` and `DecompressionStream('deflate-raw')`.
|
|
48
|
+
Verify support in the host environment. The library provides no compression
|
|
49
|
+
polyfill. The full [input reference](api.md#burg-input) is shared with direct
|
|
50
|
+
library calls; always supply all required flags explicitly.
|
|
51
|
+
|
|
52
|
+
## Compressed payload
|
|
53
|
+
|
|
54
|
+
`i=` contains this envelope:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"v": 1,
|
|
59
|
+
"burg": {
|
|
60
|
+
"name": "Ashford",
|
|
61
|
+
"population": 300,
|
|
62
|
+
"port": false,
|
|
63
|
+
"citadel": false,
|
|
64
|
+
"walls": false,
|
|
65
|
+
"plaza": true,
|
|
66
|
+
"temple": true,
|
|
67
|
+
"shanty": false,
|
|
68
|
+
"capital": false
|
|
69
|
+
},
|
|
70
|
+
"seed": 42
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Encoding is JSON → UTF-8 → raw DEFLATE → unpadded base64url. Raw DEFLATE has no
|
|
75
|
+
zlib/gzip header. `encodeBurgParam` and `decodeBurgParam` implement this envelope;
|
|
76
|
+
`encodeJsonParam` and `decodeJsonParam` provide the generic JSON codec used by
|
|
77
|
+
presentation overrides.
|
|
78
|
+
|
|
79
|
+
`v` must equal 1. `seed` is optional; otherwise generation derives it from the
|
|
80
|
+
burg name. The decoder checks the envelope, a plausible name/population and any
|
|
81
|
+
water context. It is not complete validation of every burg field. Validate
|
|
82
|
+
untrusted integration data before calling generation.
|
|
83
|
+
|
|
84
|
+
`i=` takes precedence over **all flat data fields**, including a flat `seed`.
|
|
85
|
+
A malformed `i=` is an error, not permission to retry with flat fields.
|
|
86
|
+
Presentation parameters remain separate and apply after input decoding.
|
|
87
|
+
|
|
88
|
+
## Roads
|
|
89
|
+
|
|
90
|
+
Send one record per **actual land approach**, with its independently measured
|
|
91
|
+
clockwise bearing from north. Keep both sides of a continuing route even when
|
|
92
|
+
they are not 180 degrees apart; use the same `route_id` for those two records.
|
|
93
|
+
`through: true` is a hint and never creates an exit.
|
|
94
|
+
|
|
95
|
+
| Source meaning | `group` | `kind` |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| Known land-road class | `roads` | `royal`, `main`, `market`, `town`, `local` |
|
|
98
|
+
| Known trail class | `trails` | `trail`, `footpath` |
|
|
99
|
+
| Road group without a class | `roads` | `road` |
|
|
100
|
+
| Trail group without a class | `trails` | `foot` |
|
|
101
|
+
| Sea, air or trade-only network without a land approach | — | Exclude from land approaches. |
|
|
102
|
+
|
|
103
|
+
Do not transmit UI labels such as `"roads / royal"` as a kind. `local` is accepted,
|
|
104
|
+
but an adapter should not invent it for missing source classes. `relief` and
|
|
105
|
+
`followsRiver` carry optional city growth hints. See [road inputs](api.md#roads).
|
|
106
|
+
|
|
107
|
+
Village approaches preserve source provenance as they join shared internal
|
|
108
|
+
streets. Drawn village street classes can change to `town`, `local` or `footpath`;
|
|
109
|
+
a small roadside hamlet can retain a major through road. City outputs use their
|
|
110
|
+
legacy road/foot categories and gate matching. Do not assume identical feature
|
|
111
|
+
counts or names in both engines.
|
|
112
|
+
|
|
113
|
+
An explicit `roadBearings: []` means no external approaches. Omitted approaches
|
|
114
|
+
also produce none in villages; the city planner retains its legacy random-gate
|
|
115
|
+
fallback when the field is absent.
|
|
116
|
+
|
|
117
|
+
## Water and physical units
|
|
118
|
+
|
|
119
|
+
The full [water-context v1 specification](water-context-v1.md) remains the
|
|
120
|
+
normative contract for measured **villages, population 1–1,000**. A city input with
|
|
121
|
+
`waterContext` is rejected with `water-context-unsupported-engine`; the host must
|
|
122
|
+
not strip the object and silently reinterpret its metre geometry as city units.
|
|
123
|
+
|
|
124
|
+
| Input | Village | City |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| `coastlineGeometry` | Filled polygons in burg-local metres | Filled polygons in city-local mesh units |
|
|
127
|
+
| `rivers` | Centreline, positive `widthM`, optional `meander: false` | Not consumed by city generation |
|
|
128
|
+
| `oceanBearing` | Generated coast when explicit/measured inputs do not supersede it | Legacy generated coast fallback |
|
|
129
|
+
| `waterContext` | Validated survey/unknown-unit mode | Rejected |
|
|
130
|
+
|
|
131
|
+
Positive x points east and positive y south. Do not send geographic coordinates
|
|
132
|
+
or assume city-local units are metres. `port` controls infrastructure, not water
|
|
133
|
+
visibility. A `coastal` biome chooses artwork but does not itself add an ocean.
|
|
134
|
+
|
|
135
|
+
A nonempty `coastlineGeometry` array represents **filled areas**, not bank lines.
|
|
136
|
+
Include all relevant water polygons and preserve narrow channels and bends. For
|
|
137
|
+
village rivers, supply a centreline with width or a filled polygon, not duplicate
|
|
138
|
+
representations. `followsRiver` alone creates no water.
|
|
139
|
+
|
|
140
|
+
For measured water, carry summaries, polygon associations, declared geometry
|
|
141
|
+
error and survey coverage together. An empty measured survey suppresses the
|
|
142
|
+
legacy coast; unknown units remain a diagnostic state. URL compression/length
|
|
143
|
+
problems must not fall back to a flat URL that loses measured-water semantics.
|
|
144
|
+
Simplify only within the declared physical error budget, or show an encoding error.
|
|
145
|
+
There is no automatic iframe resurvey protocol.
|
|
146
|
+
|
|
147
|
+
## Flat parameters
|
|
148
|
+
|
|
149
|
+
The flat tier is convenient for manual examples:
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
https://settlemaker.com/fmg?name=Ashford&pop=300&seed=2&biome=temperate&plaza=1&temple=1&roads=18:main,142:town,267:local
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
| Parameter | Parsed meaning |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| `name` | Name; default derived from seed/population if omitted. |
|
|
158
|
+
| `pop` | Finite population; defaults to 300. |
|
|
159
|
+
| `seed` | Optional numeric seed. |
|
|
160
|
+
| `port`, `citadel`, `walls`, `plaza`, `temple`, `shanty`, `capital`, `trade` | True for `1` or `true`; otherwise false. |
|
|
161
|
+
| `biome` | Input biome name. |
|
|
162
|
+
| `urbanDensity`, `coreCapacity` | Optional positive numbers. |
|
|
163
|
+
| `oceanBearing` | Optional finite compass bearing. |
|
|
164
|
+
| `harbourSize` | `large` or `small`. |
|
|
165
|
+
| `roads` | Comma-separated `bearing[:class[:through]]` records. |
|
|
166
|
+
|
|
167
|
+
`roads=36:main:through,209:main:through,117:town` supplies all three approaches.
|
|
168
|
+
Bearings wrap into `[0, 360)`; default class is `main`. Valid flat classes are the
|
|
169
|
+
seven land classes in `ROUTE_CLASS_ORDER`; legacy/group aliases are not accepted
|
|
170
|
+
by this flat syntax. Invalid classes or continuation tokens throw a `roads` error.
|
|
171
|
+
Flat records cannot carry route IDs, relief, river following or measured water.
|
|
172
|
+
|
|
173
|
+
With no data fields, `parseSettlementUrl` chooses a demo input and seed. That
|
|
174
|
+
includes a URL containing only presentation fields. A URL with explicit input and
|
|
175
|
+
a resolved seed is reproducible within the same deployed generator version; the
|
|
176
|
+
bare demo URL intentionally is not.
|
|
177
|
+
|
|
178
|
+
## Presentation
|
|
179
|
+
|
|
180
|
+
| Parameter | Used by | Meaning |
|
|
181
|
+
| --- | --- | --- |
|
|
182
|
+
| `theme` | City | Named palette. |
|
|
183
|
+
| `style` | City | Compressed JSON with partial `RenderTheme` overrides. |
|
|
184
|
+
| `villageTheme` | Village | One of `temperate`, `desert`, `tundra`, `tropical`, `coastal`. |
|
|
185
|
+
|
|
186
|
+
City palette keys are `default`, `classic`, `parchment`, `blueprint`, `bw`, `ink`,
|
|
187
|
+
`night`, `ancient`, `colour`, `simple`. `default` aliases `parchment`; `classic` selects the
|
|
188
|
+
original classic palette. The parser returns the name; a renderer must validate it against
|
|
189
|
+
`PALETTES` and display an unknown-palette error itself.
|
|
190
|
+
|
|
191
|
+
`style` accepts hex colour fields, nullable water colours, finite theme numbers
|
|
192
|
+
and a finite `{dx, dy}` shadow offset. Unknown fields/wrong value shapes are dropped
|
|
193
|
+
by `sanitizeThemeOverrides`. Accepted material keys include `smInk`, `smStone`,
|
|
194
|
+
`smTimber`, `smVoid`, `smCanopy1`, `smCanopy2`, alongside the geometry style fields in
|
|
195
|
+
[RenderTheme](../src/output/render-theme.ts). This is not arbitrary CSS.
|
|
196
|
+
|
|
197
|
+
`villageTheme` changes ground, water, shadows and material tokens without changing
|
|
198
|
+
the input biome's dwelling/landscape selection. An invalid name is a
|
|
199
|
+
`UrlCodecError` with reason `villageTheme`, even when the city engine would run.
|
|
200
|
+
The city presentation fields have no effect on the village renderer, and vice versa.
|
|
201
|
+
|
|
202
|
+
**Skins are a direct library feature.** There is no `skin=` URL option, remote skin
|
|
203
|
+
fetch, or embedded skin definition in this protocol. A host that offers a skin
|
|
204
|
+
picker must explicitly load a definition and pass the resulting handle to the
|
|
205
|
+
library API. There is also no `symbols=` or `fit=` URL option in this parser.
|
|
206
|
+
|
|
207
|
+
## Implementing a renderer
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
import {
|
|
211
|
+
parseSettlementUrl, generateSettlement, PALETTES, villageThemeFor,
|
|
212
|
+
} from 'settlemaker';
|
|
213
|
+
|
|
214
|
+
// In a browser page:
|
|
215
|
+
const parsed = await parseSettlementUrl(new URLSearchParams(location.search));
|
|
216
|
+
const palette = parsed.paletteName === undefined ? undefined : PALETTES[parsed.paletteName];
|
|
217
|
+
if (parsed.paletteName !== undefined && !Object.hasOwn(PALETTES, parsed.paletteName)) {
|
|
218
|
+
throw new Error(`Unknown palette: ${parsed.paletteName}`);
|
|
219
|
+
}
|
|
220
|
+
const result = generateSettlement(parsed.burg, {
|
|
221
|
+
seed: parsed.seedOverride,
|
|
222
|
+
svg: { palette, theme: parsed.themeOverrides },
|
|
223
|
+
village: parsed.villageThemeName
|
|
224
|
+
? { theme: villageThemeFor(parsed.villageThemeName) }
|
|
225
|
+
: undefined,
|
|
226
|
+
});
|
|
227
|
+
// Display result.svg and any result.waterContextResult diagnostics.
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Wrap parsing/generation in your page's error handler. `UrlCodecError.reason` can
|
|
231
|
+
be `base64`, `inflate`, `json`, `version`, `shape`, `villageTheme` or `roads`.
|
|
232
|
+
Fatal water errors use `WaterContextError.waterContextResult`; successful measured
|
|
233
|
+
results can still contain warnings. Keep errors and warnings visible without
|
|
234
|
+
hover or interaction, including inside pointer-disabled previews.
|
|
235
|
+
|
|
236
|
+
The library does not render diagnostic cards for you. An iframe host must provide
|
|
237
|
+
those in its page, and must not assume a source-map popup or console error is a
|
|
238
|
+
usable explanation for an embedded user. See [embedding and zoom](fmg-embed-zoom.md).
|
|
239
|
+
|
|
240
|
+
## Deployment and compatibility
|
|
241
|
+
|
|
242
|
+
Pin the renderer to a tested library release, deploy its diagnostics alongside it,
|
|
243
|
+
then enable corresponding measured-water inputs in the sending application.
|
|
244
|
+
Validate preview and external-open links together. Do not infer website support
|
|
245
|
+
from the npm `latest` tag.
|
|
246
|
+
|
|
247
|
+
Payload v1 evolves through optional additions. Breaking changes to the envelope
|
|
248
|
+
require a new version; decoders reject an unsupported version. Geometry changes
|
|
249
|
+
can occur without a URL or GeoJSON version bump, so save the package/build version
|
|
250
|
+
with any cached preview or generated IDs.
|
|
251
|
+
|
|
252
|
+
[Current generated fixtures and preview links](gallery.md) · [Input API](api.md)
|