mapwright 0.17.0__tar.gz → 0.18.0__tar.gz
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.
- {mapwright-0.17.0 → mapwright-0.18.0}/CHANGELOG.md +26 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/PKG-INFO +26 -11
- {mapwright-0.17.0 → mapwright-0.18.0}/README.md +25 -10
- mapwright-0.18.0/docs/gallery/theme-citadel-neon.png +0 -0
- mapwright-0.18.0/docs/gallery/theme-citadel-neon.svg +1 -0
- mapwright-0.18.0/docs/gallery/theme-dungeon-blueprint.png +0 -0
- mapwright-0.18.0/docs/gallery/theme-dungeon-blueprint.svg +1 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/examples/gallery.py +8 -4
- {mapwright-0.17.0 → mapwright-0.18.0}/pyproject.toml +1 -1
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/__init__.py +9 -1
- mapwright-0.18.0/src/mapwright/affordances.py +168 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/dungeon_renderer.py +19 -17
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/settlement_renderer.py +28 -46
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/themes.py +114 -0
- mapwright-0.18.0/tests/test_affordances.py +158 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_api_contract.py +3 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_themes.py +74 -2
- {mapwright-0.17.0 → mapwright-0.18.0}/.github/workflows/ci.yml +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/.github/workflows/publish.yml +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/.gitignore +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/LICENSE +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/NOTICE +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/age-old.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/age-old.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/age-young.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/age-young.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/archipelago.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/archipelago.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/arctic.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/arctic.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/README.md +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/city_castle_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/city_large_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/city_town_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/city_village_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/decoration_compass_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/decoration_creature_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/decoration_ship_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/dune_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/hill_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/hill_2.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/manifest.json +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/mountain_mid_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/mountain_old_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/mountain_old_2.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/mountain_young_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/mountain_young_2.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/tree_cactus_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/tree_deciduous_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/tree_deciduous_2.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/tree_pine_1.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/atlas_pack/tree_pine_2.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/citadel.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/citadel.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/continent.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/continent.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/desert.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/desert.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/dungeon.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/dungeon.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/highlands.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/highlands.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/islands.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/islands.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/pangaea.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/pangaea.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/port.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/port.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/regions.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/regions.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/roads.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/roads.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/template-atoll.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/template-atoll.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/template-isthmus.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/template-isthmus.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/theme-blueprint.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/theme-blueprint.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/theme-dune.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/theme-dune.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/theme-neon.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/theme-neon.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/theme-parchment.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/theme-parchment.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/town.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/town.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/tropical.png +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/docs/gallery/tropical.svg +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/examples/benchmark.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/_geometry.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/_graph.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/_serde.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/atlas_renderer.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/config.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/dungeon.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/names.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/regions.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/rng.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/roads.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/settlement.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/svg_renderer.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/src/mapwright/terrain.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_atlas_renderer.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_config.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_dungeon.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_dungeon_renderer.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_geometry.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_graph.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_names.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_properties.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_regions.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_rng.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_roads.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_serialize.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_settlement.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_svg_renderer.py +0 -0
- {mapwright-0.17.0 → mapwright-0.18.0}/tests/test_terrain.py +0 -0
|
@@ -8,6 +8,32 @@ All notable changes to mapwright are documented here. The format follows
|
|
|
8
8
|
`tests/test_api_contract.py`). While the version is `0.x`, minor versions may
|
|
9
9
|
make breaking changes; these will always be noted here.
|
|
10
10
|
|
|
11
|
+
## [0.18.0] — 2026-06-02
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- **Environmental affordances + cell aggregation — `affordances` module.** Two
|
|
15
|
+
domain-neutral helpers for turning terrain into a place's *environment*:
|
|
16
|
+
- `environment_affordances(biome, temperature, moisture)` → neutral
|
|
17
|
+
ecology-level tags (`scarce_water`, `disease_vector`, `predator`,
|
|
18
|
+
`extreme_heat`, …). Biome-base tags plus climate-driven additions, so a
|
|
19
|
+
hot+wet forest reads as a steamy jungle. A host app decides what, if
|
|
20
|
+
anything, tags mean mechanically; this library never touches game rules.
|
|
21
|
+
- `summarize_cells(cells)` → `CellSummary` (dominant biome, mean climate,
|
|
22
|
+
hydrology flags, water fraction, affordances) for a footprint / explored
|
|
23
|
+
area / whole map. Deterministic; ties broken by lowest `Biome` value.
|
|
24
|
+
- New exports: `environment_affordances`, `summarize_cells`, `CellSummary`
|
|
25
|
+
(purely additive to `__all__`).
|
|
26
|
+
- **Themes extended to the town & dungeon renderers.** A `Theme` now carries
|
|
27
|
+
nested `SettlementPalette` + `DungeonPalette` sub-palettes, and
|
|
28
|
+
`SettlementSVGRenderer` / `DungeonSVGRenderer` take a `theme=` just like the
|
|
29
|
+
regional renderer — so one theme skins all three. The four built-ins
|
|
30
|
+
(`parchment`, `neon`, `dune`, `blueprint`) now style towns and dungeons
|
|
31
|
+
cohesively (e.g. a neon citadel, a blueprint dungeon). Sub-palettes default to
|
|
32
|
+
parchment, so a theme that only restyles the regional map still drives the
|
|
33
|
+
other renderers, and the default output is **byte-identical**. `SettlementPalette`
|
|
34
|
+
/ `DungeonPalette` are importable from `mapwright.themes` for authoring custom
|
|
35
|
+
themes. Gallery gains a themed town + dungeon.
|
|
36
|
+
|
|
11
37
|
## [0.17.0] — 2026-06-02
|
|
12
38
|
|
|
13
39
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mapwright
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.0
|
|
4
4
|
Summary: Domain-neutral procedural fantasy map & world generation: Voronoi terrain, hydraulic erosion, biomes, rivers, Markov place-names, and shaded-relief SVG.
|
|
5
5
|
Project-URL: Homepage, https://github.com/sligara7/mapwright
|
|
6
6
|
Project-URL: Repository, https://github.com/sligara7/mapwright
|
|
@@ -61,6 +61,15 @@ re-skinned by swapping a `Theme` (palette + biome vocabulary). No regeneration:
|
|
|
61
61
|
</tr>
|
|
62
62
|
</table>
|
|
63
63
|
|
|
64
|
+
The same `theme=` drives the **town and dungeon** renderers too — one skin across all three:
|
|
65
|
+
|
|
66
|
+
<table>
|
|
67
|
+
<tr>
|
|
68
|
+
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/theme-citadel-neon.png" alt="neon-themed walled citadel"><br><sub><code>Settlement, theme="neon"</code></sub></td>
|
|
69
|
+
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/theme-dungeon-blueprint.png" alt="blueprint-themed dungeon"><br><sub><code>Dungeon, theme="blueprint"</code></sub></td>
|
|
70
|
+
</tr>
|
|
71
|
+
</table>
|
|
72
|
+
|
|
64
73
|
Below: deterministic shaded-relief renders of each built-in preset (or a dungeon),
|
|
65
74
|
produced by [`examples/gallery.py`](examples/gallery.py):
|
|
66
75
|
|
|
@@ -218,14 +227,15 @@ Settlement presets: `hamlet`, `village`, `town`, `city`, `port`, `citadel`.
|
|
|
218
227
|
| `RegionalTerrainGenerator` | Voronoi cells (Lloyd-relaxed) → **tectonic-plate** heightmap (organic coasts + mountain ranges at plate collisions; percentile sea level) → Planchon–Darboux depression fill → flux + hydraulic/creep erosion → rivers + inland lakes → latitude/elevation climate with **rain-shadow** → Whittaker biomes. |
|
|
219
228
|
| `compute_cell_polygons` | Reconstructs convex Voronoi polygons (half-plane clipping) for vector rendering. |
|
|
220
229
|
| `RegionalSVGRenderer` | Shaded-relief (hillshade) SVG: biome polygons, coastline, rivers, roads, labelled markers. Takes a `theme=`. |
|
|
221
|
-
| `Theme` / `THEMES` | A render palette + biome vocabulary; re-skins the same terrain (parchment / neon / dune / blueprint, or your own). The "Dominant Medium" layer. |
|
|
230
|
+
| `Theme` / `THEMES` | A render palette + biome vocabulary; re-skins the same terrain — and its towns and dungeons — via one `theme=` (parchment / neon / dune / blueprint, or your own). The "Dominant Medium" layer. |
|
|
231
|
+
| `environment_affordances` / `summarize_cells` | Neutral *ecology* helpers: biome + climate → affordance tags (`scarce_water`, `predator`, …); reduce a set of cells to a `CellSummary` (dominant biome, mean climate, hydrology, affordances). A host decides what tags mean mechanically. |
|
|
222
232
|
| `AtlasRenderer` / `ArtPack` | Hand-drawn / themed PNG: stamps symbols from an external *art pack* (mountains, forests, hills, settlements, sea decorations) onto the terrain. mapwright ships no art — a pack is a skin. Needs `pip install "mapwright[atlas]"`. |
|
|
223
233
|
| `RegionalRoadGenerator` | Connects settlement sites with trade routes — an MST whose edges are A*-routed over the terrain (avoids sea, climbs/crosses rivers at a cost). |
|
|
224
234
|
| `RegionGenerator` | Partitions land into named factions/territories: spread capitals seed a flood fill over the land graph (sea divides them); each `Region` is Markov-named. |
|
|
225
235
|
| `DungeonGenerator` | BSP-partitioned rooms + minimum-spanning-tree corridors → rooms, corridor cells, and a walkable grid (with `Dungeon.ascii()`). |
|
|
226
|
-
| `DungeonSVGRenderer` | Renders a `Dungeon` to SVG: walls, carved floor, room outlines, optional tile grid and per-room labels. |
|
|
236
|
+
| `DungeonSVGRenderer` | Renders a `Dungeon` to SVG: walls, carved floor, room outlines, optional tile grid and per-room labels. Takes a `theme=`. |
|
|
227
237
|
| `SettlementGenerator` | Self-contained town layout: an organic footprint divided into named Voronoi **wards** (market, docks, …), each subdivided into building **lots**, a **street** network (MST over ward adjacency + main roads from gates to the market), an optional defensive **wall** (towers + gate gaps, opened at the harbour when coastal), and optional coastline. |
|
|
228
|
-
| `SettlementSVGRenderer` | Renders a `Settlement` to SVG: sea, footprint, kind-coloured wards, building lots, streets, wall with towers/gatehouses, labels. |
|
|
238
|
+
| `SettlementSVGRenderer` | Renders a `Settlement` to SVG: sea, footprint, kind-coloured wards, building lots, streets, wall with towers/gatehouses, labels. Takes a `theme=`. |
|
|
229
239
|
|
|
230
240
|
Everything is neutral: `RegionalTerrainGenerator` returns a `TerrainResult` of `TerrainCell`s
|
|
231
241
|
(each with a `Biome`), and you decide how a `Biome` maps to your world.
|
|
@@ -288,18 +298,23 @@ regenerating anything. The neutral `Biome` enum never changes; a theme just deci
|
|
|
288
298
|
each biome looks and is named:
|
|
289
299
|
|
|
290
300
|
```python
|
|
291
|
-
from mapwright import RegionalSVGRenderer, THEMES
|
|
301
|
+
from mapwright import RegionalSVGRenderer, SettlementSVGRenderer, DungeonSVGRenderer, THEMES
|
|
292
302
|
|
|
293
|
-
svg
|
|
303
|
+
svg = RegionalSVGRenderer(theme="neon").render(terrain, markers, roads=roads)
|
|
304
|
+
town = SettlementSVGRenderer(theme="neon").render(settlement) # same theme skins the town
|
|
305
|
+
dgn = DungeonSVGRenderer(theme="blueprint").render(dungeon) # …and the dungeon
|
|
294
306
|
# built-ins: "parchment" (default), "neon" (Tron/digital-grid), "dune" (sand), "blueprint"
|
|
295
307
|
THEMES["neon"].biome_label(Biome.OCEAN) # -> "Void" (the vocabulary layer)
|
|
296
308
|
```
|
|
297
309
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
310
|
+
All three renderers take the same `theme=`, so one theme skins the world map, its towns,
|
|
311
|
+
and its dungeons together (a `Theme` carries nested `SettlementPalette` + `DungeonPalette`,
|
|
312
|
+
importable from `mapwright.themes` for custom packs). A `Theme` is plain hex-string data
|
|
313
|
+
(JSON-friendly), so a host — or the same image service that makes art packs — can author
|
|
314
|
+
new ones. This is the "Dominant Medium" idea from mapwright's longer-term vision: a sand
|
|
315
|
+
planet, a digital grid, and an irradiated waste are the *same map* wearing different skins.
|
|
316
|
+
Pair a theme with a matching `ArtPack` for a full restyle of both the vector and hand-drawn
|
|
317
|
+
renders.
|
|
303
318
|
|
|
304
319
|
## Determinism
|
|
305
320
|
|
|
@@ -34,6 +34,15 @@ re-skinned by swapping a `Theme` (palette + biome vocabulary). No regeneration:
|
|
|
34
34
|
</tr>
|
|
35
35
|
</table>
|
|
36
36
|
|
|
37
|
+
The same `theme=` drives the **town and dungeon** renderers too — one skin across all three:
|
|
38
|
+
|
|
39
|
+
<table>
|
|
40
|
+
<tr>
|
|
41
|
+
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/theme-citadel-neon.png" alt="neon-themed walled citadel"><br><sub><code>Settlement, theme="neon"</code></sub></td>
|
|
42
|
+
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/theme-dungeon-blueprint.png" alt="blueprint-themed dungeon"><br><sub><code>Dungeon, theme="blueprint"</code></sub></td>
|
|
43
|
+
</tr>
|
|
44
|
+
</table>
|
|
45
|
+
|
|
37
46
|
Below: deterministic shaded-relief renders of each built-in preset (or a dungeon),
|
|
38
47
|
produced by [`examples/gallery.py`](examples/gallery.py):
|
|
39
48
|
|
|
@@ -191,14 +200,15 @@ Settlement presets: `hamlet`, `village`, `town`, `city`, `port`, `citadel`.
|
|
|
191
200
|
| `RegionalTerrainGenerator` | Voronoi cells (Lloyd-relaxed) → **tectonic-plate** heightmap (organic coasts + mountain ranges at plate collisions; percentile sea level) → Planchon–Darboux depression fill → flux + hydraulic/creep erosion → rivers + inland lakes → latitude/elevation climate with **rain-shadow** → Whittaker biomes. |
|
|
192
201
|
| `compute_cell_polygons` | Reconstructs convex Voronoi polygons (half-plane clipping) for vector rendering. |
|
|
193
202
|
| `RegionalSVGRenderer` | Shaded-relief (hillshade) SVG: biome polygons, coastline, rivers, roads, labelled markers. Takes a `theme=`. |
|
|
194
|
-
| `Theme` / `THEMES` | A render palette + biome vocabulary; re-skins the same terrain (parchment / neon / dune / blueprint, or your own). The "Dominant Medium" layer. |
|
|
203
|
+
| `Theme` / `THEMES` | A render palette + biome vocabulary; re-skins the same terrain — and its towns and dungeons — via one `theme=` (parchment / neon / dune / blueprint, or your own). The "Dominant Medium" layer. |
|
|
204
|
+
| `environment_affordances` / `summarize_cells` | Neutral *ecology* helpers: biome + climate → affordance tags (`scarce_water`, `predator`, …); reduce a set of cells to a `CellSummary` (dominant biome, mean climate, hydrology, affordances). A host decides what tags mean mechanically. |
|
|
195
205
|
| `AtlasRenderer` / `ArtPack` | Hand-drawn / themed PNG: stamps symbols from an external *art pack* (mountains, forests, hills, settlements, sea decorations) onto the terrain. mapwright ships no art — a pack is a skin. Needs `pip install "mapwright[atlas]"`. |
|
|
196
206
|
| `RegionalRoadGenerator` | Connects settlement sites with trade routes — an MST whose edges are A*-routed over the terrain (avoids sea, climbs/crosses rivers at a cost). |
|
|
197
207
|
| `RegionGenerator` | Partitions land into named factions/territories: spread capitals seed a flood fill over the land graph (sea divides them); each `Region` is Markov-named. |
|
|
198
208
|
| `DungeonGenerator` | BSP-partitioned rooms + minimum-spanning-tree corridors → rooms, corridor cells, and a walkable grid (with `Dungeon.ascii()`). |
|
|
199
|
-
| `DungeonSVGRenderer` | Renders a `Dungeon` to SVG: walls, carved floor, room outlines, optional tile grid and per-room labels. |
|
|
209
|
+
| `DungeonSVGRenderer` | Renders a `Dungeon` to SVG: walls, carved floor, room outlines, optional tile grid and per-room labels. Takes a `theme=`. |
|
|
200
210
|
| `SettlementGenerator` | Self-contained town layout: an organic footprint divided into named Voronoi **wards** (market, docks, …), each subdivided into building **lots**, a **street** network (MST over ward adjacency + main roads from gates to the market), an optional defensive **wall** (towers + gate gaps, opened at the harbour when coastal), and optional coastline. |
|
|
201
|
-
| `SettlementSVGRenderer` | Renders a `Settlement` to SVG: sea, footprint, kind-coloured wards, building lots, streets, wall with towers/gatehouses, labels. |
|
|
211
|
+
| `SettlementSVGRenderer` | Renders a `Settlement` to SVG: sea, footprint, kind-coloured wards, building lots, streets, wall with towers/gatehouses, labels. Takes a `theme=`. |
|
|
202
212
|
|
|
203
213
|
Everything is neutral: `RegionalTerrainGenerator` returns a `TerrainResult` of `TerrainCell`s
|
|
204
214
|
(each with a `Biome`), and you decide how a `Biome` maps to your world.
|
|
@@ -261,18 +271,23 @@ regenerating anything. The neutral `Biome` enum never changes; a theme just deci
|
|
|
261
271
|
each biome looks and is named:
|
|
262
272
|
|
|
263
273
|
```python
|
|
264
|
-
from mapwright import RegionalSVGRenderer, THEMES
|
|
274
|
+
from mapwright import RegionalSVGRenderer, SettlementSVGRenderer, DungeonSVGRenderer, THEMES
|
|
265
275
|
|
|
266
|
-
svg
|
|
276
|
+
svg = RegionalSVGRenderer(theme="neon").render(terrain, markers, roads=roads)
|
|
277
|
+
town = SettlementSVGRenderer(theme="neon").render(settlement) # same theme skins the town
|
|
278
|
+
dgn = DungeonSVGRenderer(theme="blueprint").render(dungeon) # …and the dungeon
|
|
267
279
|
# built-ins: "parchment" (default), "neon" (Tron/digital-grid), "dune" (sand), "blueprint"
|
|
268
280
|
THEMES["neon"].biome_label(Biome.OCEAN) # -> "Void" (the vocabulary layer)
|
|
269
281
|
```
|
|
270
282
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
283
|
+
All three renderers take the same `theme=`, so one theme skins the world map, its towns,
|
|
284
|
+
and its dungeons together (a `Theme` carries nested `SettlementPalette` + `DungeonPalette`,
|
|
285
|
+
importable from `mapwright.themes` for custom packs). A `Theme` is plain hex-string data
|
|
286
|
+
(JSON-friendly), so a host — or the same image service that makes art packs — can author
|
|
287
|
+
new ones. This is the "Dominant Medium" idea from mapwright's longer-term vision: a sand
|
|
288
|
+
planet, a digital grid, and an irradiated waste are the *same map* wearing different skins.
|
|
289
|
+
Pair a theme with a matching `ArtPack` for a full restyle of both the vector and hand-drawn
|
|
290
|
+
renders.
|
|
276
291
|
|
|
277
292
|
## Determinism
|
|
278
293
|
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="630" height="630" viewBox="0 0 630 630"><rect width="630" height="630" fill="#05060a"/><polygon points="111.4,358.6 115.7,328.0 124.8,297.3 138.4,267.1 156.2,238.1 177.7,210.9 202.5,186.2 230.1,164.5 259.9,146.3 291.1,132.1 323.1,122.4 355.1,117.5 386.1,117.7 415.4,123.0 441.9,133.2 465.1,148.1 484.2,167.3 498.7,190.0 508.5,215.6 513.3,243.4 513.2,272.6 508.4,302.4 499.1,332.2 485.6,361.3 468.3,389.2 447.5,415.5 423.7,439.5 397.2,460.7 368.5,478.8 338.3,493.2 307.0,503.4 275.6,509.0 244.8,509.8 215.4,505.6 188.4,496.4 164.5,482.4 144.4,464.0 128.8,441.7 117.9,416.2 112.1,388.2" fill="#0a1420"/><g stroke="#00e5ff" stroke-width="1" stroke-linejoin="round"><polygon points="130.7,284.2 138.4,267.1 156.2,238.1 177.7,210.9 202.5,186.2 209.4,180.8 240.2,218.5 213.3,295.6 165.6,301.6" fill="#0e2838"/><polygon points="364.5,480.7 338.3,493.2 307.0,503.4 295.9,505.4 287.4,466.5 308.8,441.0 346.6,447.3" fill="#0e2838"/><polygon points="490.3,176.9 498.7,190.0 502.3,199.5 440.6,262.5 415.8,262.8 396.7,244.9 390.9,208.8" fill="#0e2838"/><polygon points="213.3,295.6 240.2,218.5 285.7,231.5 273.8,300.1 230.8,307.9" fill="#1b2a52"/><polygon points="488.2,355.6 485.6,361.3 468.3,389.2 447.5,415.5 427.8,435.4 395.2,403.3 423.3,347.4" fill="#0e2838"/><polygon points="427.8,435.4 423.7,439.5 397.2,460.7 368.5,478.8 364.5,480.7 346.6,447.3 371.0,403.0 395.2,403.3" fill="#10202e"/><polygon points="385.3,200.7 390.9,208.8 396.7,244.9 338.3,270.8 292.9,230.2 333.0,182.0" fill="#0e2233"/><polygon points="230.9,483.1 190.8,454.3 189.8,449.0 192.2,441.3 239.7,423.5 254.5,457.4" fill="#0e2233"/><polygon points="333.6,299.1 367.6,330.1 344.0,375.2 323.1,375.7 297.4,345.7 299.5,317.4" fill="#103a4a"/><polygon points="229.8,507.6 215.4,505.6 188.4,496.4 175.8,489.0 190.8,454.3 230.9,483.1" fill="#0e2233"/><polygon points="346.6,447.3 308.8,441.0 298.0,408.6 323.1,375.7 344.0,375.2 371.0,403.0" fill="#10202e"/><polygon points="338.3,270.8 396.7,244.9 415.8,262.8 402.0,324.2 367.6,330.1 333.6,299.1" fill="#0e2233"/><polygon points="192.2,441.3 189.8,449.0 136.4,452.5 128.8,441.7 117.9,416.2 114.9,401.5 166.5,392.0" fill="#0e2233"/><polygon points="298.0,408.6 308.8,441.0 287.4,466.5 254.5,457.4 239.7,423.5 248.0,400.0" fill="#1b2a52"/><polygon points="416.3,123.3 441.9,133.2 465.1,148.1 484.2,167.3 490.3,176.9 390.9,208.8 385.3,200.7" fill="#10202e"/><polygon points="295.9,505.4 275.6,509.0 244.8,509.8 229.8,507.6 230.9,483.1 254.5,457.4 287.4,466.5" fill="#13314a"/><polygon points="503.6,317.6 499.1,332.2 488.2,355.6 423.3,347.4 402.0,324.2 415.8,262.8 440.6,262.5" fill="#0e2233"/><polygon points="423.3,347.4 395.2,403.3 371.0,403.0 344.0,375.2 367.6,330.1 402.0,324.2" fill="#1b2a52"/><polygon points="209.4,180.8 230.1,164.5 259.9,146.3 291.1,132.1 314.4,125.0 333.0,182.0 292.9,230.2 285.7,231.5 240.2,218.5" fill="#0e2233"/><polygon points="297.4,345.7 323.1,375.7 298.0,408.6 248.0,400.0 237.7,375.9 241.5,367.5" fill="#10202e"/><polygon points="165.6,301.6 213.3,295.6 230.8,307.9 241.5,367.5 237.7,375.9 180.0,376.5" fill="#13314a"/><polygon points="314.4,125.0 323.1,122.4 355.1,117.5 386.1,117.7 415.4,123.0 416.3,123.3 385.3,200.7 333.0,182.0" fill="#0e2233"/><polygon points="230.8,307.9 273.8,300.1 299.5,317.4 297.4,345.7 241.5,367.5" fill="#0e2838"/><polygon points="111.4,358.6 115.7,328.0 124.8,297.3 130.7,284.2 165.6,301.6 180.0,376.5 166.5,392.0 114.9,401.5 112.1,388.2" fill="#0e2838"/><polygon points="502.3,199.5 508.5,215.6 513.3,243.4 513.2,272.6 508.4,302.4 503.6,317.6 440.6,262.5" fill="#10202e"/><polygon points="273.8,300.1 285.7,231.5 292.9,230.2 338.3,270.8 333.6,299.1 299.5,317.4" fill="#0e2233"/><polygon points="237.7,375.9 248.0,400.0 239.7,423.5 192.2,441.3 166.5,392.0 180.0,376.5" fill="#0e2838"/><polygon points="189.8,449.0 190.8,454.3 175.8,489.0 164.5,482.4 144.4,464.0 136.4,452.5" fill="#0e2838"/></g><g fill="#0a3a4a" stroke="#00e5ff" stroke-width="0.5" stroke-linejoin="round"><polygon points="227.9,245.3 226.1,250.3 204.5,242.8 206.2,237.8"/><polygon points="234.4,227.4 229.9,240.3 221.7,237.4 226.2,224.5"/><polygon points="216.8,235.6 207.9,232.5 212.3,219.8 221.2,222.9"/><polygon points="220.0,198.0 228.1,207.9 218.1,216.1 214.1,214.7"/><polygon points="231.6,211.4 237.7,218.9 236.3,223.0 223.1,218.3"/><polygon points="196.3,196.9 204.6,188.6 208.9,185.2 215.5,193.2 212.2,202.4"/><polygon points="206.6,206.0 211.6,207.7 207.3,220.1 202.3,218.4"/><polygon points="192.1,201.5 202.0,205.0 198.1,216.3 188.1,212.8"/><polygon points="205.2,224.3 199.3,241.2 193.3,239.1 199.2,222.2"/><polygon points="188.2,237.1 180.8,234.5 186.5,218.0 194.0,220.6"/><polygon points="166.9,229.5 180.0,212.9 183.3,209.6 175.3,232.4"/><polygon points="177.9,239.7 185.3,242.3 178.9,260.7 171.5,258.1"/><polygon points="157.5,240.6 158.2,239.5 162.7,233.7 173.0,237.3 170.3,245.1"/><polygon points="151.0,251.4 155.0,245.0 168.5,249.7 166.1,256.7"/><polygon points="169.2,262.8 177.8,265.9 174.6,275.2 165.9,272.2"/><polygon points="143.9,263.7 148.5,256.3 164.1,261.8 161.3,269.8"/><polygon points="156.2,274.2 172.3,279.8 169.7,287.3 152.6,281.4"/><polygon points="168.1,292.6 165.9,298.9 148.0,290.0 149.9,286.2"/><polygon points="134.6,282.8 140.9,268.9 150.6,272.3 143.2,287.1"/><polygon points="212.3,289.3 211.1,292.8 193.7,295.0 192.1,282.3"/><polygon points="182.8,297.9 170.1,299.5 172.0,294.1"/><polygon points="187.8,295.0 174.7,290.4 179.2,277.6 185.9,279.9"/><polygon points="190.7,243.8 196.6,245.8 190.6,263.0 184.7,261.0"/><polygon points="201.6,247.6 208.0,249.9 202.0,266.9 195.7,264.7"/><polygon points="200.2,272.0 198.0,278.3 180.8,272.3 183.0,266.0"/><polygon points="217.9,274.5 214.5,284.1 203.0,280.1 206.4,270.4"/><polygon points="213.2,252.0 224.0,255.8 219.4,269.0 208.6,265.3"/><polygon points="307.5,500.1 306.3,500.5 298.2,501.9 294.7,485.9 303.9,483.9"/><polygon points="317.3,497.6 312.6,499.1 308.9,482.1 313.7,481.1"/><polygon points="293.6,479.9 291.6,471.0 309.8,467.1 311.8,475.9"/><polygon points="328.5,494.2 321.5,496.5 319.3,486.2 326.5,484.7"/><polygon points="346.5,486.6 337.4,490.9 332.7,492.4 330.9,484.1 345.2,481.0"/><polygon points="317.4,474.2 315.5,465.4 329.9,462.3 331.8,471.1"/><polygon points="318.4,482.3 317.6,479.0 333.6,475.5 334.3,478.9"/><polygon points="334.9,460.9 340.6,459.7 344.2,476.2 338.5,477.4"/><polygon points="360.2,479.3 351.5,483.4 346.2,459.1 349.0,458.5"/><polygon points="314.7,444.7 327.0,446.8 325.2,457.8 317.9,459.4"/><polygon points="332.1,447.4 345.0,449.5 347.0,453.3 330.5,456.9"/><polygon points="293.4,464.3 309.0,445.6 312.2,460.2"/><polygon points="406.1,219.9 412.5,218.9 417.4,234.2 411.3,236.2"/><polygon points="398.7,240.1 395.7,221.7 400.9,220.9 406.3,237.6"/><polygon points="394.8,216.5 393.8,210.5 408.3,205.9 410.9,213.9"/><polygon points="409.3,252.7 401.3,245.1 418.7,239.5 421.7,248.7"/><polygon points="426.5,260.7 416.6,260.8 412.4,256.9 424.1,253.2"/><polygon points="443.0,255.9 439.3,259.6 431.3,259.7 427.8,248.7 439.5,244.9"/><polygon points="426.1,243.1 422.1,230.6 433.7,226.9 437.7,239.4"/><polygon points="460.2,239.3 447.3,252.5 444.7,244.3"/><polygon points="443.3,240.1 438.3,224.6 442.6,223.2 447.6,238.8"/><polygon points="447.4,222.2 455.1,219.7 459.8,234.3 452.1,236.8"/><polygon points="423.1,201.3 433.7,197.9 437.3,209.0 426.7,212.4"/><polygon points="416.6,216.3 412.7,204.0 418.4,202.2 422.3,214.4"/><polygon points="420.1,225.4 418.5,220.6 439.0,214.1 440.5,218.8"/><polygon points="439.4,196.5 446.5,194.2 453.0,214.4 445.8,216.7"/><polygon points="496.3,190.7 496.6,191.1 499.5,198.9 492.0,206.6 486.3,200.9"/><polygon points="489.0,180.6 492.8,186.5 482.8,196.6 472.1,186.0"/><polygon points="475.4,222.6 473.6,224.5 457.7,208.9 469.8,205.1"/><polygon points="488.4,210.0 480.0,218.6 475.0,203.0 479.7,201.5"/><polygon points="470.6,228.7 464.9,234.6 459.6,218.0"/><polygon points="467.5,188.3 477.3,197.8 471.2,199.7"/><polygon points="455.7,203.6 452.0,192.1 463.0,188.6 466.6,200.0"/><polygon points="218.3,294.0 228.9,263.8 240.5,267.9 228.8,301.4"/><polygon points="269.4,296.9 237.0,302.8 242.3,287.4"/><polygon points="248.4,270.4 273.3,279.2 271.6,289.2 245.1,279.9"/><polygon points="276.6,233.2 280.9,234.5 274.6,270.9 264.6,267.4"/><polygon points="240.0,230.7 242.7,223.2 269.0,230.7 265.8,239.7"/><polygon points="231.6,255.9 237.7,238.4 262.8,247.1 256.7,264.6"/><polygon points="419.2,387.5 432.6,394.2 431.6,401.9 417.6,400.1"/><polygon points="400.9,398.1 408.9,382.2 413.9,384.7 412.0,399.5"/><polygon points="411.9,377.1 415.1,370.7 434.1,380.2 433.1,387.7"/><polygon points="419.5,361.9 425.1,350.8 437.6,352.4 436.2,364.0"/><polygon points="419.6,367.1 436.6,369.3 435.8,375.3"/><polygon points="414.7,415.3 423.5,406.4 431.2,407.3 430.0,417.3"/><polygon points="428.6,431.4 427.8,432.2 415.7,420.3 429.7,422.1"/><polygon points="409.3,413.7 398.4,403.0 417.4,405.4"/><polygon points="439.9,421.3 432.5,428.7 433.5,420.5"/><polygon points="452.2,404.8 445.3,413.5 441.7,417.2 435.6,416.4 437.3,402.9"/><polygon points="457.2,398.7 456.5,399.5 438.0,397.2 438.9,390.0 458.0,392.4"/><polygon points="456.8,371.7 461.3,372.3 459.3,387.6 454.9,387.0"/><polygon points="439.6,384.3 441.4,370.6 451.8,371.9 450.1,385.6"/><polygon points="474.6,374.4 466.3,387.8 463.5,391.3 465.8,373.3"/><polygon points="465.0,367.9 453.9,366.5 455.4,354.3 466.6,355.7"/><polygon points="448.8,366.3 441.5,365.3 443.1,352.3 450.4,353.2"/><polygon points="484.3,357.8 483.3,360.0 477.4,369.6 470.4,368.7 471.9,356.2"/><polygon points="356.2,437.4 360.2,430.1 382.1,442.2 378.1,449.5"/><polygon points="364.8,472.6 351.2,447.4 352.9,444.3 374.0,455.9"/><polygon points="400.9,452.8 395.0,457.6 375.3,470.0 388.5,446.0"/><polygon points="391.2,439.1 387.6,437.1 400.6,413.5 408.6,421.4"/><polygon points="422.5,435.4 421.2,436.7 407.4,447.8 397.9,442.5 413.6,426.6"/><polygon points="370.5,411.4 373.1,406.6 393.8,406.8 395.3,408.3 388.3,421.2"/><polygon points="381.6,434.2 363.1,424.0 366.9,417.1 385.4,427.2"/><polygon points="387.8,247.0 378.4,251.1 376.5,246.8 385.9,242.6"/><polygon points="375.2,243.4 372.7,237.6 381.7,233.6 384.2,239.4"/><polygon points="374.9,252.3 370.6,254.2 364.9,241.3 369.2,239.3"/><polygon points="363.0,238.1 360.4,232.3 366.9,229.4 369.5,235.2"/><polygon points="370.0,228.2 377.9,224.7 380.4,230.3 372.5,233.8"/><polygon points="390.6,219.4 392.1,228.7 384.3,229.9 381.4,223.5"/><polygon points="392.8,232.6 394.6,243.7 391.0,245.3 385.9,233.7"/><polygon points="386.7,206.6 388.8,209.6 389.7,215.2 381.4,218.9 377.8,210.6"/><polygon points="384.1,202.2 384.7,203.1 375.8,207.1 371.7,197.8"/><polygon points="353.0,213.9 349.3,205.6 358.4,201.6 362.1,209.9"/><polygon points="362.0,199.6 367.4,197.2 371.4,206.2 365.9,208.6"/><polygon points="378.0,220.9 369.0,224.9 367.0,220.2 376.0,216.2"/><polygon points="365.5,217.3 363.8,213.3 373.0,209.2 374.7,213.2"/><polygon points="365.4,226.1 359.4,228.7 354.6,217.9 360.6,215.3"/><polygon points="346.0,199.2 340.4,186.6 349.3,189.8"/><polygon points="349.3,201.1 352.8,191.2 363.2,194.9"/><polygon points="315.4,220.6 317.3,222.2 310.0,230.9 308.1,229.3"/><polygon points="301.3,223.0 307.9,215.0 312.6,218.9 305.9,226.8"/><polygon points="303.9,237.5 295.5,230.0 298.9,225.9 307.5,233.1"/><polygon points="314.7,230.6 319.5,224.9 327.5,231.6 317.2,236.2"/><polygon points="307.4,241.3 306.0,240.0 312.0,232.8 314.4,238.2"/><polygon points="310.5,212.2 317.4,203.8 324.2,209.4 313.1,214.3"/><polygon points="319.3,200.8 325.1,193.8 328.5,196.7 322.7,203.6"/><polygon points="330.9,198.9 337.0,204.0 328.2,207.9 325.3,205.6"/><polygon points="327.4,191.5 333.6,184.1 335.8,184.9 338.1,190.2 330.1,193.8"/><polygon points="339.6,193.3 343.2,201.4 340.5,202.6 332.9,196.2"/><polygon points="336.0,227.9 331.7,229.8 324.9,224.1 331.0,216.7"/><polygon points="321.8,221.8 316.2,217.1 329.0,211.4 329.5,212.6"/><polygon points="344.5,204.3 348.3,212.9 343.8,214.9 340.0,206.3"/><polygon points="340.8,216.1 335.8,218.3 332.1,209.9 337.0,207.7"/><polygon points="349.4,216.3 351.5,221.0 339.7,226.3 337.6,221.6"/><polygon points="326.3,257.3 321.4,252.9 328.7,244.7 333.0,254.3"/><polygon points="339.6,268.3 338.6,268.8 329.0,260.2 334.8,257.5"/><polygon points="318.3,250.5 310.4,243.4 317.9,240.1 321.1,247.4"/><polygon points="320.6,238.4 325.7,236.2 327.7,240.7 323.6,245.2"/><polygon points="349.0,263.9 342.8,266.6 338.7,257.3 344.8,254.6"/><polygon points="367.3,256.0 363.1,257.9 358.7,248.1 363.0,246.2"/><polygon points="359.5,259.1 352.6,262.2 348.5,253.0 355.5,250.0"/><polygon points="340.5,230.1 344.9,228.2 349.7,239.2 345.4,241.1"/><polygon points="348.3,226.7 353.2,224.6 358.0,235.5 353.2,237.6"/><polygon points="359.5,239.0 361.3,243.0 348.7,248.6 347.0,244.5"/><polygon points="337.0,254.0 334.7,248.9 343.2,245.1 345.5,250.2"/><polygon points="333.2,246.0 331.7,242.7 340.7,238.7 342.2,242.1"/><polygon points="330.6,239.8 328.6,235.2 337.2,231.4 339.2,236.0"/><polygon points="213.4,467.0 207.6,462.9 214.9,452.7 227.0,461.3 227.2,461.9"/><polygon points="216.7,448.3 221.0,442.2 225.8,454.8"/><polygon points="230.6,479.9 218.1,470.9 229.5,466.6 233.3,476.9"/><polygon points="201.7,440.5 217.3,434.6 218.7,438.3 211.9,447.8"/><polygon points="203.0,459.6 193.3,452.7 192.7,449.2 194.5,443.5 196.0,442.9 208.6,451.9"/><polygon points="247.9,448.9 251.4,456.9 245.5,463.4 235.0,453.7"/><polygon points="242.5,467.2 236.9,473.4 230.4,456.1"/><polygon points="221.7,432.7 238.5,426.4 240.0,429.9 223.4,437.2"/><polygon points="241.5,435.0 245.3,443.8 229.0,450.0 225.9,441.8"/><polygon points="181.5,490.0 178.3,488.1 183.3,476.5 189.3,479.1"/><polygon points="185.7,472.6 191.9,458.3 199.6,463.8 191.5,475.1"/><polygon points="204.6,498.9 189.5,493.8 186.1,491.8 191.4,484.5 206.9,495.6"/><polygon points="194.2,480.0 203.8,466.7 208.9,470.3 199.3,483.7"/><polygon points="213.0,473.5 219.5,478.2 210.2,491.2 203.7,486.5"/><polygon points="227.3,504.5 216.0,502.9 210.0,500.9 216.5,491.7 227.5,499.5"/><polygon points="218.8,487.4 223.8,480.5 228.8,484.1 228.3,494.2"/><polygon points="346.8,382.9 354.6,391.0 337.9,407.2 334.4,405.3"/><polygon points="359.3,396.2 366.5,403.6 358.5,418.2 344.6,410.6"/><polygon points="314.7,393.5 325.2,379.7 340.1,379.4 328.2,401.0"/><polygon points="304.5,406.0 309.5,399.4 331.2,411.4 327.3,418.5"/><polygon points="316.1,438.4 311.6,437.7 303.7,413.8 323.6,424.8"/><polygon points="345.0,444.0 322.8,440.2 328.1,430.8 346.6,441.0"/><polygon points="331.8,425.2 337.4,414.9 355.0,424.6 349.3,434.8"/><polygon points="363.9,262.8 373.6,258.5 380.1,273.3 370.4,277.6"/><polygon points="353.7,266.6 358.5,264.4 365.7,280.5 360.8,282.7"/><polygon points="379.1,298.2 370.1,302.2 363.5,287.4 372.5,283.4"/><polygon points="382.9,278.2 389.9,293.9 384.3,296.4 377.4,280.7"/><polygon points="342.9,271.4 349.2,268.6 355.4,282.7 349.2,285.5"/><polygon points="339.6,275.3 345.2,287.7 337.0,291.4"/><polygon points="352.4,310.4 350.7,311.2 336.4,298.1 336.7,296.6 344.7,293.1"/><polygon points="357.2,287.9 364.7,304.7 357.4,307.9 349.9,291.1"/><polygon points="365.0,325.3 353.5,314.8 359.2,312.3"/><polygon points="365.0,315.4 363.1,311.0 377.4,304.6 379.4,309.1"/><polygon points="385.5,324.2 370.7,326.8 367.6,319.7 380.9,313.9"/><polygon points="399.5,314.4 401.2,318.4 400.3,322.4 390.7,324.1 388.6,319.3"/><polygon points="391.8,298.8 396.9,310.3 387.2,314.6 382.1,303.1"/><polygon points="409.7,275.6 406.1,291.9 394.0,289.2 391.5,283.7"/><polygon points="405.7,297.4 402.7,311.0 395.6,295.1"/><polygon points="397.8,275.0 388.6,279.0 384.6,270.1 393.8,266.0"/><polygon points="408.7,259.6 413.0,263.7 412.0,268.5 402.1,272.8 398.3,264.2"/><polygon points="395.0,247.6 396.4,247.0 406.1,256.1 400.0,258.8"/><polygon points="378.8,255.8 391.3,250.3 395.4,259.7 383.0,265.2"/><polygon points="167.1,399.0 175.8,415.8 170.3,418.7 161.6,401.9"/><polygon points="165.7,421.2 154.9,426.8 150.3,417.9 161.1,412.3"/><polygon points="147.6,414.1 145.3,409.6 157.1,403.4 159.4,407.9"/><polygon points="140.4,399.3 163.3,395.1 143.6,405.4"/><polygon points="127.6,401.7 135.4,400.3 141.3,411.7 134.7,415.2"/><polygon points="122.9,421.2 120.4,415.4 118.0,403.6 122.4,402.8 130.1,417.4"/><polygon points="133.9,421.6 143.4,416.7 149.9,429.1 140.4,434.1"/><polygon points="130.5,439.9 124.5,425.9 129.1,423.5 136.1,437.0"/><polygon points="169.0,447.9 160.1,448.5 158.9,430.4 159.6,430.0"/><polygon points="150.9,448.9 137.7,449.8 133.5,443.9 145.1,437.8"/><polygon points="155.8,448.5 148.8,435.1 154.7,432.0"/><polygon points="169.0,436.5 164.6,428.0 178.2,420.8 182.7,429.3"/><polygon points="189.2,441.6 187.7,446.3 174.5,447.2 171.6,441.5 185.4,434.3"/><polygon points="256.2,450.8 244.2,423.4 250.8,404.8 263.8,407.0"/><polygon points="295.5,450.3 285.9,461.8 263.9,455.7 265.7,445.2"/><polygon points="294.8,412.3 304.1,440.1 301.7,442.9 287.0,440.4 291.9,411.8"/><polygon points="278.7,438.9 267.1,437.0 272.0,408.4 283.6,410.4"/><polygon points="463.5,151.8 481.3,169.6 484.7,174.9 472.2,178.9"/><polygon points="464.8,180.9 442.0,188.2 438.5,177.2 461.3,169.9"/><polygon points="435.9,170.1 433.1,161.4 456.5,153.9 459.3,162.5"/><polygon points="423.8,130.7 440.0,137.0 454.5,146.3 431.2,153.8"/><polygon points="408.3,199.1 400.1,173.5 401.0,171.3 416.9,177.7"/><polygon points="433.0,183.6 435.4,191.1 416.5,197.2 423.5,179.8"/><polygon points="402.1,201.9 392.1,205.1 388.8,200.3 395.9,182.5"/><polygon points="416.3,134.6 424.0,158.8 405.6,164.7 404.4,164.2"/><polygon points="427.6,164.7 431.5,176.8 413.0,169.4"/><polygon points="275.2,466.3 284.8,469.0 288.2,484.5 279.5,486.4"/><polygon points="271.6,477.8 273.8,487.8 260.0,490.9 257.8,480.8"/><polygon points="253.9,462.5 255.5,460.8 268.6,464.4 270.3,472.1 256.6,475.1"/><polygon points="235.2,482.3 249.2,467.1 251.7,478.7"/><polygon points="233.4,496.4 233.8,488.4 252.5,484.4 254.1,491.9"/><polygon points="255.3,506.6 244.9,506.9 232.8,505.2 232.9,502.6 253.5,498.1"/><polygon points="275.7,506.0 275.3,506.0 261.2,506.4 259.2,497.0 273.1,493.9"/><polygon points="292.5,503.1 281.4,505.1 278.7,492.6 289.7,490.2"/><polygon points="455.6,313.2 449.3,320.4 440.9,313.0 447.1,305.8"/><polygon points="437.2,310.0 429.8,303.5 436.3,296.1 443.7,302.6"/><polygon points="453.3,277.5 458.6,282.1 445.4,297.2 440.2,292.6"/><polygon points="462.9,285.3 472.6,293.7 467.1,299.9 457.5,291.5"/><polygon points="464.0,303.6 458.8,309.5 449.1,301.0 454.3,295.1"/><polygon points="447.2,271.0 450.2,273.6 439.9,285.4 436.9,282.8"/><polygon points="435.5,265.1 439.6,265.1 443.5,268.4 433.9,279.4 427.7,274.1"/><polygon points="417.0,265.7 417.3,264.6 430.4,264.5 424.0,271.8"/><polygon points="412.7,285.3 416.2,269.9 421.9,274.9"/><polygon points="424.8,277.5 427.6,280.0 416.7,292.4 413.9,290.0"/><polygon points="430.7,283.4 436.4,288.5 426.3,300.1 420.6,295.0"/><polygon points="410.7,329.1 405.4,323.3 408.1,311.3 423.3,314.7"/><polygon points="409.1,305.4 411.6,294.3 428.6,309.2 428.1,309.7"/><polygon points="427.1,344.8 424.8,344.5 414.9,333.7 421.4,326.4 433.7,337.2"/><polygon points="424.4,321.8 432.7,312.2 438.5,317.3 430.2,326.8"/><polygon points="442.0,320.1 446.5,324.0 437.9,333.9 433.4,329.9"/><polygon points="468.2,341.5 469.8,328.5 475.3,333.3"/><polygon points="488.8,346.9 486.4,352.3 468.5,350.0 468.8,348.0 478.0,337.5"/><polygon points="462.8,349.7 448.2,347.9 449.8,335.2 463.1,346.9"/><polygon points="443.2,347.6 433.0,346.3 445.1,332.4"/><polygon points="450.0,327.9 457.4,319.4 465.3,326.4 463.6,339.7"/><polygon points="500.2,318.6 496.3,331.1 491.6,341.3 484.3,334.9 499.3,317.7"/><polygon points="479.8,331.1 475.6,327.4 490.7,310.2 494.9,313.8"/><polygon points="471.2,324.0 460.8,314.9 466.4,308.4 476.9,317.6"/><polygon points="470.0,304.5 476.3,297.3 486.6,306.3 480.3,313.5"/><polygon points="409.6,365.4 401.3,381.8 384.9,373.5 393.2,357.2"/><polygon points="418.5,348.1 413.4,358.2 396.6,349.8 404.9,333.3"/><polygon points="366.7,340.8 370.3,333.9 397.8,329.2 386.9,351.0"/><polygon points="377.5,369.9 356.8,359.5 362.8,348.1 383.2,358.4"/><polygon points="368.5,394.3 349.1,374.4 353.1,366.9 376.4,378.6"/><polygon points="398.1,389.1 392.9,399.5 374.9,399.3 383.7,381.8"/><polygon points="315.5,158.3 292.1,165.9 305.5,149.9"/><polygon points="315.6,136.8 322.0,156.4 308.6,145.2"/><polygon points="324.4,189.0 319.7,194.6 310.9,187.3 315.6,181.7"/><polygon points="326.3,169.1 330.4,181.5 327.1,185.4 318.6,178.3"/><polygon points="312.1,177.7 307.5,183.3 293.6,171.8 308.6,166.9"/><polygon points="323.9,161.0 324.9,163.9 316.3,174.1 313.2,164.5"/><polygon points="290.0,136.0 292.2,135.1 312.4,129.0 313.0,131.1 301.2,145.3"/><polygon points="290.4,144.5 297.2,150.2 285.2,164.6 278.4,159.0"/><polygon points="264.4,147.5 283.7,138.7 285.9,140.6 273.7,155.2"/><polygon points="261.9,153.1 275.7,164.6 270.4,170.8 256.6,159.4"/><polygon points="267.7,175.2 258.8,186.0 254.3,182.3 263.3,171.5"/><polygon points="250.9,179.0 244.3,173.5 252.8,163.3 259.4,168.8"/><polygon points="234.4,165.0 255.2,152.3 240.4,170.0"/><polygon points="250.7,185.2 255.8,189.4 248.0,198.8 241.1,193.0"/><polygon points="244.8,202.3 237.4,211.2 230.1,202.2 237.4,196.2"/><polygon points="222.5,172.8 229.8,167.0 233.6,170.2 226.9,178.2"/><polygon points="236.4,173.7 246.5,182.1 237.4,189.6 230.4,181.0"/><polygon points="213.7,181.3 219.5,176.7 232.7,192.9 227.1,197.6"/><polygon points="254.5,220.7 241.3,216.9 239.7,215.0 243.0,211.1"/><polygon points="246.9,208.2 254.7,198.9 264.6,207.2 256.8,216.5"/><polygon points="268.8,224.1 259.7,221.5 269.0,210.3 275.7,215.8"/><polygon points="290.9,228.2 285.8,229.2 273.7,225.7 279.5,218.7"/><polygon points="303.5,213.5 294.2,224.6 288.0,219.5 297.3,208.3"/><polygon points="284.1,216.9 280.9,214.3 290.9,202.3 294.1,204.9"/><polygon points="303.8,187.5 309.5,192.2 299.8,203.8 294.2,199.1"/><polygon points="313.3,194.9 316.9,197.9 306.8,210.1 303.2,207.1"/><polygon points="280.1,168.1 289.9,176.2 282.8,184.8 273.0,176.7"/><polygon points="294.1,179.1 300.2,184.3 292.5,193.5 286.4,188.4"/><polygon points="289.1,197.1 277.7,210.8 272.6,206.6 284.0,192.9"/><polygon points="268.5,203.1 258.1,194.4 264.3,187.0 274.7,195.7"/><polygon points="266.7,182.9 269.3,179.8 280.9,189.4 278.2,192.5"/><polygon points="252.9,366.4 273.2,358.5 275.9,365.4 255.5,373.3"/><polygon points="277.8,372.0 282.2,383.2 263.0,390.8 258.6,379.5"/><polygon points="250.1,396.2 241.4,375.9 244.0,370.2 246.9,369.0 256.5,393.7"/><polygon points="290.4,403.7 262.4,398.9 285.0,390.0"/><polygon points="313.0,383.8 297.7,403.8 292.9,391.6"/><polygon points="296.2,350.4 303.4,358.8 286.6,373.2 280.1,356.7"/><polygon points="308.6,364.5 317.3,374.6 290.9,384.9 289.4,381.0"/><polygon points="178.2,353.3 175.5,339.5 183.7,337.9 186.4,351.8"/><polygon points="188.7,336.9 196.3,335.4 199.0,349.5 191.4,350.9"/><polygon points="203.4,373.6 195.5,373.7 195.3,355.4 199.8,354.6"/><polygon points="189.9,373.5 182.3,373.6 179.5,358.6 189.8,356.7"/><polygon points="207.8,366.2 205.6,354.7 219.7,352.0 221.9,363.6"/><polygon points="224.8,374.0 208.4,374.1 207.8,371.5 223.8,368.4"/><polygon points="239.0,365.4 239.4,367.3 236.4,373.9 229.0,374.0 227.8,367.5"/><polygon points="235.4,348.6 237.6,360.9 227.5,362.8 225.2,350.6"/><polygon points="215.0,346.8 204.4,348.9 201.7,334.9 212.3,332.8"/><polygon points="232.4,327.9 233.1,331.5 217.6,334.3 216.9,330.9"/><polygon points="233.3,336.0 234.6,343.5 220.5,346.2 219.0,338.6"/><polygon points="217.2,326.4 204.3,328.8 202.8,320.6 215.6,318.1"/><polygon points="229.9,315.1 231.4,323.9 221.9,325.8 220.2,317.0"/><polygon points="212.1,298.4 212.6,298.3 228.4,309.5 228.6,310.4 215.0,313.1"/><polygon points="199.0,300.1 206.9,299.1 209.7,314.0 202.0,315.5"/><polygon points="181.0,333.7 174.2,335.0 172.0,323.7 178.8,322.4"/><polygon points="197.2,319.5 199.1,329.5 185.7,332.1 183.8,322.1"/><polygon points="169.2,304.2 180.6,302.7 182.4,316.5 171.9,318.5"/><polygon points="186.0,301.6 193.8,300.6 196.4,314.2 187.9,315.9"/><polygon points="395.7,122.2 412.6,125.3 409.1,133.9 393.5,127.7"/><polygon points="407.3,138.9 405.1,144.5 389.1,138.1 391.3,132.5"/><polygon points="402.9,149.3 400.0,156.6 384.4,150.3 387.3,143.1"/><polygon points="374.2,120.7 385.9,120.8 389.7,121.5 384.7,134.1 371.0,128.6"/><polygon points="382.7,139.6 379.3,148.2 365.2,142.5 368.6,133.9"/><polygon points="351.2,120.3 355.3,119.7 369.0,119.8 366.1,126.9 351.0,120.9"/><polygon points="363.6,131.3 360.0,140.4 346.1,134.8 349.8,125.8"/><polygon points="322.8,125.5 323.8,125.2 345.2,121.9 340.9,132.7"/><polygon points="325.1,148.7 323.5,143.6 339.7,138.3 341.0,138.9 335.4,152.9"/><polygon points="330.4,167.9 325.9,154.1 334.5,157.6"/><polygon points="321.3,139.1 318.0,128.9 333.4,135.1"/><polygon points="358.6,146.0 368.3,149.9 363.1,162.8 353.4,158.9"/><polygon points="340.7,154.1 346.1,140.7 353.6,143.7 348.2,157.1"/><polygon points="336.1,180.4 335.1,180.0 332.9,173.5 338.8,158.9 343.9,161.0"/><polygon points="354.1,187.3 340.4,182.4 342.4,177.3 355.9,182.7"/><polygon points="344.9,173.0 348.8,163.2 361.1,168.1 357.2,177.9"/><polygon points="388.4,186.3 383.9,197.6 375.8,194.7 380.0,182.9"/><polygon points="370.9,192.5 359.1,188.3 363.7,176.8 374.9,181.3"/><polygon points="374.5,175.4 365.4,171.7 369.5,161.2 378.7,164.9"/><polygon points="370.8,156.9 373.2,150.9 383.2,154.9 380.8,160.9"/><polygon points="393.6,173.3 390.2,181.7 379.1,177.2 382.5,168.8"/><polygon points="398.5,161.4 395.5,168.9 384.2,164.3 387.1,156.9"/><polygon points="265.6,354.8 243.8,363.4 242.3,354.9 264.9,350.9"/><polygon points="241.0,349.3 239.0,338.2 250.6,336.1 252.6,347.2"/><polygon points="255.4,334.8 262.6,333.5 264.7,345.5 257.5,346.8"/><polygon points="283.7,348.9 270.5,354.1 269.5,348.6 283.2,346.1"/><polygon points="269.5,343.9 267.6,333.1 279.7,331.0 281.6,341.7"/><polygon points="295.8,328.2 294.7,343.7 288.1,346.3 285.2,330.1"/><polygon points="285.1,310.5 297.0,318.5 296.7,322.8 287.6,324.4"/><polygon points="280.7,316.3 282.2,324.7 264.3,327.9 262.8,319.4"/><polygon points="260.1,305.5 273.2,303.1 279.2,307.1 279.8,310.5 261.6,313.7"/><polygon points="233.5,309.8 241.3,308.4 243.5,320.5 235.7,321.8"/><polygon points="246.0,307.7 254.8,306.0 257.0,317.9 248.2,319.5"/><polygon points="257.5,323.2 258.6,329.1 238.1,332.8 237.1,326.8"/><polygon points="150.0,336.7 142.2,338.2 139.0,321.5 146.8,320.0"/><polygon points="165.8,316.3 167.4,325.1 153.8,327.7 152.1,318.9"/><polygon points="169.0,329.6 169.7,333.7 154.9,336.5 154.1,332.4"/><polygon points="132.9,288.6 145.4,294.9 137.2,311.3"/><polygon points="154.6,298.7 163.6,303.2 165.1,311.3 157.3,312.8"/><polygon points="149.7,298.5 152.5,313.6 141.1,315.8"/><polygon points="121.9,316.7 127.4,298.2 128.5,295.7 132.1,314.7"/><polygon points="128.7,320.7 133.3,319.8 137.1,339.5 132.5,340.3"/><polygon points="116.5,343.0 118.6,328.6 120.3,322.7 123.5,322.1 127.1,340.9"/><polygon points="114.0,357.9 115.3,348.7 125.7,346.7 127.4,355.3"/><polygon points="128.5,360.0 129.5,365.5 113.9,368.5 113.8,362.8"/><polygon points="129.8,370.8 131.6,380.2 115.0,383.4 114.7,373.7"/><polygon points="132.7,386.1 134.4,394.9 117.2,398.0 115.4,389.4"/><polygon points="136.6,373.4 135.0,365.3 154.8,361.5 156.4,369.6"/><polygon points="160.7,390.3 151.5,391.9 148.8,377.2 157.9,375.4"/><polygon points="146.4,393.2 139.9,394.4 137.0,379.1 143.5,377.8"/><polygon points="176.5,372.4 177.1,375.7 165.9,388.6 163.3,374.9"/><polygon points="173.6,357.4 175.4,367.1 162.4,369.6 160.5,359.9"/><polygon points="130.9,346.0 140.7,344.1 143.3,357.7 133.5,359.5"/><polygon points="146.4,343.3 158.4,341.0 160.9,354.0 148.9,356.3"/><polygon points="163.6,339.3 170.2,338.1 172.9,352.4 166.4,353.6"/><polygon points="507.1,296.9 506.3,301.9 502.5,313.8 496.7,308.7"/><polygon points="505.4,289.3 493.3,303.2 481.1,292.6 493.3,278.7"/><polygon points="510.3,268.1 510.3,272.3 508.6,282.9 496.9,272.7 505.3,263.1"/><polygon points="475.0,287.4 462.8,276.7 474.6,263.1 486.2,274.5"/><polygon points="456.8,271.7 446.1,262.3 459.8,248.4 469.1,257.6"/><polygon points="489.1,248.1 500.4,259.1 491.5,269.3 479.7,257.7"/><polygon points="464.4,242.9 474.0,233.1 484.4,243.3 474.8,253.1"/><polygon points="509.7,238.9 510.5,243.7 510.4,260.2 499.4,249.4"/><polygon points="504.0,235.0 495.4,243.9 479.5,228.4 488.2,219.5"/><polygon points="501.1,205.1 505.5,216.5 507.6,228.7 492.5,213.9"/><polygon points="278.5,286.3 280.6,274.0 287.4,275.2 285.3,287.4"/><polygon points="291.5,275.6 295.8,276.3 293.6,289.2 289.3,288.5"/><polygon points="299.6,277.2 305.2,278.1 303.0,290.7 297.4,289.7"/><polygon points="293.8,293.2 302.4,294.7 301.2,301.5 289.6,299.5"/><polygon points="300.3,305.6 298.8,314.2 285.8,305.5 287.2,303.3"/><polygon points="282.1,290.2 291.0,291.8 288.9,294.9"/><polygon points="276.0,299.2 277.5,290.9 286.5,296.9 282.2,303.3"/><polygon points="324.9,301.9 316.2,306.6 312.5,299.7"/><polygon points="312.7,308.0 303.3,313.0 305.7,299.0 308.1,299.4"/><polygon points="309.1,278.7 322.0,281.0 321.2,285.7 308.3,283.5"/><polygon points="321.1,289.1 319.6,297.8 315.6,297.1 317.1,288.4"/><polygon points="312.5,296.3 306.1,295.2 307.5,287.1 313.9,288.2"/><polygon points="332.8,293.3 332.0,298.0 329.6,299.3 322.8,298.1 323.9,291.8"/><polygon points="325.8,281.5 334.4,283.0 333.3,289.8 324.6,288.3"/><polygon points="281.0,269.9 282.0,264.4 293.2,266.4 292.3,271.9"/><polygon points="282.3,260.9 282.8,257.6 294.7,259.7 294.2,262.9"/><polygon points="283.9,254.1 285.0,247.6 296.0,249.5 294.9,256.0"/><polygon points="300.2,250.3 315.1,252.9 316.3,254.0 310.3,260.6 300.0,251.3"/><polygon points="304.1,268.7 299.6,273.7 295.5,273.0 296.4,267.4"/><polygon points="307.9,263.8 306.2,265.8 297.4,264.3 298.9,255.7"/><polygon points="306.6,243.8 312.7,249.3 305.8,248.1"/><polygon points="299.6,238.1 304.3,242.3 303.5,247.2 298.1,246.3"/><polygon points="285.2,243.9 285.9,239.9 295.9,241.6 295.2,245.7"/><polygon points="286.4,236.7 287.1,232.9 292.4,231.9 296.9,235.9 296.4,238.4"/><polygon points="328.1,269.8 321.8,276.8 315.2,275.7 316.6,267.8"/><polygon points="319.5,256.5 330.3,266.1 317.1,263.8 318.1,258.0"/><polygon points="333.8,269.2 336.3,271.4 335.0,279.4 326.1,277.9"/><polygon points="311.3,275.3 303.7,274.0 313.4,263.2"/><polygon points="194.6,378.6 203.6,378.5 203.7,388.7 194.7,388.8"/><polygon points="173.0,388.6 181.2,379.1 189.7,379.0 189.8,388.4"/><polygon points="181.7,415.0 170.8,394.1 182.2,394.0 182.4,415.0"/><polygon points="187.4,393.4 203.7,393.3 203.7,398.8 187.4,398.9"/><polygon points="203.2,404.1 203.3,414.7 188.2,414.9 188.1,404.3"/><polygon points="203.6,434.1 193.5,437.8 189.1,429.3 203.5,421.8"/><polygon points="186.1,425.6 183.0,419.7 197.7,419.6"/><polygon points="218.9,428.7 208.9,432.4 208.8,419.6 218.8,419.5"/><polygon points="208.2,415.2 208.2,408.9 219.2,408.8 219.3,415.1"/><polygon points="231.5,423.8 224.0,426.6 223.9,409.4 231.4,409.3"/><polygon points="242.9,408.5 238.2,422.0 236.1,422.8 236.0,408.6"/><polygon points="243.2,395.8 245.1,400.1 243.8,403.8 227.3,403.9 227.2,395.9"/><polygon points="235.9,378.7 240.9,390.3 227.1,390.4 227.0,378.8"/><polygon points="221.7,403.9 209.0,404.0 208.9,392.5 221.6,392.4"/><polygon points="208.5,387.1 208.4,378.7 221.8,378.6 221.9,387.0"/><polygon points="155.9,470.7 146.5,462.1 141.5,455.0 154.8,454.1"/><polygon points="187.3,452.3 187.6,453.9 183.5,463.3 161.9,453.9"/><polygon points="162.0,477.1 161.3,476.4 160.1,459.0 168.3,462.5"/><polygon points="181.2,468.9 174.4,484.7 166.3,480.0 172.8,465.2"/></g><g fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M190.1,249.5 L226.8,257.0 L251.3,265.7" stroke="#1a0a2a" stroke-width="3.4"/><path d="M251.3,265.7 L279.8,265.8 L303.8,277.2" stroke="#1a0a2a" stroke-width="3.4"/><path d="M251.3,265.7 L252.3,304.0 L264.1,330.6" stroke="#1a0a2a" stroke-width="3.4"/><path d="M264.1,330.6 L269.4,356.6 L280.6,378.9" stroke="#1a0a2a" stroke-width="3.4"/><path d="M280.6,378.9 L273.0,404.3 L273.8,432.0" stroke="#1a0a2a" stroke-width="3.4"/><path d="M273.8,432.0 L247.1,440.5 L224.3,452.0" stroke="#1a0a2a" stroke-width="3.4"/><path d="M224.3,452.0 L210.9,468.7 L204.5,484.2" stroke="#1a0a2a" stroke-width="3.4"/><path d="M204.5,484.2 L183.3,471.6 L167.0,464.3" stroke="#1a0a2a" stroke-width="3.4"/><path d="M167.0,464.3 L163.1,450.8 L152.8,424.0" stroke="#1a0a2a" stroke-width="3.4"/><path d="M224.3,452.0 L215.9,432.4 L208.3,403.6" stroke="#1a0a2a" stroke-width="3.4"/><path d="M224.3,452.0 L242.7,470.3 L262.3,487.2" stroke="#1a0a2a" stroke-width="3.4"/><path d="M264.1,330.6 L236.1,337.7 L204.1,337.5" stroke="#1a0a2a" stroke-width="3.4"/><path d="M262.3,487.2 L291.7,485.9 L322.2,472.3" stroke="#1a0a2a" stroke-width="3.4"/><path d="M280.6,378.9 L310.6,392.1 L333.3,411.2" stroke="#1a0a2a" stroke-width="3.4"/><path d="M333.3,411.2 L358.8,425.2 L383.2,438.5" stroke="#1a0a2a" stroke-width="3.4"/><path d="M204.1,337.5 L172.8,339.1 L143.0,348.1" stroke="#1a0a2a" stroke-width="3.4"/><path d="M280.6,378.9 L310.3,360.7 L330.2,338.1" stroke="#1a0a2a" stroke-width="3.4"/><path d="M330.2,338.1 L355.8,352.7 L383.9,363.4" stroke="#1a0a2a" stroke-width="3.4"/><path d="M383.9,363.4 L409.3,375.3 L437.9,385.3" stroke="#1a0a2a" stroke-width="3.4"/><path d="M303.8,277.2 L315.6,250.5 L347.8,226.4" stroke="#1a0a2a" stroke-width="3.4"/><path d="M330.2,338.1 L350.6,314.6 L376.8,288.5" stroke="#1a0a2a" stroke-width="3.4"/><path d="M437.9,385.3 L455.8,351.5 L449.0,312.9" stroke="#1a0a2a" stroke-width="3.4"/><path d="M449.0,312.9 L472.1,290.1 L486.8,259.4" stroke="#1a0a2a" stroke-width="3.4"/><path d="M486.8,259.4 L471.4,231.0 L442.9,220.1" stroke="#1a0a2a" stroke-width="3.4"/><path d="M442.9,220.1 L440.6,192.8 L432.5,168.0" stroke="#1a0a2a" stroke-width="3.4"/><path d="M432.5,168.0 L400.8,162.0 L365.0,152.0" stroke="#1a0a2a" stroke-width="3.4"/><path d="M347.8,226.4 L312.9,206.1 L277.3,180.6" stroke="#1a0a2a" stroke-width="3.4"/><path d="M322.2,472.3 L327.7,444.2 L333.3,411.2" stroke="#1a0a2a" stroke-width="3.4"/><path d="M437.9,385.3 L411.5,419.3 L383.2,438.5" stroke="#1a0a2a" stroke-width="3.4"/><path d="M376.8,288.5 L384.8,327.2 L383.9,363.4" stroke="#1a0a2a" stroke-width="3.4"/><path d="M152.8,424.0 L179.3,416.7 L208.3,403.6" stroke="#1a0a2a" stroke-width="3.4"/><path d="M449.0,312.9 L412.7,335.8 L383.9,363.4" stroke="#1a0a2a" stroke-width="3.4"/><path d="M280.6,378.9 L239.6,371.7 L204.1,337.5" stroke="#1a0a2a" stroke-width="3.4"/><path d="M330.2,338.1 L124.8,297.3" stroke="#1a0a2a" stroke-width="4.8"/><path d="M330.2,338.1 L465.1,148.1" stroke="#1a0a2a" stroke-width="4.8"/><path d="M330.2,338.1 L368.5,478.8" stroke="#1a0a2a" stroke-width="4.8"/><path d="M190.1,249.5 L226.8,257.0 L251.3,265.7" stroke="#ff2bd6" stroke-width="1.8"/><path d="M251.3,265.7 L279.8,265.8 L303.8,277.2" stroke="#ff2bd6" stroke-width="1.8"/><path d="M251.3,265.7 L252.3,304.0 L264.1,330.6" stroke="#ff2bd6" stroke-width="1.8"/><path d="M264.1,330.6 L269.4,356.6 L280.6,378.9" stroke="#ff2bd6" stroke-width="1.8"/><path d="M280.6,378.9 L273.0,404.3 L273.8,432.0" stroke="#ff2bd6" stroke-width="1.8"/><path d="M273.8,432.0 L247.1,440.5 L224.3,452.0" stroke="#ff2bd6" stroke-width="1.8"/><path d="M224.3,452.0 L210.9,468.7 L204.5,484.2" stroke="#ff2bd6" stroke-width="1.8"/><path d="M204.5,484.2 L183.3,471.6 L167.0,464.3" stroke="#ff2bd6" stroke-width="1.8"/><path d="M167.0,464.3 L163.1,450.8 L152.8,424.0" stroke="#ff2bd6" stroke-width="1.8"/><path d="M224.3,452.0 L215.9,432.4 L208.3,403.6" stroke="#ff2bd6" stroke-width="1.8"/><path d="M224.3,452.0 L242.7,470.3 L262.3,487.2" stroke="#ff2bd6" stroke-width="1.8"/><path d="M264.1,330.6 L236.1,337.7 L204.1,337.5" stroke="#ff2bd6" stroke-width="1.8"/><path d="M262.3,487.2 L291.7,485.9 L322.2,472.3" stroke="#ff2bd6" stroke-width="1.8"/><path d="M280.6,378.9 L310.6,392.1 L333.3,411.2" stroke="#ff2bd6" stroke-width="1.8"/><path d="M333.3,411.2 L358.8,425.2 L383.2,438.5" stroke="#ff2bd6" stroke-width="1.8"/><path d="M204.1,337.5 L172.8,339.1 L143.0,348.1" stroke="#ff2bd6" stroke-width="1.8"/><path d="M280.6,378.9 L310.3,360.7 L330.2,338.1" stroke="#ff2bd6" stroke-width="1.8"/><path d="M330.2,338.1 L355.8,352.7 L383.9,363.4" stroke="#ff2bd6" stroke-width="1.8"/><path d="M383.9,363.4 L409.3,375.3 L437.9,385.3" stroke="#ff2bd6" stroke-width="1.8"/><path d="M303.8,277.2 L315.6,250.5 L347.8,226.4" stroke="#ff2bd6" stroke-width="1.8"/><path d="M330.2,338.1 L350.6,314.6 L376.8,288.5" stroke="#ff2bd6" stroke-width="1.8"/><path d="M437.9,385.3 L455.8,351.5 L449.0,312.9" stroke="#ff2bd6" stroke-width="1.8"/><path d="M449.0,312.9 L472.1,290.1 L486.8,259.4" stroke="#ff2bd6" stroke-width="1.8"/><path d="M486.8,259.4 L471.4,231.0 L442.9,220.1" stroke="#ff2bd6" stroke-width="1.8"/><path d="M442.9,220.1 L440.6,192.8 L432.5,168.0" stroke="#ff2bd6" stroke-width="1.8"/><path d="M432.5,168.0 L400.8,162.0 L365.0,152.0" stroke="#ff2bd6" stroke-width="1.8"/><path d="M347.8,226.4 L312.9,206.1 L277.3,180.6" stroke="#ff2bd6" stroke-width="1.8"/><path d="M322.2,472.3 L327.7,444.2 L333.3,411.2" stroke="#ff2bd6" stroke-width="1.8"/><path d="M437.9,385.3 L411.5,419.3 L383.2,438.5" stroke="#ff2bd6" stroke-width="1.8"/><path d="M376.8,288.5 L384.8,327.2 L383.9,363.4" stroke="#ff2bd6" stroke-width="1.8"/><path d="M152.8,424.0 L179.3,416.7 L208.3,403.6" stroke="#ff2bd6" stroke-width="1.8"/><path d="M449.0,312.9 L412.7,335.8 L383.9,363.4" stroke="#ff2bd6" stroke-width="1.8"/><path d="M280.6,378.9 L239.6,371.7 L204.1,337.5" stroke="#ff2bd6" stroke-width="1.8"/><path d="M330.2,338.1 L124.8,297.3" stroke="#ff2bd6" stroke-width="3.2"/><path d="M330.2,338.1 L465.1,148.1" stroke="#ff2bd6" stroke-width="3.2"/><path d="M330.2,338.1 L368.5,478.8" stroke="#ff2bd6" stroke-width="3.2"/></g><path d="M111.4,358.6 L115.7,328.0 M115.7,328.0 L122.2,306.0 M128.6,289.0 L138.4,267.1 M138.4,267.1 L156.2,238.1 M156.2,238.1 L177.7,210.9 M177.7,210.9 L202.5,186.2 M202.5,186.2 L230.1,164.5 M230.1,164.5 L259.9,146.3 M259.9,146.3 L291.1,132.1 M291.1,132.1 L323.1,122.4 M323.1,122.4 L355.1,117.5 M355.1,117.5 L386.1,117.7 M386.1,117.7 L415.4,123.0 M415.4,123.0 L441.9,133.2 M441.9,133.2 L457.4,143.2 M471.5,154.6 L484.2,167.3 M484.2,167.3 L498.7,190.0 M498.7,190.0 L508.5,215.6 M508.5,215.6 L513.3,243.4 M513.3,243.4 L513.2,272.6 M513.2,272.6 L508.4,302.4 M508.4,302.4 L499.1,332.2 M499.1,332.2 L485.6,361.3 M485.6,361.3 L468.3,389.2 M468.3,389.2 L447.5,415.5 M447.5,415.5 L423.7,439.5 M423.7,439.5 L397.2,460.7 M397.2,460.7 L376.2,474.0 M360.3,482.7 L338.3,493.2 M338.3,493.2 L307.0,503.4 M307.0,503.4 L275.6,509.0 M275.6,509.0 L244.8,509.8 M244.8,509.8 L215.4,505.6 M215.4,505.6 L188.4,496.4 M188.4,496.4 L164.5,482.4 M164.5,482.4 L144.4,464.0 M144.4,464.0 L128.8,441.7 M128.8,441.7 L117.9,416.2 M117.9,416.2 L112.1,388.2 M112.1,388.2 L111.4,358.6" fill="none" stroke="#00343f" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><g fill="#00343f" stroke="#00e5ff" stroke-width="0.6"><circle cx="111.4" cy="358.6" r="2.6"/><circle cx="115.7" cy="328.0" r="2.6"/><circle cx="124.8" cy="297.3" r="2.6"/><circle cx="138.4" cy="267.1" r="2.6"/><circle cx="156.2" cy="238.1" r="2.6"/><circle cx="177.7" cy="210.9" r="2.6"/><circle cx="202.5" cy="186.2" r="2.6"/><circle cx="230.1" cy="164.5" r="2.6"/><circle cx="259.9" cy="146.3" r="2.6"/><circle cx="291.1" cy="132.1" r="2.6"/><circle cx="323.1" cy="122.4" r="2.6"/><circle cx="355.1" cy="117.5" r="2.6"/><circle cx="386.1" cy="117.7" r="2.6"/><circle cx="415.4" cy="123.0" r="2.6"/><circle cx="441.9" cy="133.2" r="2.6"/><circle cx="465.1" cy="148.1" r="2.6"/><circle cx="484.2" cy="167.3" r="2.6"/><circle cx="498.7" cy="190.0" r="2.6"/><circle cx="508.5" cy="215.6" r="2.6"/><circle cx="513.3" cy="243.4" r="2.6"/><circle cx="513.2" cy="272.6" r="2.6"/><circle cx="508.4" cy="302.4" r="2.6"/><circle cx="499.1" cy="332.2" r="2.6"/><circle cx="485.6" cy="361.3" r="2.6"/><circle cx="468.3" cy="389.2" r="2.6"/><circle cx="447.5" cy="415.5" r="2.6"/><circle cx="423.7" cy="439.5" r="2.6"/><circle cx="397.2" cy="460.7" r="2.6"/><circle cx="368.5" cy="478.8" r="2.6"/><circle cx="338.3" cy="493.2" r="2.6"/><circle cx="307.0" cy="503.4" r="2.6"/><circle cx="275.6" cy="509.0" r="2.6"/><circle cx="244.8" cy="509.8" r="2.6"/><circle cx="215.4" cy="505.6" r="2.6"/><circle cx="188.4" cy="496.4" r="2.6"/><circle cx="164.5" cy="482.4" r="2.6"/><circle cx="144.4" cy="464.0" r="2.6"/><circle cx="128.8" cy="441.7" r="2.6"/><circle cx="117.9" cy="416.2" r="2.6"/><circle cx="112.1" cy="388.2" r="2.6"/></g><g fill="#00343f" stroke="#00e5ff" stroke-width="0.8"><rect x="121.8" y="294.3" width="6.0" height="6.0"/><rect x="462.1" y="145.1" width="6.0" height="6.0"/><rect x="365.5" y="475.8" width="6.0" height="6.0"/></g><g font-family="Georgia, serif" font-size="8" text-anchor="middle"><text x="190.1" y="252.5" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Craftsmen</text><text x="322.2" y="475.3" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Craftsmen</text><text x="442.9" y="223.1" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Craftsmen</text><text x="251.3" y="268.7" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Noble</text><text x="437.9" y="388.3" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Craftsmen</text><text x="383.2" y="441.5" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Garrison</text><text x="347.8" y="229.4" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Slums</text><text x="224.3" y="455.0" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Residential</text><text x="330.2" y="341.1" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Market</text><text x="204.5" y="487.2" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Residential</text><text x="333.3" y="414.2" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Garrison</text><text x="376.8" y="291.5" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Residential</text><text x="152.8" y="427.0" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Residential</text><text x="273.8" y="435.0" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Noble</text><text x="432.5" y="171.0" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Garrison</text><text x="262.3" y="490.2" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Temple</text><text x="449.0" y="315.9" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Residential</text><text x="383.9" y="366.4" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Noble</text><text x="277.3" y="183.6" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Residential</text><text x="280.6" y="381.9" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Garrison</text><text x="204.1" y="340.5" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Temple</text><text x="365.0" y="155.0" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Residential</text><text x="264.1" y="333.6" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Craftsmen</text><text x="143.0" y="351.1" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Craftsmen</text><text x="486.8" y="262.4" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Garrison</text><text x="303.8" y="280.2" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Slums</text><text x="208.3" y="406.6" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Craftsmen</text><text x="167.0" y="467.3" stroke="#05060a" stroke-width="2.5" paint-order="stroke" fill="#aef7ff">Craftsmen</text></g><text x="315.0" y="20" text-anchor="middle" font-family="Georgia, serif" font-size="16" font-weight="bold" stroke="#05060a" stroke-width="3" paint-order="stroke" fill="#aef7ff">Caldwingate</text></svg>
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="624" height="408" viewBox="0 0 624 408"><rect width="624" height="408" fill="#071925"/><g fill="#123a55"><rect x="12.0" y="12.0" width="60.0" height="12.0"/><rect x="120.0" y="12.0" width="36.0" height="12.0"/><rect x="540.0" y="12.0" width="72.0" height="12.0"/><rect x="12.0" y="24.0" width="60.0" height="12.0"/><rect x="120.0" y="24.0" width="36.0" height="12.0"/><rect x="540.0" y="24.0" width="72.0" height="12.0"/><rect x="12.0" y="36.0" width="60.0" height="12.0"/><rect x="120.0" y="36.0" width="36.0" height="12.0"/><rect x="360.0" y="36.0" width="144.0" height="12.0"/><rect x="540.0" y="36.0" width="72.0" height="12.0"/><rect x="12.0" y="48.0" width="60.0" height="12.0"/><rect x="120.0" y="48.0" width="36.0" height="12.0"/><rect x="360.0" y="48.0" width="144.0" height="12.0"/><rect x="540.0" y="48.0" width="72.0" height="12.0"/><rect x="12.0" y="60.0" width="60.0" height="12.0"/><rect x="120.0" y="60.0" width="36.0" height="12.0"/><rect x="360.0" y="60.0" width="144.0" height="12.0"/><rect x="540.0" y="60.0" width="72.0" height="12.0"/><rect x="12.0" y="72.0" width="600.0" height="12.0"/><rect x="12.0" y="84.0" width="60.0" height="12.0"/><rect x="120.0" y="84.0" width="36.0" height="12.0"/><rect x="252.0" y="84.0" width="360.0" height="12.0"/><rect x="12.0" y="96.0" width="60.0" height="12.0"/><rect x="120.0" y="96.0" width="36.0" height="12.0"/><rect x="252.0" y="96.0" width="72.0" height="12.0"/><rect x="360.0" y="96.0" width="144.0" height="12.0"/><rect x="540.0" y="96.0" width="72.0" height="12.0"/><rect x="12.0" y="108.0" width="60.0" height="12.0"/><rect x="120.0" y="108.0" width="36.0" height="12.0"/><rect x="252.0" y="108.0" width="72.0" height="12.0"/><rect x="360.0" y="108.0" width="144.0" height="12.0"/><rect x="540.0" y="108.0" width="72.0" height="12.0"/><rect x="12.0" y="120.0" width="60.0" height="12.0"/><rect x="120.0" y="120.0" width="36.0" height="12.0"/><rect x="252.0" y="120.0" width="72.0" height="12.0"/><rect x="396.0" y="120.0" width="12.0" height="12.0"/><rect x="540.0" y="120.0" width="72.0" height="12.0"/><rect x="12.0" y="132.0" width="60.0" height="12.0"/><rect x="132.0" y="132.0" width="12.0" height="12.0"/><rect x="252.0" y="132.0" width="72.0" height="12.0"/><rect x="396.0" y="132.0" width="12.0" height="12.0"/><rect x="540.0" y="132.0" width="72.0" height="12.0"/><rect x="36.0" y="144.0" width="24.0" height="12.0"/><rect x="132.0" y="144.0" width="12.0" height="12.0"/><rect x="252.0" y="144.0" width="72.0" height="12.0"/><rect x="396.0" y="144.0" width="12.0" height="12.0"/><rect x="540.0" y="144.0" width="72.0" height="12.0"/><rect x="36.0" y="156.0" width="24.0" height="12.0"/><rect x="132.0" y="156.0" width="12.0" height="12.0"/><rect x="300.0" y="156.0" width="12.0" height="12.0"/><rect x="396.0" y="156.0" width="12.0" height="12.0"/><rect x="576.0" y="156.0" width="12.0" height="12.0"/><rect x="12.0" y="168.0" width="72.0" height="12.0"/><rect x="132.0" y="168.0" width="12.0" height="12.0"/><rect x="300.0" y="168.0" width="12.0" height="12.0"/><rect x="396.0" y="168.0" width="12.0" height="12.0"/><rect x="576.0" y="168.0" width="12.0" height="12.0"/><rect x="12.0" y="180.0" width="72.0" height="12.0"/><rect x="120.0" y="180.0" width="84.0" height="12.0"/><rect x="300.0" y="180.0" width="12.0" height="12.0"/><rect x="396.0" y="180.0" width="12.0" height="12.0"/><rect x="576.0" y="180.0" width="12.0" height="12.0"/><rect x="12.0" y="192.0" width="72.0" height="12.0"/><rect x="120.0" y="192.0" width="84.0" height="12.0"/><rect x="300.0" y="192.0" width="12.0" height="12.0"/><rect x="396.0" y="192.0" width="12.0" height="12.0"/><rect x="576.0" y="192.0" width="12.0" height="12.0"/><rect x="12.0" y="204.0" width="192.0" height="12.0"/><rect x="276.0" y="204.0" width="48.0" height="12.0"/><rect x="396.0" y="204.0" width="12.0" height="12.0"/><rect x="468.0" y="204.0" width="120.0" height="12.0"/><rect x="12.0" y="216.0" width="72.0" height="12.0"/><rect x="120.0" y="216.0" width="84.0" height="12.0"/><rect x="276.0" y="216.0" width="48.0" height="12.0"/><rect x="360.0" y="216.0" width="84.0" height="12.0"/><rect x="468.0" y="216.0" width="120.0" height="12.0"/><rect x="12.0" y="228.0" width="72.0" height="12.0"/><rect x="132.0" y="228.0" width="12.0" height="12.0"/><rect x="276.0" y="228.0" width="312.0" height="12.0"/><rect x="36.0" y="240.0" width="12.0" height="12.0"/><rect x="60.0" y="240.0" width="12.0" height="12.0"/><rect x="132.0" y="240.0" width="12.0" height="12.0"/><rect x="276.0" y="240.0" width="48.0" height="12.0"/><rect x="360.0" y="240.0" width="84.0" height="12.0"/><rect x="468.0" y="240.0" width="108.0" height="12.0"/><rect x="36.0" y="252.0" width="12.0" height="12.0"/><rect x="60.0" y="252.0" width="12.0" height="12.0"/><rect x="132.0" y="252.0" width="12.0" height="12.0"/><rect x="396.0" y="252.0" width="12.0" height="12.0"/><rect x="552.0" y="252.0" width="12.0" height="12.0"/><rect x="36.0" y="264.0" width="48.0" height="12.0"/><rect x="132.0" y="264.0" width="12.0" height="12.0"/><rect x="156.0" y="264.0" width="36.0" height="12.0"/><rect x="396.0" y="264.0" width="12.0" height="12.0"/><rect x="552.0" y="264.0" width="12.0" height="12.0"/><rect x="36.0" y="276.0" width="48.0" height="12.0"/><rect x="132.0" y="276.0" width="12.0" height="12.0"/><rect x="156.0" y="276.0" width="36.0" height="12.0"/><rect x="396.0" y="276.0" width="12.0" height="12.0"/><rect x="552.0" y="276.0" width="12.0" height="12.0"/><rect x="36.0" y="288.0" width="48.0" height="12.0"/><rect x="132.0" y="288.0" width="12.0" height="12.0"/><rect x="156.0" y="288.0" width="36.0" height="12.0"/><rect x="348.0" y="288.0" width="96.0" height="12.0"/><rect x="552.0" y="288.0" width="12.0" height="12.0"/><rect x="36.0" y="300.0" width="48.0" height="12.0"/><rect x="132.0" y="300.0" width="12.0" height="12.0"/><rect x="156.0" y="300.0" width="36.0" height="12.0"/><rect x="348.0" y="300.0" width="96.0" height="12.0"/><rect x="552.0" y="300.0" width="12.0" height="12.0"/><rect x="36.0" y="312.0" width="48.0" height="12.0"/><rect x="132.0" y="312.0" width="12.0" height="12.0"/><rect x="156.0" y="312.0" width="36.0" height="12.0"/><rect x="348.0" y="312.0" width="96.0" height="12.0"/><rect x="552.0" y="312.0" width="12.0" height="12.0"/><rect x="36.0" y="324.0" width="408.0" height="12.0"/><rect x="504.0" y="324.0" width="96.0" height="12.0"/><rect x="48.0" y="336.0" width="36.0" height="12.0"/><rect x="132.0" y="336.0" width="12.0" height="12.0"/><rect x="156.0" y="336.0" width="36.0" height="12.0"/><rect x="264.0" y="336.0" width="36.0" height="12.0"/><rect x="348.0" y="336.0" width="96.0" height="12.0"/><rect x="504.0" y="336.0" width="96.0" height="12.0"/><rect x="48.0" y="348.0" width="36.0" height="12.0"/><rect x="132.0" y="348.0" width="12.0" height="12.0"/><rect x="156.0" y="348.0" width="36.0" height="12.0"/><rect x="264.0" y="348.0" width="36.0" height="12.0"/><rect x="348.0" y="348.0" width="96.0" height="12.0"/><rect x="504.0" y="348.0" width="96.0" height="12.0"/><rect x="48.0" y="360.0" width="36.0" height="12.0"/><rect x="132.0" y="360.0" width="468.0" height="12.0"/><rect x="48.0" y="372.0" width="36.0" height="12.0"/><rect x="156.0" y="372.0" width="36.0" height="12.0"/><rect x="264.0" y="372.0" width="36.0" height="12.0"/><rect x="504.0" y="372.0" width="96.0" height="12.0"/><rect x="48.0" y="384.0" width="36.0" height="12.0"/><rect x="504.0" y="384.0" width="96.0" height="12.0"/></g><g fill="#1a5e78" stroke="#8fe6ff" stroke-width="2" stroke-linejoin="round"><rect x="12.0" y="12.0" width="60.0" height="132.0"/><rect x="120.0" y="12.0" width="36.0" height="120.0"/><rect x="12.0" y="168.0" width="72.0" height="72.0"/><rect x="120.0" y="180.0" width="84.0" height="48.0"/><rect x="48.0" y="264.0" width="36.0" height="132.0"/><rect x="156.0" y="264.0" width="36.0" height="120.0"/><rect x="252.0" y="72.0" width="72.0" height="84.0"/><rect x="360.0" y="36.0" width="144.0" height="84.0"/><rect x="540.0" y="12.0" width="72.0" height="144.0"/><rect x="276.0" y="204.0" width="48.0" height="48.0"/><rect x="360.0" y="216.0" width="84.0" height="36.0"/><rect x="264.0" y="336.0" width="36.0" height="48.0"/><rect x="348.0" y="288.0" width="96.0" height="72.0"/><rect x="468.0" y="204.0" width="108.0" height="48.0"/><rect x="504.0" y="324.0" width="96.0" height="72.0"/></g><g font-family="Georgia, serif" font-size="11" text-anchor="middle"><text x="42.0" y="82.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">1</text><text x="138.0" y="76.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">2</text><text x="48.0" y="208.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">3</text><text x="162.0" y="208.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">4</text><text x="66.0" y="334.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">5</text><text x="174.0" y="328.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">6</text><text x="288.0" y="118.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">7</text><text x="432.0" y="82.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">8</text><text x="576.0" y="88.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">9</text><text x="300.0" y="232.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">10</text><text x="402.0" y="238.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">11</text><text x="282.0" y="364.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">12</text><text x="396.0" y="328.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">13</text><text x="522.0" y="232.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">14</text><text x="552.0" y="364.0" stroke="#071925" stroke-width="3" paint-order="stroke" fill="#d8f4ff">15</text></g></svg>
|
|
@@ -78,15 +78,16 @@ def render_template(template: str, sea_level: float, seed: int) -> str:
|
|
|
78
78
|
return RegionalSVGRenderer(scale=MAP_SCALE).render(t)
|
|
79
79
|
|
|
80
80
|
|
|
81
|
-
def render_dungeon(seed: int = 3) -> str:
|
|
81
|
+
def render_dungeon(seed: int = 3, theme: str = "parchment") -> str:
|
|
82
82
|
dungeon = DungeonGenerator(SeededRNG(seed)).generate(DUNGEON_W, DUNGEON_H)
|
|
83
|
-
return DungeonSVGRenderer(scale=DUNGEON_SCALE).render(
|
|
83
|
+
return DungeonSVGRenderer(scale=DUNGEON_SCALE, theme=theme).render(
|
|
84
|
+
dungeon, labels=True)
|
|
84
85
|
|
|
85
86
|
|
|
86
|
-
def render_settlement(preset: str | None, seed: int) -> str:
|
|
87
|
+
def render_settlement(preset: str | None, seed: int, theme: str = "parchment") -> str:
|
|
87
88
|
cfg = SettlementConfig.preset(preset) if preset else None
|
|
88
89
|
town = SettlementGenerator(SeededRNG(seed)).generate(TOWN_W, TOWN_H, cfg)
|
|
89
|
-
return SettlementSVGRenderer(scale=TOWN_SCALE).render(town)
|
|
90
|
+
return SettlementSVGRenderer(scale=TOWN_SCALE, theme=theme).render(town)
|
|
90
91
|
|
|
91
92
|
|
|
92
93
|
def render_roads(seed: int = 7) -> str:
|
|
@@ -198,6 +199,9 @@ def main() -> None:
|
|
|
198
199
|
emit("theme-neon", render_themed("neon"))
|
|
199
200
|
emit("theme-dune", render_themed("dune"))
|
|
200
201
|
emit("theme-blueprint", render_themed("blueprint"))
|
|
202
|
+
# The same theme drives the town & dungeon renderers too.
|
|
203
|
+
emit("theme-citadel-neon", render_settlement("citadel", seed=3, theme="neon"))
|
|
204
|
+
emit("theme-dungeon-blueprint", render_dungeon(theme="blueprint"))
|
|
201
205
|
|
|
202
206
|
# AtlasRenderer thumbnail — a direct PNG (no SVG), from the bundled sample pack.
|
|
203
207
|
atlas_png = render_atlas(seed=5)
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mapwright"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.18.0"
|
|
8
8
|
description = "Domain-neutral procedural fantasy map & world generation: Voronoi terrain, hydraulic erosion, biomes, rivers, Markov place-names, and shaded-relief SVG."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -24,6 +24,11 @@ Quickstart::
|
|
|
24
24
|
svg = RegionalSVGRenderer().render(terrain)
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
+
from .affordances import (
|
|
28
|
+
CellSummary,
|
|
29
|
+
environment_affordances,
|
|
30
|
+
summarize_cells,
|
|
31
|
+
)
|
|
27
32
|
from .atlas_renderer import ArtPack, AtlasRenderer
|
|
28
33
|
from .config import WorldMapConfig, PRESETS
|
|
29
34
|
from .dungeon import Dungeon, DungeonConfig, DungeonGenerator, Rect
|
|
@@ -55,12 +60,15 @@ from .terrain import (
|
|
|
55
60
|
compute_cell_polygons,
|
|
56
61
|
)
|
|
57
62
|
|
|
58
|
-
__version__ = "0.
|
|
63
|
+
__version__ = "0.18.0"
|
|
59
64
|
|
|
60
65
|
__all__ = [
|
|
61
66
|
"SeededRNG",
|
|
62
67
|
"WorldMapConfig",
|
|
63
68
|
"PRESETS",
|
|
69
|
+
"CellSummary",
|
|
70
|
+
"environment_affordances",
|
|
71
|
+
"summarize_cells",
|
|
64
72
|
"NameGenerator",
|
|
65
73
|
"MarkovNameGenerator",
|
|
66
74
|
"NAMEBASES",
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Neutral environmental affordances and cell aggregation.
|
|
2
|
+
|
|
3
|
+
Two domain-neutral helpers that sit on top of the terrain model:
|
|
4
|
+
|
|
5
|
+
* :func:`environment_affordances` — *what a place affords*: the coarse,
|
|
6
|
+
ecology-level hazards/opportunities implied by a biome plus its climate
|
|
7
|
+
(a swamp breeds disease; a desert is short on water; a hot, wet forest gets
|
|
8
|
+
both predators and biting-insect disease vectors). Tags are neutral strings —
|
|
9
|
+
a host app decides what, if anything, they *mechanically* mean (a D&D layer
|
|
10
|
+
might map ``"scarce_water"`` to an exhaustion clock, ``"predator"`` to an
|
|
11
|
+
encounter table). This library never reaches into game rules.
|
|
12
|
+
|
|
13
|
+
* :func:`summarize_cells` — reduce a set of :class:`TerrainCell` (a place's
|
|
14
|
+
footprint, an explored area, a whole map) to one :class:`CellSummary`:
|
|
15
|
+
dominant biome, mean climate, hydrology flags, and the affordance tags for
|
|
16
|
+
the aggregate. Pure stats; the caller chooses which cells make up the place.
|
|
17
|
+
|
|
18
|
+
Both are deterministic — same cells in, same summary out — so they fit the
|
|
19
|
+
seed-reproducible contract of the rest of mapwright.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from collections import Counter
|
|
25
|
+
from dataclasses import dataclass
|
|
26
|
+
from typing import Iterable
|
|
27
|
+
|
|
28
|
+
from .terrain import Biome, TerrainCell
|
|
29
|
+
|
|
30
|
+
# Base affordances intrinsic to each biome, independent of climate swing.
|
|
31
|
+
# Neutral, ecology-level vocabulary — not game mechanics.
|
|
32
|
+
_BIOME_BASE_AFFORDANCES: dict[Biome, tuple[str, ...]] = {
|
|
33
|
+
Biome.OCEAN: ("drowning", "no_shelter", "open_water"),
|
|
34
|
+
Biome.COAST: ("tides", "open_water"),
|
|
35
|
+
Biome.BEACH: ("tides", "exposure"),
|
|
36
|
+
Biome.DESERT: ("scarce_water", "exposure", "extreme_heat"),
|
|
37
|
+
Biome.PLAINS: ("exposure",),
|
|
38
|
+
Biome.FOREST: ("dense_cover", "predator", "easy_to_get_lost"),
|
|
39
|
+
Biome.SWAMP: ("disease_vector", "difficult_terrain", "predator", "miasma"),
|
|
40
|
+
Biome.HILLS: ("difficult_terrain",),
|
|
41
|
+
Biome.MOUNTAIN: ("rockfall", "thin_air", "difficult_terrain", "extreme_cold"),
|
|
42
|
+
Biome.TUNDRA: ("extreme_cold", "exposure", "scarce_food"),
|
|
43
|
+
Biome.SNOW: ("extreme_cold", "exposure", "whiteout"),
|
|
44
|
+
Biome.RIVER: ("currents", "water_source"),
|
|
45
|
+
Biome.LAKE: ("cold_water", "water_source"),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Climate thresholds (on the 0..1 cell scales) that add extra affordances on top
|
|
49
|
+
# of the biome base — this is what makes a *hot, wet* forest read as a steamy
|
|
50
|
+
# jungle (predators + disease vectors) rather than a temperate wood.
|
|
51
|
+
_ARID = 0.22
|
|
52
|
+
_HUMID = 0.78
|
|
53
|
+
_COLD = 0.22
|
|
54
|
+
_HOT = 0.78
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def environment_affordances(
|
|
58
|
+
biome: Biome, temperature: float, moisture: float
|
|
59
|
+
) -> tuple[str, ...]:
|
|
60
|
+
"""Neutral affordance tags for a biome under a given climate.
|
|
61
|
+
|
|
62
|
+
``temperature`` and ``moisture`` are the 0..1 cell scales. Returns a
|
|
63
|
+
de-duplicated tuple, biome-base tags first then climate-driven additions,
|
|
64
|
+
each in a stable order so the result is reproducible.
|
|
65
|
+
"""
|
|
66
|
+
base = _BIOME_BASE_AFFORDANCES.get(biome, ())
|
|
67
|
+
tags: list[str] = list(base)
|
|
68
|
+
# A freshwater biome (river/lake) affords drinking water, so heat/aridity must
|
|
69
|
+
# not also call water "scarce" — that's contradictory. An ocean keeps
|
|
70
|
+
# scarce_water on purpose: plenty of water at sea, none of it drinkable.
|
|
71
|
+
has_fresh_water = "water_source" in base
|
|
72
|
+
|
|
73
|
+
extra: list[str] = []
|
|
74
|
+
if moisture <= _ARID and not has_fresh_water:
|
|
75
|
+
extra.append("scarce_water")
|
|
76
|
+
if moisture >= _HUMID:
|
|
77
|
+
# standing water + warmth is what actually breeds the biting insects /
|
|
78
|
+
# waterborne illness; pair with heat below for the full jungle effect.
|
|
79
|
+
extra.append("disease_vector")
|
|
80
|
+
if temperature >= _HOT:
|
|
81
|
+
extra.append("extreme_heat")
|
|
82
|
+
if not has_fresh_water:
|
|
83
|
+
extra.append("scarce_water")
|
|
84
|
+
if temperature <= _COLD:
|
|
85
|
+
extra.append("extreme_cold")
|
|
86
|
+
|
|
87
|
+
# de-dup, preserving first-seen order (base before climate extras)
|
|
88
|
+
seen: dict[str, None] = {}
|
|
89
|
+
for tag in (*tags, *extra):
|
|
90
|
+
seen.setdefault(tag, None)
|
|
91
|
+
return tuple(seen)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass
|
|
95
|
+
class CellSummary:
|
|
96
|
+
"""Aggregate of a set of terrain cells — one place's environment."""
|
|
97
|
+
|
|
98
|
+
dominant_biome: Biome
|
|
99
|
+
temperature: float # mean over the cells, 0..1
|
|
100
|
+
moisture: float # mean over the cells, 0..1
|
|
101
|
+
mean_height: float # mean raw height, 0..1 (compare to a TerrainResult.sea_level)
|
|
102
|
+
has_river: bool
|
|
103
|
+
has_lake: bool
|
|
104
|
+
water_fraction: float # fraction of cells that are ocean or lake, 0..1
|
|
105
|
+
cell_count: int
|
|
106
|
+
affordances: tuple[str, ...]
|
|
107
|
+
|
|
108
|
+
def to_dict(self) -> dict:
|
|
109
|
+
return {
|
|
110
|
+
"dominant_biome": int(self.dominant_biome),
|
|
111
|
+
"temperature": self.temperature,
|
|
112
|
+
"moisture": self.moisture,
|
|
113
|
+
"mean_height": self.mean_height,
|
|
114
|
+
"has_river": self.has_river,
|
|
115
|
+
"has_lake": self.has_lake,
|
|
116
|
+
"water_fraction": self.water_fraction,
|
|
117
|
+
"cell_count": self.cell_count,
|
|
118
|
+
"affordances": list(self.affordances),
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
def from_dict(cls, data: dict) -> "CellSummary":
|
|
123
|
+
return cls(
|
|
124
|
+
dominant_biome=Biome(int(data["dominant_biome"])),
|
|
125
|
+
temperature=float(data["temperature"]),
|
|
126
|
+
moisture=float(data["moisture"]),
|
|
127
|
+
mean_height=float(data["mean_height"]),
|
|
128
|
+
has_river=bool(data["has_river"]),
|
|
129
|
+
has_lake=bool(data["has_lake"]),
|
|
130
|
+
water_fraction=float(data["water_fraction"]),
|
|
131
|
+
cell_count=int(data["cell_count"]),
|
|
132
|
+
affordances=tuple(data["affordances"]),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def summarize_cells(cells: Iterable[TerrainCell]) -> CellSummary:
|
|
137
|
+
"""Reduce a set of cells to one :class:`CellSummary`.
|
|
138
|
+
|
|
139
|
+
The dominant biome is the modal biome over the cells (ties broken by the
|
|
140
|
+
lowest :class:`Biome` value, for determinism). Climate fields are means.
|
|
141
|
+
Raises ``ValueError`` on an empty input — an empty footprint is a caller
|
|
142
|
+
bug, not something to paper over with a default.
|
|
143
|
+
"""
|
|
144
|
+
cells = list(cells)
|
|
145
|
+
if not cells:
|
|
146
|
+
raise ValueError("summarize_cells requires at least one cell")
|
|
147
|
+
|
|
148
|
+
counts = Counter(c.biome for c in cells)
|
|
149
|
+
top = max(counts.values())
|
|
150
|
+
dominant = min(b for b, n in counts.items() if n == top)
|
|
151
|
+
|
|
152
|
+
n = len(cells)
|
|
153
|
+
temperature = sum(c.temperature for c in cells) / n
|
|
154
|
+
moisture = sum(c.moisture for c in cells) / n
|
|
155
|
+
mean_height = sum(c.height for c in cells) / n
|
|
156
|
+
water_fraction = sum(c.is_water or c.is_lake for c in cells) / n
|
|
157
|
+
|
|
158
|
+
return CellSummary(
|
|
159
|
+
dominant_biome=dominant,
|
|
160
|
+
temperature=temperature,
|
|
161
|
+
moisture=moisture,
|
|
162
|
+
mean_height=mean_height,
|
|
163
|
+
has_river=any(c.is_river for c in cells),
|
|
164
|
+
has_lake=any(c.is_lake for c in cells),
|
|
165
|
+
water_fraction=water_fraction,
|
|
166
|
+
cell_count=n,
|
|
167
|
+
affordances=environment_affordances(dominant, temperature, moisture),
|
|
168
|
+
)
|