mapwright 0.21.0__tar.gz → 0.22.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.21.0 → mapwright-0.22.0}/CHANGELOG.md +23 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/PKG-INFO +14 -7
- {mapwright-0.21.0 → mapwright-0.22.0}/README.md +13 -6
- mapwright-0.22.0/docs/gallery/fortress-town.png +0 -0
- mapwright-0.22.0/docs/gallery/fortress-town.svg +1 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/examples/gallery.py +1 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/pyproject.toml +1 -1
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/__init__.py +3 -1
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/settlement.py +117 -19
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/settlement_renderer.py +22 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_api_contract.py +2 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_settlement.py +81 -5
- {mapwright-0.21.0 → mapwright-0.22.0}/.github/workflows/ci.yml +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/.github/workflows/publish.yml +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/.gitignore +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/LICENSE +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/NOTICE +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/age-old.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/age-old.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/age-young.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/age-young.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/archipelago.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/archipelago.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/arctic.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/arctic.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/README.md +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/city_castle_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/city_large_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/city_town_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/city_village_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/decoration_compass_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/decoration_creature_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/decoration_ship_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/dune_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/hill_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/hill_2.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/manifest.json +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/mountain_mid_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/mountain_old_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/mountain_old_2.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/mountain_young_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/mountain_young_2.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/tree_cactus_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/tree_deciduous_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/tree_deciduous_2.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/tree_pine_1.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/tree_pine_2.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/citadel.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/citadel.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/continent.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/continent.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/desert.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/desert.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/dungeon.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/dungeon.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/grid-city.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/grid-city.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/highlands.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/highlands.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/hint.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/hint.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/islands.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/islands.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/metropolis.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/metropolis.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/pangaea.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/pangaea.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/port.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/port.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/regions.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/regions.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/roads.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/roads.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/shantytown.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/shantytown.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/template-atoll.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/template-atoll.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/template-isthmus.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/template-isthmus.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-blueprint.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-blueprint.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-citadel-neon.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-citadel-neon.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-dune.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-dune.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-dungeon-blueprint.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-dungeon-blueprint.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-neon.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-neon.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-parchment.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-parchment.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/town.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/town.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/tropical.png +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/tropical.svg +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/examples/benchmark.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/_geometry.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/_graph.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/_serde.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/affordances.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/atlas_renderer.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/config.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/dungeon.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/dungeon_renderer.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/names.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/regions.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/rng.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/roads.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/svg_renderer.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/terrain.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/themes.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_affordances.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_atlas_renderer.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_config.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_dungeon.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_dungeon_renderer.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_geometry.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_graph.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_names.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_properties.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_regions.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_rng.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_roads.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_serialize.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_svg_renderer.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_terrain.py +0 -0
- {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_themes.py +0 -0
|
@@ -8,6 +8,29 @@ 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.22.0] — 2026-06-02
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- **Settlement `purpose` + landmarks — what a town exists for.** A new
|
|
15
|
+
`SettlementConfig.purpose` field (`"general"` default, or `"trade"`,
|
|
16
|
+
`"fortress"`, `"religious"`, `"harbor"`, `"extraction"`, `"transit"`). Anything
|
|
17
|
+
but `"general"`:
|
|
18
|
+
- promotes the central ward to a purpose-specific **landmark** kind
|
|
19
|
+
(fortress→`citadel`, religious→`temple`, trade→`market`, harbor→`docks`,
|
|
20
|
+
extraction→`mine`, transit→`plaza`), recorded on the new
|
|
21
|
+
`Settlement.landmark` field (a new public `Landmark` type);
|
|
22
|
+
- focuses the **main roads** on that landmark (they radiate from it to the
|
|
23
|
+
gates);
|
|
24
|
+
- **biases the ward-kind mix** toward the purpose (e.g. fortress → more
|
|
25
|
+
garrison wards).
|
|
26
|
+
- The renderer draws a star glyph over the landmark ward.
|
|
27
|
+
- New presets **`fortress_town`**, **`pilgrimage_site`**, **`mining_camp`**;
|
|
28
|
+
gallery `fortress-town` showcase.
|
|
29
|
+
- `"general"` output is **byte-identical** to before (no landmark, unchanged
|
|
30
|
+
ward bag and roads). New `Landmark` public type; `Settlement` serialisation
|
|
31
|
+
bumps to `mapwright/settlement@5` (back-compatible — older payloads load with
|
|
32
|
+
no landmark and `purpose="general"`).
|
|
33
|
+
|
|
11
34
|
## [0.21.0] — 2026-06-02
|
|
12
35
|
|
|
13
36
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mapwright
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.22.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
|
|
@@ -100,6 +100,10 @@ produced by [`examples/gallery.py`](examples/gallery.py):
|
|
|
100
100
|
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/grid-city.png" alt="planned town with a geometric street grid"><br><sub><code>layout="grid"</code> (grid_city)</sub></td>
|
|
101
101
|
</tr>
|
|
102
102
|
<tr>
|
|
103
|
+
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/fortress-town.png" alt="walled fortress town with a central citadel landmark"><br><sub><code>purpose="fortress"</code> (fortress_town)</sub></td>
|
|
104
|
+
<td align="center" colspan="2"><sub>A <code>purpose</code> seeds a central <b>landmark</b> (★) the main roads focus on — citadel, temple, mine, … — and biases the ward mix toward what the town is for.</sub></td>
|
|
105
|
+
</tr>
|
|
106
|
+
<tr>
|
|
103
107
|
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/roads.png" alt="settlements linked by terrain-routed roads"><br><sub><code>RegionalRoadGenerator</code></sub></td>
|
|
104
108
|
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/regions.png" alt="land partitioned into named territories"><br><sub><code>RegionGenerator</code></sub></td>
|
|
105
109
|
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/template-isthmus.png" alt="isthmus heightmap template"><br><sub><code>template="isthmus"</code></sub></td>
|
|
@@ -226,11 +230,14 @@ open("town.svg", "w").write(SettlementSVGRenderer().render(town))
|
|
|
226
230
|
```
|
|
227
231
|
|
|
228
232
|
Settlement presets: `hamlet`, `village`, `town`, `city`, `port`, `citadel`,
|
|
229
|
-
`shantytown`, `metropolis`, `grid_city
|
|
230
|
-
(
|
|
231
|
-
mix, and block regularity. The
|
|
232
|
-
|
|
233
|
-
aligned to the town's long axis,
|
|
233
|
+
`shantytown`, `metropolis`, `grid_city`, `fortress_town`, `pilgrimage_site`,
|
|
234
|
+
`mining_camp`. The `wealth` (poor ⇄ rich) and `era` (ancient ⇄ modern) knobs drive
|
|
235
|
+
the shanty↔skyscraper axis — plot size, ward-kind mix, and block regularity. The
|
|
236
|
+
`layout` knob picks the street pattern: `"organic"` (winding ward-to-ward roads,
|
|
237
|
+
the default) or `"grid"` (a geometric street grid aligned to the town's long axis,
|
|
238
|
+
with grid-aligned lots). The `purpose` knob (`"fortress"`, `"religious"`,
|
|
239
|
+
`"extraction"`, …) gives the town a central **landmark** the main roads focus on
|
|
240
|
+
and biases its ward mix toward what it's for.
|
|
234
241
|
|
|
235
242
|
## What's inside
|
|
236
243
|
|
|
@@ -248,7 +255,7 @@ aligned to the town's long axis, with grid-aligned lots).
|
|
|
248
255
|
| `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. |
|
|
249
256
|
| `DungeonGenerator` | BSP-partitioned rooms + minimum-spanning-tree corridors → rooms, corridor cells, and a walkable grid (with `Dungeon.ascii()`). |
|
|
250
257
|
| `DungeonSVGRenderer` | Renders a `Dungeon` to SVG: walls, carved floor, room outlines, optional tile grid and per-room labels. Takes a `theme=`. |
|
|
251
|
-
| `SettlementGenerator` | Self-contained town layout: an organic footprint divided into named Voronoi **wards** (market, docks, …), each subdivided into building **lots**, a **street** network (`layout="organic"` → MST over ward adjacency + gate-to-
|
|
258
|
+
| `SettlementGenerator` | Self-contained town layout: an organic footprint divided into named Voronoi **wards** (market, docks, …), each subdivided into building **lots**, a **street** network (`layout="organic"` → MST over ward adjacency + gate-to-hub roads; `layout="grid"` → a geometric street grid + grid-aligned lots), an optional defensive **wall** (towers + gate gaps, opened at the harbour when coastal), an optional **`purpose`** that places a central **landmark** (citadel/temple/mine/…) the main roads focus on, and optional coastline. |
|
|
252
259
|
| `SettlementSVGRenderer` | Renders a `Settlement` to SVG: sea, footprint, kind-coloured wards, building lots, streets, wall with towers/gatehouses, labels. Takes a `theme=`. |
|
|
253
260
|
|
|
254
261
|
Everything is neutral: `RegionalTerrainGenerator` returns a `TerrainResult` of `TerrainCell`s
|
|
@@ -73,6 +73,10 @@ produced by [`examples/gallery.py`](examples/gallery.py):
|
|
|
73
73
|
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/grid-city.png" alt="planned town with a geometric street grid"><br><sub><code>layout="grid"</code> (grid_city)</sub></td>
|
|
74
74
|
</tr>
|
|
75
75
|
<tr>
|
|
76
|
+
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/fortress-town.png" alt="walled fortress town with a central citadel landmark"><br><sub><code>purpose="fortress"</code> (fortress_town)</sub></td>
|
|
77
|
+
<td align="center" colspan="2"><sub>A <code>purpose</code> seeds a central <b>landmark</b> (★) the main roads focus on — citadel, temple, mine, … — and biases the ward mix toward what the town is for.</sub></td>
|
|
78
|
+
</tr>
|
|
79
|
+
<tr>
|
|
76
80
|
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/roads.png" alt="settlements linked by terrain-routed roads"><br><sub><code>RegionalRoadGenerator</code></sub></td>
|
|
77
81
|
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/regions.png" alt="land partitioned into named territories"><br><sub><code>RegionGenerator</code></sub></td>
|
|
78
82
|
<td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/template-isthmus.png" alt="isthmus heightmap template"><br><sub><code>template="isthmus"</code></sub></td>
|
|
@@ -199,11 +203,14 @@ open("town.svg", "w").write(SettlementSVGRenderer().render(town))
|
|
|
199
203
|
```
|
|
200
204
|
|
|
201
205
|
Settlement presets: `hamlet`, `village`, `town`, `city`, `port`, `citadel`,
|
|
202
|
-
`shantytown`, `metropolis`, `grid_city
|
|
203
|
-
(
|
|
204
|
-
mix, and block regularity. The
|
|
205
|
-
|
|
206
|
-
aligned to the town's long axis,
|
|
206
|
+
`shantytown`, `metropolis`, `grid_city`, `fortress_town`, `pilgrimage_site`,
|
|
207
|
+
`mining_camp`. The `wealth` (poor ⇄ rich) and `era` (ancient ⇄ modern) knobs drive
|
|
208
|
+
the shanty↔skyscraper axis — plot size, ward-kind mix, and block regularity. The
|
|
209
|
+
`layout` knob picks the street pattern: `"organic"` (winding ward-to-ward roads,
|
|
210
|
+
the default) or `"grid"` (a geometric street grid aligned to the town's long axis,
|
|
211
|
+
with grid-aligned lots). The `purpose` knob (`"fortress"`, `"religious"`,
|
|
212
|
+
`"extraction"`, …) gives the town a central **landmark** the main roads focus on
|
|
213
|
+
and biases its ward mix toward what it's for.
|
|
207
214
|
|
|
208
215
|
## What's inside
|
|
209
216
|
|
|
@@ -221,7 +228,7 @@ aligned to the town's long axis, with grid-aligned lots).
|
|
|
221
228
|
| `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. |
|
|
222
229
|
| `DungeonGenerator` | BSP-partitioned rooms + minimum-spanning-tree corridors → rooms, corridor cells, and a walkable grid (with `Dungeon.ascii()`). |
|
|
223
230
|
| `DungeonSVGRenderer` | Renders a `Dungeon` to SVG: walls, carved floor, room outlines, optional tile grid and per-room labels. Takes a `theme=`. |
|
|
224
|
-
| `SettlementGenerator` | Self-contained town layout: an organic footprint divided into named Voronoi **wards** (market, docks, …), each subdivided into building **lots**, a **street** network (`layout="organic"` → MST over ward adjacency + gate-to-
|
|
231
|
+
| `SettlementGenerator` | Self-contained town layout: an organic footprint divided into named Voronoi **wards** (market, docks, …), each subdivided into building **lots**, a **street** network (`layout="organic"` → MST over ward adjacency + gate-to-hub roads; `layout="grid"` → a geometric street grid + grid-aligned lots), an optional defensive **wall** (towers + gate gaps, opened at the harbour when coastal), an optional **`purpose`** that places a central **landmark** (citadel/temple/mine/…) the main roads focus on, and optional coastline. |
|
|
225
232
|
| `SettlementSVGRenderer` | Renders a `Settlement` to SVG: sea, footprint, kind-coloured wards, building lots, streets, wall with towers/gatehouses, labels. Takes a `theme=`. |
|
|
226
233
|
|
|
227
234
|
Everything is neutral: `RegionalTerrainGenerator` returns a `TerrainResult` of `TerrainCell`s
|
|
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="#c9d2bb"/><polygon points="112.4,390.5 112.4,361.1 117.4,330.8 127.1,300.4 141.2,270.5 159.2,241.7 180.9,214.6 205.7,189.8 233.2,167.8 262.9,149.2 294.0,134.5 326.0,124.2 357.9,118.7 388.8,118.3 417.8,123.1 444.0,133.0 466.6,147.7 484.9,166.7 498.5,189.2 507.1,214.6 510.8,242.1 509.6,270.7 503.8,299.9 493.9,328.9 480.0,357.3 462.7,384.5 442.2,410.2 418.8,433.9 392.9,455.1 364.9,473.5 335.1,488.5 304.4,499.5 273.2,506.1 242.6,507.9 213.4,504.7 186.5,496.3 162.7,483.1 143.0,465.2 127.8,443.3 117.5,418.2" fill="#e6dcc0"/><g stroke="#4a4230" stroke-width="1" stroke-linejoin="round"><polygon points="133.8,286.2 141.2,270.5 159.2,241.7 180.9,214.6 205.7,189.8 212.6,184.3 240.8,218.7 215.1,296.2 163.9,301.6" fill="#b3a684"/><polygon points="360.3,475.8 335.1,488.5 304.4,499.5 294.4,501.6 285.4,464.7 307.5,438.2 343.2,445.0" fill="#b3a684"/><polygon points="492.7,179.7 498.5,189.2 500.5,195.2 438.6,261.5 413.2,261.6 395.8,245.4 388.9,204.9" fill="#b3a684"/><polygon points="215.1,296.2 240.8,218.7 285.3,232.4 273.6,299.8 230.6,307.4" fill="#b9a98c"/><polygon points="480.9,355.5 480.0,357.3 462.7,384.5 442.2,410.2 420.8,431.8 390.6,401.4 421.6,345.9" fill="#c2b48f"/><polygon points="420.8,431.8 418.8,433.9 392.9,455.1 364.9,473.5 360.3,475.8 343.2,445.0 368.3,400.1 390.6,401.4" fill="#c2b48f"/><polygon points="386.9,202.0 388.9,204.9 395.8,245.4 337.2,270.5 293.7,231.0 334.1,182.5" fill="#c2b48f"/><polygon points="230.1,481.5 189.2,452.7 189.0,451.2 192.4,439.8 239.4,422.6 254.0,455.7" fill="#d8cdb0"/><polygon points="332.5,298.8 366.1,329.3 343.2,374.1 322.1,374.4 296.7,344.9 298.9,316.8" fill="#cdbf9e"/><polygon points="229.5,506.5 213.4,504.7 186.5,496.3 173.4,489.0 189.2,452.7 230.1,481.5" fill="#c2b48f"/><polygon points="343.2,445.0 307.5,438.2 296.9,407.4 322.1,374.4 343.2,374.1 368.3,400.1" fill="#c2b48f"/><polygon points="337.2,270.5 395.8,245.4 413.2,261.6 400.2,323.2 366.1,329.3 332.5,298.8" fill="#c2b48f"/><polygon points="192.4,439.8 189.0,451.2 131.4,448.6 127.8,443.3 117.5,418.2 114.5,402.1 167.3,392.4" fill="#b9a98c"/><polygon points="296.9,407.4 307.5,438.2 285.4,464.7 254.0,455.7 239.4,422.6 247.9,398.8" fill="#b3a684"/><polygon points="415.9,122.8 417.8,123.1 444.0,133.0 466.6,147.7 484.9,166.7 492.7,179.7 388.9,204.9 386.9,202.0" fill="#b3a684"/><polygon points="294.4,501.6 273.2,506.1 242.6,507.9 229.5,506.5 230.1,481.5 254.0,455.7 285.4,464.7" fill="#dfd6c0"/><polygon points="499.4,312.9 493.9,328.9 480.9,355.5 421.6,345.9 400.2,323.2 413.2,261.6 438.6,261.5" fill="#b9a98c"/><polygon points="421.6,345.9 390.6,401.4 368.3,400.1 343.2,374.1 366.1,329.3 400.2,323.2" fill="#c2b48f"/><polygon points="212.6,184.3 233.2,167.8 262.9,149.2 294.0,134.5 315.2,127.7 334.1,182.5 293.7,231.0 285.3,232.4 240.8,218.7" fill="#cdbf9e"/><polygon points="296.7,344.9 322.1,374.4 296.9,407.4 247.9,398.8 237.5,374.7 241.1,366.7" fill="#cdbf9e"/><polygon points="163.9,301.6 215.1,296.2 230.6,307.4 241.1,366.7 237.5,374.7 181.2,375.8" fill="#b3a684"/><polygon points="315.2,127.7 326.0,124.2 357.9,118.7 388.8,118.3 415.9,122.8 386.9,202.0 334.1,182.5" fill="#b9a98c"/><polygon points="230.6,307.4 273.6,299.8 298.9,316.8 296.7,344.9 241.1,366.7" fill="#cdbf9e"/><polygon points="112.4,390.5 112.4,361.1 117.4,330.8 127.1,300.4 133.8,286.2 163.9,301.6 181.2,375.8 167.3,392.4 114.5,402.1" fill="#b9a98c"/><polygon points="500.5,195.2 507.1,214.6 510.8,242.1 509.6,270.7 503.8,299.9 499.4,312.9 438.6,261.5" fill="#d8cdb0"/><polygon points="273.6,299.8 285.3,232.4 293.7,231.0 337.2,270.5 332.5,298.8 298.9,316.8" fill="#dfd6c0"/><polygon points="237.5,374.7 247.9,398.8 239.4,422.6 192.4,439.8 167.3,392.4 181.2,375.8" fill="#cdbf9e"/><polygon points="189.0,451.2 189.2,452.7 173.4,489.0 162.7,483.1 143.0,465.2 131.4,448.6" fill="#d8cdb0"/></g><g fill="#7d6c52" stroke="#4a3e2e" stroke-width="0.5" stroke-linejoin="round"><polygon points="238.4,219.9 236.0,227.2 228.0,224.6 230.4,217.3"/><polygon points="234.9,230.7 232.6,237.6 224.5,234.9 226.8,228.1"/><polygon points="210.1,214.4 211.2,211.0 226.8,216.2 225.7,219.5"/><polygon points="213.6,230.9 205.7,228.3 209.0,218.4 216.9,221.0"/><polygon points="221.1,233.9 217.1,232.6 220.8,221.7 224.7,223.1"/><polygon points="231.8,240.9 229.1,249.0 224.7,247.5 227.4,239.4"/><polygon points="221.7,246.3 215.7,244.4 218.3,236.6 224.3,238.6"/><polygon points="212.2,242.8 202.0,239.4 204.3,232.4 214.5,235.8"/><polygon points="223.5,207.7 220.2,210.4 211.8,207.6 212.9,204.3"/><polygon points="217.2,193.0 226.7,204.5 226.1,205.0 214.5,201.2"/><polygon points="229.2,207.1 236.0,215.5 223.9,211.4"/><polygon points="205.5,192.2 206.7,191.0 212.4,186.4 214.7,189.3 213.0,194.7"/><polygon points="197.2,201.3 203.2,195.3 211.2,197.9 208.9,205.2"/><polygon points="201.8,206.8 208.1,208.9 205.8,215.9 199.5,213.8"/><polygon points="188.0,210.0 193.7,204.2 198.6,205.8 196.3,212.7"/><polygon points="199.2,234.7 198.0,238.1 181.9,232.8 183.0,229.3"/><polygon points="202.7,224.8 200.6,231.0 191.9,228.2 193.9,221.9"/><polygon points="195.5,215.7 205.1,218.9 204.1,221.9 194.5,218.7"/><polygon points="188.0,213.7 192.3,215.1 188.4,227.0 184.1,225.6"/><polygon points="171.7,229.4 182.4,216.0 183.4,215.1 178.0,231.4"/><polygon points="190.6,239.6 196.2,241.5 193.3,250.0 187.8,248.2"/><polygon points="199.3,242.3 203.3,243.6 200.4,252.6 196.4,251.2"/><polygon points="199.4,255.5 198.5,258.0 185.8,253.7 186.6,251.3"/><polygon points="197.1,261.2 195.5,265.8 183.5,261.8 185.0,257.2"/><polygon points="184.2,249.0 183.2,251.8 172.6,248.3 173.5,245.4"/><polygon points="181.8,254.8 179.8,260.7 170.0,257.4 171.9,251.6"/><polygon points="177.4,235.6 187.0,238.7 184.7,245.5 175.1,242.3"/><polygon points="161.6,242.0 168.9,232.8 173.5,234.3 170.0,244.7"/><polygon points="155.1,252.2 159.3,245.6 168.6,248.7 166.3,255.9"/><polygon points="174.7,270.6 172.1,278.4 167.3,276.8 169.9,268.9"/><polygon points="164.2,275.5 157.7,273.4 160.1,265.9 166.7,268.1"/><polygon points="162.6,258.9 176.7,263.6 175.5,267.2 161.4,262.5"/><polygon points="144.3,268.9 148.1,262.8 155.8,267.7 154.3,272.2"/><polygon points="149.8,259.8 152.6,255.4 159.3,257.6 157.1,264.4"/><polygon points="163.2,278.8 171.0,281.4 169.3,286.6 160.7,283.8"/><polygon points="167.6,290.1 164.5,299.4 164.3,299.4 155.6,294.9 159.4,287.4"/><polygon points="146.7,273.5 152.8,275.5 149.8,284.5 143.7,282.5"/><polygon points="137.6,281.0 142.1,271.4 143.9,272.0 140.6,282.0"/><polygon points="135.9,285.4 136.7,283.7 149.0,287.8 147.8,291.5"/><polygon points="156.3,276.6 159.9,277.8 151.9,293.4 150.9,292.9"/><polygon points="185.5,295.4 184.9,297.2 169.2,298.9 171.9,290.9"/><polygon points="180.4,265.0 194.1,269.6 192.6,274.3 178.8,269.7"/><polygon points="181.2,289.6 173.0,286.9 175.6,279.1 183.8,281.8"/><polygon points="176.1,275.7 177.1,272.8 186.4,275.9 185.4,278.8"/><polygon points="191.8,278.0 187.2,291.9 184.6,291.0 189.2,277.1"/><polygon points="205.6,295.2 189.4,296.9 191.5,290.5"/><polygon points="196.3,274.5 197.4,271.0 207.1,274.2 205.9,277.6"/><polygon points="201.3,289.5 192.9,286.7 195.7,278.1 204.2,280.9"/><polygon points="216.4,285.9 213.8,293.7 205.2,290.9 207.8,283.1"/><polygon points="219.3,278.3 217.9,282.6 208.6,279.5 210.0,275.2"/><polygon points="206.2,244.6 212.7,246.8 210.7,252.9 204.2,250.7"/><polygon points="215.9,248.3 227.6,252.2 225.9,257.4 214.2,253.6"/><polygon points="222.8,267.0 220.2,274.9 212.7,272.5 215.4,264.5"/><polygon points="225.4,260.8 224.3,264.1 215.9,261.3 217.0,258.0"/><polygon points="209.1,271.1 199.0,267.8 201.2,261.3 211.2,264.7"/><polygon points="202.0,257.7 203.3,253.8 214.0,257.4 212.7,261.3"/><polygon points="300.3,498.8 295.6,499.8 293.5,491.2 298.2,490.0"/><polygon points="312.2,494.6 303.8,497.6 303.7,497.6 301.7,489.6 310.5,487.4"/><polygon points="292.8,488.0 290.6,478.9 295.5,477.7 297.7,486.8"/><polygon points="299.2,477.2 307.5,475.2 309.5,483.6 301.2,485.6"/><polygon points="327.6,489.1 321.9,491.1 318.2,480.7 325.1,479.0"/><polygon points="318.8,492.8 315.6,493.9 312.6,481.6 314.6,481.1"/><polygon points="312.2,478.3 311.2,474.1 323.4,471.1 324.4,475.3"/><polygon points="309.0,459.4 311.1,458.9 313.8,470.2 311.8,470.7"/><polygon points="314.3,458.7 320.0,457.4 322.5,467.5 316.8,468.9"/><polygon points="295.8,463.1 306.2,460.6 307.3,465.4 296.9,467.9"/><polygon points="308.4,468.4 309.1,471.2 298.1,473.9 297.4,471.1"/><polygon points="289.9,475.6 287.4,465.2 292.4,464.0 294.9,474.3"/><polygon points="345.6,481.5 334.5,487.1 331.0,488.3 330.3,485.2"/><polygon points="335.2,470.2 342.4,468.4 344.7,478.0 337.6,479.8"/><polygon points="329.7,482.1 327.2,471.8 331.6,470.7 334.1,481.0"/><polygon points="357.4,474.9 349.2,479.0 346.5,467.6 352.5,466.1"/><polygon points="338.5,453.2 344.4,451.8 350.2,462.1 341.2,464.3"/><polygon points="326.6,468.2 325.1,462.2 335.7,459.7 337.2,465.6"/><polygon points="323.9,459.0 323.2,456.2 334.7,453.4 335.3,456.2"/><polygon points="321.2,442.3 326.5,443.3 324.6,452.9 323.9,453.0"/><polygon points="329.6,444.4 342.0,446.7 342.7,448.0 328.2,451.5"/><polygon points="318.3,445.5 320.2,453.1 309.2,455.8 307.3,448.2"/><polygon points="305.3,443.1 308.1,439.8 318.2,441.7 318.2,441.8 305.7,444.8"/><polygon points="299.5,450.3 302.9,446.2 305.6,457.3 301.4,458.3"/><polygon points="290.1,461.3 297.0,452.9 298.6,459.2"/><polygon points="415.3,200.4 419.6,199.4 422.8,212.6 418.5,213.6"/><polygon points="423.0,198.3 433.3,195.8 434.4,200.1 424.0,202.7"/><polygon points="435.0,203.6 436.4,209.3 426.4,211.7 425.0,206.0"/><polygon points="437.3,212.9 439.5,222.0 433.4,223.5 431.1,214.4"/><polygon points="429.6,224.2 421.7,226.1 419.6,217.4 427.5,215.4"/><polygon points="416.4,221.1 417.8,227.1 407.0,229.7 405.6,223.7"/><polygon points="403.3,215.1 414.6,213.2 415.6,217.4 404.5,220.1"/><polygon points="395.3,233.0 394.4,227.3 402.4,226.0 403.7,231.0"/><polygon points="393.9,224.0 392.7,216.9 399.8,215.7 401.5,222.7"/><polygon points="402.5,203.7 411.5,201.5 413.5,209.5 403.8,211.2"/><polygon points="392.2,213.4 391.0,206.3 398.7,204.4 400.0,212.0"/><polygon points="400.5,247.0 397.6,244.4 396.3,236.4 402.2,235.0 404.8,246.0"/><polygon points="405.6,233.8 409.0,233.0 411.9,244.6 408.4,245.5"/><polygon points="415.2,259.7 414.0,259.7 403.8,250.2 412.4,248.1"/><polygon points="414.0,239.3 412.3,232.2 419.3,230.5 421.0,237.6"/><polygon points="416.3,248.9 414.7,242.6 421.9,240.8 423.4,247.2"/><polygon points="426.3,259.7 419.1,259.8 417.3,252.2 424.1,250.6"/><polygon points="436.7,259.8 430.0,259.8 428.3,252.9 434.7,251.4"/><polygon points="446.7,250.5 439.7,258.0 437.9,250.5 446.2,248.5"/><polygon points="427.8,249.0 426.8,245.0 443.8,240.9 444.8,244.9"/><polygon points="425.7,240.9 423.0,230.0 429.3,228.5 431.9,239.4"/><polygon points="433.3,227.6 440.2,225.9 442.8,236.7 435.9,238.4"/><polygon points="458.7,205.5 457.5,204.5 462.8,198.8 467.7,203.3"/><polygon points="465.5,196.7 472.6,189.2 475.4,200.8 471.1,201.9"/><polygon points="479.4,215.5 474.9,220.5 471.4,217.2 478.1,210.0"/><polygon points="469.1,214.9 466.2,212.1 472.5,205.3 476.5,204.3 477.1,206.4"/><polygon points="463.7,210.7 460.4,207.6 468.5,205.6"/><polygon points="449.8,197.8 444.1,192.5 448.3,191.5"/><polygon points="455.5,201.7 453.3,199.7 451.4,191.8 460.0,189.7 461.4,195.3"/><polygon points="463.0,188.2 471.0,186.2 464.3,193.4"/><polygon points="490.0,204.3 481.9,212.9 480.4,206.6"/><polygon points="497.8,194.2 498.0,194.7 493.3,199.7 480.2,202.9 479.2,198.8"/><polygon points="491.7,182.0 496.6,190.1 496.6,190.2 487.3,192.4 485.2,183.6"/><polygon points="483.7,193.6 477.8,195.0 475.6,185.7 481.4,184.3"/><polygon points="465.8,229.3 456.1,239.6 449.4,233.3"/><polygon points="454.0,242.7 449.3,247.7 446.4,235.6"/><polygon points="457.4,223.1 454.4,210.7 458.2,209.7 464.4,215.6"/><polygon points="472.4,222.7 471.3,223.9 459.0,226.9 467.4,218.0"/><polygon points="454.3,227.7 445.5,229.8 443.4,221.4 452.3,219.3"/><polygon points="442.1,217.9 441.0,213.5 450.8,211.1 451.9,215.6"/><polygon points="440.3,208.5 437.2,195.4 441.5,194.3 447.9,200.3"/><polygon points="451.0,202.3 455.7,206.7 444.3,209.5"/><polygon points="219.2,294.9 224.2,279.8 235.5,283.6 229.3,302.2"/><polygon points="241.7,285.0 249.4,287.6 244.6,302.0 235.5,303.6"/><polygon points="251.9,260.9 257.7,262.8 251.3,282.2 245.5,280.3"/><polygon points="226.7,273.1 232.6,255.5 245.4,259.7 239.6,277.3"/><polygon points="272.4,285.3 270.4,296.6 252.3,299.8 258.6,280.7"/><polygon points="263.0,264.6 276.1,268.9 274.3,279.0 259.9,274.2"/><polygon points="274.6,233.1 281.0,235.1 276.3,262.0 266.2,258.6"/><polygon points="240.7,230.2 243.1,223.1 267.7,230.7 265.2,238.3"/><polygon points="235.3,248.2 238.8,237.5 262.3,245.3 258.8,256.0"/><polygon points="397.0,394.5 404.3,381.4 409.4,384.3 402.1,397.4"/><polygon points="413.5,386.8 420.0,390.4 412.9,403.1 406.4,399.5"/><polygon points="406.8,414.2 393.6,401.0 394.8,398.7 410.6,407.5"/><polygon points="414.8,422.5 410.2,417.9 416.8,406.1 422.3,409.2"/><polygon points="433.4,415.6 420.8,428.4 418.4,425.9 426.4,411.6"/><polygon points="428.6,407.2 419.3,402.0 424.4,392.9 433.7,398.1"/><polygon points="444.7,403.7 440.6,408.8 437.1,412.4 432.4,409.8 437.9,399.9"/><polygon points="415.2,363.7 423.2,349.4 431.5,350.7 429.0,365.9"/><polygon points="425.9,387.8 415.9,382.2 422.3,370.7 428.5,371.7"/><polygon points="411.5,380.5 406.2,377.5 411.4,368.3 417.7,369.3"/><polygon points="439.1,395.2 430.9,390.6 432.9,378.1 441.7,379.5"/><polygon points="454.0,390.9 447.1,399.5 444.2,397.9 447.0,380.5 455.5,381.8"/><polygon points="458.1,365.7 456.4,376.4 443.9,374.4 445.7,363.7"/><polygon points="447.0,352.3 460.4,354.5 459.4,360.8 446.0,358.6"/><polygon points="433.7,372.8 437.2,351.2 442.1,351.9 438.6,373.6"/><polygon points="475.9,357.9 461.7,380.2 465.6,356.2"/><polygon points="347.9,442.8 352.8,433.9 366.4,441.4 361.4,450.3"/><polygon points="355.4,462.8 347.4,448.3 359.7,455.2"/><polygon points="367.8,459.5 364.7,454.8 371.2,443.2 375.5,445.6"/><polygon points="389.5,454.1 372.3,465.3 371.1,463.5 379.5,448.5"/><polygon points="368.6,468.5 363.8,471.7 361.2,473.0 357.9,467.0 362.4,459.0"/><polygon points="403.2,444.2 394.4,451.4 390.7,449.3 395.9,440.1"/><polygon points="416.6,431.8 406.3,440.3 399.1,436.3 407.0,422.2"/><polygon points="387.0,446.6 381.7,443.6 390.5,427.7 395.9,430.7"/><polygon points="377.1,440.8 370.9,437.4 379.6,421.8 385.8,425.2"/><polygon points="399.3,413.1 403.8,417.6 398.7,426.9 393.3,423.9"/><polygon points="382.1,417.0 389.4,403.9 389.4,403.9 395.7,410.2 389.5,421.2"/><polygon points="368.2,404.7 369.5,402.3 384.3,403.2 379.9,411.2"/><polygon points="366.1,434.8 355.4,428.8 360.5,419.5 371.3,425.5"/><polygon points="362.6,414.9 366.1,408.7 377.6,415.1 374.1,421.3"/><polygon points="369.4,253.4 360.0,257.5 353.8,243.2 363.3,239.1"/><polygon points="381.9,248.4 374.8,251.5 368.4,236.6 375.6,233.5"/><polygon points="390.0,227.4 392.7,243.7 386.9,246.2 380.6,231.4"/><polygon points="384.4,223.7 375.6,227.5 369.6,213.4 378.4,209.7"/><polygon points="386.7,205.0 387.1,205.7 390.0,222.4 389.2,222.7 382.4,206.9"/><polygon points="381.5,202.6 367.1,208.8 363.8,201.1 370.1,198.4"/><polygon points="370.5,229.8 351.3,238.0 349.0,232.6 368.2,224.4"/><polygon points="347.1,227.1 340.5,211.8 350.2,207.7 356.7,222.9"/><polygon points="354.9,204.8 359.4,202.9 366.5,219.6 362.1,221.5"/><polygon points="338.0,206.3 331.2,190.6 335.0,186.0 345.1,189.7 339.1,205.9"/><polygon points="350.3,191.2 363.3,196.0 345.8,203.5"/><polygon points="297.3,229.4 305.7,219.3 310.8,223.6"/><polygon points="309.6,216.5 318.6,205.7 329.2,214.5 314.7,220.7"/><polygon points="321.4,201.3 327.2,194.4 334.9,212.5"/><polygon points="326.5,251.7 343.1,233.3 347.2,242.8"/><polygon points="338.1,268.0 337.6,268.2 325.7,257.4 332.4,254.5"/><polygon points="354.4,259.8 342.3,265.0 337.4,253.5 349.5,248.3"/><polygon points="319.6,251.1 311.8,243.9 320.9,233.9 325.5,244.6"/><polygon points="307.9,240.4 299.9,233.1 317.3,225.7 318.5,228.7"/><polygon points="339.9,224.7 341.1,227.5 329.3,240.5 325.2,231.0"/><polygon points="321.4,223.3 336.9,216.6 338.5,220.3 323.0,226.9"/><polygon points="225.2,472.9 193.5,450.6 195.8,443.0 212.1,437.1"/><polygon points="243.7,442.8 249.1,454.8 233.6,471.5 225.5,449.4"/><polygon points="218.9,433.7 237.6,426.8 241.5,435.5 222.1,442.6"/><polygon points="354.4,346.0 347.9,358.6 339.0,354.0 345.4,341.4"/><polygon points="334.1,351.9 327.4,348.5 334.2,335.3 340.8,338.7"/><polygon points="323.2,346.8 319.3,344.8 326.5,330.8 330.3,332.8"/><polygon points="338.9,308.7 346.9,316.0 334.8,329.4 329.6,326.7"/><polygon points="356.1,323.8 362.8,329.9 357.0,341.2 349.2,337.2"/><polygon points="351.4,318.9 352.6,320.0 344.8,335.4 339.0,332.5"/><polygon points="325.6,322.2 324.9,323.7 307.8,315.0 318.7,309.2"/><polygon points="332.2,301.6 335.1,304.3 328.5,317.2 322.8,306.6"/><polygon points="301.7,335.5 299.8,334.6 301.1,318.3 302.2,317.6 309.0,321.1"/><polygon points="322.1,328.5 315.3,341.8 306.5,337.4 313.4,324.0"/><polygon points="307.7,354.2 299.1,344.2 299.4,339.5 311.9,345.9"/><polygon points="321.9,369.3 312.0,357.8 316.4,349.2 328.9,355.6"/><polygon points="345.3,363.6 341.4,371.2 327.6,371.4 334.4,358.1"/><polygon points="181.9,491.0 176.4,488.0 182.4,474.3 188.1,476.8"/><polygon points="188.8,494.7 187.3,494.3 185.8,493.4 192.4,478.5 198.4,481.1"/><polygon points="184.6,469.8 190.3,456.6 198.8,462.6 191.6,472.8"/><polygon points="202.9,464.8 207.7,468.2 201.1,477.6 195.6,475.2"/><polygon points="215.1,502.4 213.9,502.2 193.7,496.0 197.8,490.1"/><polygon points="227.5,504.1 225.2,503.9 219.7,500.0 227.8,488.5"/><polygon points="216.3,497.4 212.9,495.0 223.7,479.8 227.0,482.1"/><polygon points="209.0,491.3 201.4,485.9 211.3,471.9 218.9,477.3"/><polygon points="358.7,410.8 351.4,423.8 341.0,418.0 348.3,404.9"/><polygon points="335.6,415.3 327.7,410.9 335.3,397.3 343.2,401.7"/><polygon points="345.0,380.3 356.2,391.9 349.4,398.5 338.3,392.3"/><polygon points="360.4,394.9 365.8,400.4 362.2,406.7 353.3,401.8"/><polygon points="321.7,379.0 323.3,376.9 340.7,376.6 335.2,386.5"/><polygon points="305.7,399.4 308.5,395.7 323.9,407.4 323.0,409.0"/><polygon points="312.6,392.0 318.8,383.9 331.8,391.1 325.7,402.1"/><polygon points="306.2,425.3 300.2,407.9 303.3,403.9 314.6,410.3"/><polygon points="319.0,438.5 308.9,436.6 307.6,432.8 309.4,429.6 320.5,435.9"/><polygon points="312.6,426.0 319.8,413.1 329.1,418.3 321.9,431.1"/><polygon points="339.1,442.4 322.8,439.3 325.4,434.7"/><polygon points="329.0,431.0 334.3,421.4 348.4,429.3 343.0,438.9"/><polygon points="363.6,270.7 367.0,278.8 353.7,284.5 350.3,276.5"/><polygon points="346.2,268.8 360.8,262.6 362.4,266.2 347.8,272.4"/><polygon points="339.8,272.5 342.1,271.4 348.6,286.6 336.5,291.8"/><polygon points="362.7,309.9 352.8,314.2 349.2,310.9 358.6,300.5"/><polygon points="345.7,306.9 336.2,298.2 353.7,290.7 355.9,295.7"/><polygon points="373.7,294.0 377.7,303.2 367.0,307.8 363.1,298.5"/><polygon points="369.4,283.1 372.1,289.5 360.9,294.3 358.1,288.0"/><polygon points="369.0,326.7 366.7,327.1 356.1,317.5 363.6,314.3"/><polygon points="379.4,308.6 385.5,322.8 373.7,324.9 368.7,313.2"/><polygon points="408.1,271.6 405.3,284.6 386.8,280.7"/><polygon points="401.0,306.8 398.0,320.9 391.4,322.1 387.3,312.6"/><polygon points="386.8,306.6 385.5,307.1 376.2,285.4 377.3,285.0 390.8,287.8"/><polygon points="404.6,290.2 402.4,300.5 393.1,304.5 396.5,288.4"/><polygon points="407.3,259.5 410.5,262.5 410.0,264.8 393.8,271.7 391.5,266.3"/><polygon points="385.7,252.6 395.3,248.5 403.0,255.7 389.5,261.5"/><polygon points="365.2,261.3 380.9,254.6 383.4,260.5 367.8,267.2"/><polygon points="385.3,265.6 388.7,273.5 373.6,280.0 370.2,272.1"/><polygon points="188.2,440.2 186.1,447.2 157.1,445.9 157.5,437.9 187.7,439.3"/><polygon points="158.0,430.0 158.6,416.8 176.0,417.6 183.2,431.1"/><polygon points="157.9,409.7 158.5,397.1 165.7,395.8 173.4,410.4"/><polygon points="120.6,411.8 119.4,405.5 151.2,399.7 150.5,413.1"/><polygon points="127.6,436.6 120.2,418.3 128.4,418.7"/><polygon points="149.1,445.2 133.8,444.5 134.9,420.8 150.2,421.5"/><polygon points="267.0,404.1 275.8,405.6 274.5,412.9 265.7,411.4"/><polygon points="254.6,401.9 263.2,403.4 261.9,410.7 253.3,409.2"/><polygon points="245.9,408.5 248.8,400.3 251.5,400.8 250.1,409.2"/><polygon points="244.2,414.6 245.3,411.6 259.9,414.1 259.4,417.3"/><polygon points="244.3,429.7 241.1,422.6 242.8,417.8 246.9,418.5 244.9,429.8"/><polygon points="250.4,419.7 258.3,421.1 256.6,431.3 248.6,429.9"/><polygon points="263.6,414.9 273.8,416.7 272.6,423.3 262.5,421.5"/><polygon points="271.9,427.2 270.7,433.9 260.6,432.2 261.8,425.4"/><polygon points="256.2,454.3 255.4,454.1 250.6,443.1 258.8,439.5"/><polygon points="248.9,439.9 245.9,433.3 258.9,435.5"/><polygon points="261.9,442.6 263.0,436.3 270.4,437.6 269.3,443.9"/><polygon points="266.7,457.3 259.9,455.3 261.5,446.2 268.4,447.4"/><polygon points="303.9,434.3 305.1,437.8 298.6,445.6 292.7,440.6 298.8,433.4"/><polygon points="289.5,438.7 284.1,434.1 286.9,430.8 294.9,432.2"/><polygon points="281.7,432.9 276.2,428.3 284.4,429.7"/><polygon points="273.2,440.6 273.3,439.7 284.9,441.8 280.7,446.9"/><polygon points="270.6,458.6 270.2,458.5 272.7,444.5 278.4,449.2"/><polygon points="273.8,436.7 274.9,430.6 284.5,438.6"/><polygon points="285.3,462.5 284.9,463.0 273.7,459.8 277.2,455.7"/><polygon points="296.3,448.8 292.4,453.5 284.8,447.2 288.7,442.5"/><polygon points="290.2,456.3 287.2,460.0 279.3,453.5 282.4,449.8"/><polygon points="295.7,408.7 297.8,415.0 290.6,417.5 292.2,408.1"/><polygon points="298.5,418.8 302.2,429.6 289.5,427.3 290.5,421.5"/><polygon points="278.4,412.3 279.5,406.2 288.8,407.8 287.8,413.9"/><polygon points="285.2,426.7 276.4,425.2 278.0,416.2 286.8,417.7"/><polygon points="487.4,174.6 489.6,178.4 475.1,181.9 474.1,177.9"/><polygon points="479.2,163.9 483.1,168.0 484.9,171.0 473.3,173.8 471.4,165.8"/><polygon points="470.6,154.1 476.9,160.7 469.9,162.4 468.0,154.7"/><polygon points="472.0,183.3 469.6,183.9 466.9,172.6 469.2,172.0"/><polygon points="466.4,184.1 460.8,185.5 458.3,175.2 463.9,173.8"/><polygon points="457.4,171.6 456.2,166.5 466.8,163.9 468.0,169.0"/><polygon points="455.2,163.0 454.1,158.3 464.9,155.7 466.0,160.4"/><polygon points="447.9,188.6 439.7,190.6 437.8,182.9 446.1,180.9"/><polygon points="457.4,186.5 451.5,187.9 449.5,179.9 455.5,178.4"/><polygon points="441.2,161.4 450.7,159.1 451.9,164.0 442.4,166.3"/><polygon points="452.5,167.6 454.3,174.9 445.3,177.1 443.5,169.8"/><polygon points="437.0,179.0 433.3,163.8 437.6,162.7 441.4,178.0"/><polygon points="455.8,143.0 465.3,149.2 467.4,151.4 458.4,153.6"/><polygon points="453.3,148.3 454.9,154.5 446.3,156.6 444.8,150.4"/><polygon points="447.4,137.6 451.2,140.0 452.4,144.8 444.0,146.8 442.1,138.9"/><polygon points="442.5,157.2 432.3,159.7 430.6,152.7 440.8,150.2"/><polygon points="429.6,148.7 428.0,142.4 438.3,139.9 439.9,146.2"/><polygon points="423.7,126.9 429.6,129.1 426.3,137.8"/><polygon points="432.5,130.6 443.2,134.6 443.4,134.8 429.6,138.1"/><polygon points="408.2,193.4 406.2,185.2 411.7,183.8"/><polygon points="406.1,181.8 403.4,170.9 414.2,174.8 412.2,180.3"/><polygon points="433.5,181.9 435.8,191.4 430.1,192.8 426.8,179.5"/><polygon points="426.3,193.9 419.2,195.6 416.6,185.2 424.9,188.2"/><polygon points="415.9,196.8 409.6,198.3 409.5,198.0 413.5,187.0"/><polygon points="415.7,181.2 417.8,175.6 422.6,177.4 424.3,184.3"/><polygon points="406.5,198.9 401.0,200.3 398.6,190.3 405.0,192.7"/><polygon points="397.4,200.9 389.7,202.7 389.0,201.7 393.8,188.8 394.6,189.1"/><polygon points="395.2,185.2 399.7,172.8 403.5,188.2"/><polygon points="419.9,154.6 405.0,158.2 408.0,150.2"/><polygon points="417.0,124.7 417.4,124.7 420.3,125.8 421.8,132.3 413.5,134.3"/><polygon points="422.4,135.7 423.5,140.3 410.3,143.6 412.3,138.2"/><polygon points="424.3,144.1 426.4,152.8 411.4,147.3"/><polygon points="428.0,159.9 432.1,176.9 423.0,173.5"/><polygon points="412.7,170.2 402.0,166.2 403.3,162.5 410.5,160.8"/><polygon points="418.9,172.1 417.1,171.5 414.4,160.1 424.2,157.7"/><polygon points="268.3,473.7 264.9,485.4 259.7,486.7 257.2,476.4"/><polygon points="253.7,460.1 254.9,458.8 270.7,463.3 269.3,468.3 256.5,471.4"/><polygon points="276.2,465.2 282.9,467.1 285.9,479.5 271.1,483.1"/><polygon points="238.6,475.3 249.6,463.5 251.7,472.1"/><polygon points="240.3,479.8 252.2,476.9 254.7,487.4 242.9,490.3"/><polygon points="231.8,493.8 232.0,482.3 233.5,480.7 235.6,480.1 238.5,492.1"/><polygon points="239.6,505.8 231.4,504.9 231.5,497.7 237.3,496.3"/><polygon points="259.4,504.0 244.2,504.9 242.1,496.3 256.6,492.8"/><polygon points="278.6,501.8 272.8,503.0 265.5,503.5 262.5,491.5 275.4,488.4"/><polygon points="291.3,499.7 284.3,501.2 280.7,486.5 287.7,484.8"/><polygon points="416.4,266.9 416.7,265.8 437.0,265.7 450.0,276.7 440.8,287.5"/><polygon points="412.3,283.5 414.1,275.2 436.2,293.9 431.4,299.6"/><polygon points="408.4,325.8 404.8,321.9 411.0,292.6 425.9,305.2"/><polygon points="422.2,340.5 414.2,332.0 430.8,312.4 439.6,319.9"/><polygon points="440.3,345.4 429.7,343.6 445.9,324.4 453.0,330.3"/><polygon points="477.8,351.4 448.6,346.6 458.4,335.0"/><polygon points="486.4,337.1 482.0,346.1 467.7,334.1 474.0,326.6"/><polygon points="495.5,314.0 490.8,327.6 489.1,331.0 478.3,321.9 489.3,308.8"/><polygon points="449.5,289.3 456.3,281.2 472.4,294.9 465.6,302.9"/><polygon points="451.4,319.1 435.9,306.0 444.9,295.3 460.4,308.4"/><polygon points="462.9,328.9 457.4,324.3 478.0,299.9 483.4,304.6"/><polygon points="418.4,346.3 409.7,362.0 404.9,359.4 414.5,342.2"/><polygon points="400.6,357.1 386.3,349.2 389.0,344.3 403.3,352.3"/><polygon points="407.9,334.3 411.6,338.2 405.7,348.7 401.2,346.2"/><polygon points="391.5,340.2 399.4,326.0 404.4,331.3 397.6,343.6"/><polygon points="382.0,346.0 365.5,336.9 368.0,332.0 379.8,329.9 382.5,345.1"/><polygon points="384.5,328.0 394.1,326.3 386.6,339.8"/><polygon points="350.0,366.0 356.5,353.2 362.4,356.2 355.8,369.2"/><polygon points="366.8,359.1 376.6,364.0 369.7,376.4 360.6,371.3"/><polygon points="359.6,348.7 362.9,342.3 382.4,353.2 379.3,358.8"/><polygon points="362.2,389.6 346.7,373.5 348.1,370.9 366.8,381.4"/><polygon points="376.6,398.8 369.1,398.3 365.1,394.2 367.0,390.7 377.8,396.7"/><polygon points="370.2,387.2 377.0,374.9 386.0,379.9 379.1,392.2"/><polygon points="396.5,385.5 389.1,398.7 381.9,398.3 390.8,382.3"/><polygon points="406.4,366.7 398.8,380.3 389.1,374.9 396.7,361.2"/><polygon points="383.9,372.8 379.2,370.2 387.5,355.3 392.2,357.9"/><polygon points="293.7,190.2 288.3,185.7 297.7,174.5 303.0,178.9"/><polygon points="300.3,170.8 306.6,163.3 312.5,168.2 306.2,175.7"/><polygon points="313.5,186.1 307.5,181.1 315.7,171.3 321.7,176.3"/><polygon points="330.4,183.4 322.1,193.5 316.9,189.2 325.3,179.2"/><polygon points="318.0,197.0 311.8,204.4 298.3,193.2 304.5,185.8"/><polygon points="319.7,148.2 323.6,159.5 313.3,163.0 309.7,160.1"/><polygon points="325.4,163.7 330.1,177.4 317.1,166.6"/><polygon points="312.1,149.5 305.1,157.8 296.9,151.0 303.9,142.6"/><polygon points="313.5,130.6 313.8,130.5 318.1,142.8 315.4,146.0 306.7,138.8"/><polygon points="285.5,141.4 295.0,136.9 304.6,133.9 293.0,147.7"/><polygon points="292.2,153.4 302.2,161.7 297.5,167.3 287.5,159.0"/><polygon points="293.6,171.0 284.5,181.8 275.6,174.4 284.7,163.5"/><polygon points="271.2,148.1 280.4,143.8 288.3,150.4 282.4,157.5"/><polygon points="270.9,154.7 279.0,161.5 271.0,171.0 262.9,164.2"/><polygon points="253.6,157.4 263.8,151.0 266.2,149.9 267.7,151.2 258.9,161.8"/><polygon points="259.2,168.2 268.0,175.6 260.2,185.0 251.4,177.6"/><polygon points="238.7,167.4 250.1,160.3 255.3,164.6 247.2,174.4"/><polygon points="250.1,183.9 256.6,189.2 247.2,200.5 238.6,193.3"/><polygon points="244.1,204.8 238.3,211.8 229.1,200.6 234.1,196.5"/><polygon points="230.4,173.1 234.3,170.1 246.3,180.2 240.3,185.1"/><polygon points="217.0,184.8 226.6,177.1 235.6,188.1 226.1,195.9"/><polygon points="250.0,218.8 242.3,216.4 241.7,215.6 251.8,203.5 258.3,208.8"/><polygon points="260.9,222.8 254.3,220.8 262.2,211.3 267.1,215.4"/><polygon points="283.8,229.6 265.4,224.0 270.2,218.3"/><polygon points="272.2,177.9 279.5,184.0 275.1,189.3 267.8,183.2"/><polygon points="271.6,192.4 261.4,204.5 255.3,199.4 265.4,187.3"/><polygon points="282.5,187.2 294.1,196.8 289.7,202.0 278.2,192.4"/><polygon points="287.2,205.7 284.6,208.8 272.5,198.7 275.1,195.6"/><polygon points="281.3,212.0 276.7,217.5 265.3,208.0 269.9,202.5"/><polygon points="295.9,224.7 292.4,228.8 290.4,229.2 280.4,220.8 284.8,215.4"/><polygon points="297.9,201.0 307.8,209.2 298.8,219.9 288.9,211.7"/><polygon points="251.8,364.9 258.2,362.4 262.9,374.5 256.5,377.0"/><polygon points="243.3,382.0 240.2,374.7 242.9,368.6 247.5,366.8 252.1,378.5"/><polygon points="257.8,397.7 249.8,396.3 245.7,386.6 255.5,382.8 260.9,396.5"/><polygon points="259.9,380.5 264.6,378.7 270.3,393.4 265.7,395.2"/><polygon points="274.5,402.0 263.4,400.0 272.4,396.5"/><polygon points="274.7,392.9 272.7,387.7 284.5,383.1 286.5,388.3"/><polygon points="292.2,404.1 278.6,401.7 276.7,396.9 287.8,392.6"/><polygon points="299.9,399.4 297.1,403.0 288.8,381.9 292.5,380.5"/><polygon points="316.3,370.4 319.8,374.5 316.0,379.4 308.3,373.5"/><polygon points="312.4,382.4 303.4,394.2 297.5,378.9 304.3,376.2"/><polygon points="296.1,347.4 299.4,351.2 290.3,359.0 287.1,350.9"/><polygon points="301.9,355.7 311.7,367.0 296.8,372.8 293.1,363.3"/><polygon points="292.0,375.4 284.3,378.4 279.7,366.5 287.3,363.5"/><polygon points="279.7,380.1 271.5,383.3 266.9,371.6 275.1,368.4"/><polygon points="265.2,366.5 263.1,361.1 282.9,353.4 285.0,358.8"/><polygon points="209.8,350.4 215.2,349.1 218.0,361.3 212.6,362.6"/><polygon points="201.0,365.2 200.7,352.6 205.8,351.5 208.5,363.4"/><polygon points="220.9,373.1 211.3,373.3 211.2,366.9 219.0,365.1"/><polygon points="207.9,373.7 200.5,373.9 200.4,369.0 207.8,367.3"/><polygon points="179.2,358.5 178.9,357.5 196.7,353.4 196.8,358.2"/><polygon points="185.3,374.0 182.5,374.0 179.8,362.2 185.1,362.1"/><polygon points="196.8,373.3 189.2,373.4 189.0,362.4 196.6,362.3"/><polygon points="233.7,372.6 225.0,372.8 223.0,364.1 231.2,362.2"/><polygon points="238.4,359.8 239.6,366.5 237.2,371.8 234.6,360.7"/><polygon points="228.1,358.6 222.0,360.0 219.2,348.3 225.4,346.9"/><polygon points="235.2,344.5 237.3,356.5 232.1,357.7 229.3,345.8"/><polygon points="222.0,343.8 214.4,345.6 213.0,339.5 220.6,337.7"/><polygon points="212.2,336.3 210.9,330.7 218.6,328.9 219.9,334.5"/><polygon points="232.0,326.1 233.2,332.6 223.4,334.3 222.1,328.4"/><polygon points="234.0,336.1 234.8,340.8 225.3,343.0 224.1,337.8"/><polygon points="229.7,314.7 231.0,322.1 220.5,324.6 218.8,317.2"/><polygon points="228.9,308.1 229.2,308.3 229.7,310.9 217.3,313.8 216.7,310.9"/><polygon points="216.3,325.6 210.6,326.9 207.5,313.7 213.2,312.4"/><polygon points="218.0,299.9 226.2,305.8 219.7,307.3"/><polygon points="216.5,305.5 217.1,307.8 205.9,310.4 205.4,308.1"/><polygon points="203.8,299.2 214.4,298.1 215.4,302.5 205.1,304.9"/><polygon points="206.5,327.2 207.9,333.1 198.1,335.4 196.7,329.5"/><polygon points="205.6,321.3 206.3,324.0 195.5,326.5 194.9,323.8"/><polygon points="194.6,336.3 190.3,337.3 187.7,325.9 191.9,324.9"/><polygon points="208.7,336.7 210.9,346.2 204.8,347.6 202.6,338.1"/><polygon points="201.0,348.3 193.6,350.1 191.4,340.9 198.9,339.1"/><polygon points="189.5,350.9 178.2,353.5 176.6,347.0 188.0,344.4"/><polygon points="175.7,342.9 174.1,336.0 185.4,333.4 187.0,340.2"/><polygon points="172.7,332.4 172.0,329.4 184.4,326.5 185.1,329.6"/><polygon points="169.5,318.0 168.1,312.1 176.7,310.1 178.0,316.0"/><polygon points="180.1,324.2 171.3,326.2 170.2,321.3 178.9,319.2"/><polygon points="185.4,308.3 186.9,322.3 183.5,323.1 180.3,309.5"/><polygon points="166.5,303.5 184.5,301.6 184.8,304.2 167.6,308.2"/><polygon points="203.2,313.9 204.2,318.1 190.8,321.3 190.1,315.3"/><polygon points="198.8,299.3 200.5,299.1 203.1,310.4 200.0,310.8"/><polygon points="188.6,301.0 195.5,300.3 196.5,310.5 189.7,311.2"/><polygon points="403.1,124.1 411.4,125.4 403.6,146.8 395.8,144.0"/><polygon points="384.4,121.9 388.5,121.8 396.2,123.1 389.5,141.5 378.6,137.5"/><polygon points="400.7,153.3 393.9,171.9 381.7,167.4 388.5,148.9"/><polygon points="375.1,165.8 368.6,163.5 376.0,143.4 382.4,145.8"/><polygon points="352.7,122.7 358.2,121.7 377.7,121.5 374.3,130.6"/><polygon points="352.1,129.5 371.5,136.6 367.8,146.8 348.4,139.7"/><polygon points="365.7,153.3 362.9,161.1 342.9,153.8 345.7,145.9"/><polygon points="320.6,130.4 327.0,128.3 340.7,126.0 344.6,127.4 336.2,150.3 326.2,146.6"/><polygon points="336.4,179.3 328.1,155.2 343.2,160.8"/><polygon points="391.0,179.1 384.5,196.9 370.1,191.5 376.6,173.8"/><polygon points="362.4,188.5 343.5,181.5 349.8,164.2 368.8,171.2"/><polygon points="263.4,355.5 256.2,358.3 252.0,347.7 261.7,346.0"/><polygon points="251.7,359.9 243.0,363.3 240.6,349.9 247.3,348.7"/><polygon points="240.2,344.4 238.9,337.2 258.8,333.7 260.1,340.9"/><polygon points="286.2,345.8 268.3,352.8 266.5,342.9 285.1,339.6"/><polygon points="265.1,337.5 264.2,332.3 283.7,328.9 284.6,334.0"/><polygon points="295.8,326.5 294.5,343.3 291.4,344.5 288.5,327.8"/><polygon points="275.6,304.4 288.1,312.9 280.6,324.0 279.1,324.3"/><polygon points="292.4,314.6 297.0,317.7 296.7,322.1 286.1,324.0"/><polygon points="248.2,317.2 250.3,329.2 237.8,331.4 235.7,319.4"/><polygon points="233.1,309.1 247.1,306.7 248.1,312.1 234.1,314.6"/><polygon points="271.7,315.8 273.3,325.0 256.1,328.0 254.5,318.8"/><polygon points="252.0,306.4 269.8,303.3 271.0,310.0 253.2,313.2"/><polygon points="161.2,308.4 165.4,326.7 147.4,330.8 143.2,312.6"/><polygon points="137.0,291.2 157.8,301.8 140.4,305.9"/><polygon points="126.6,308.7 129.0,301.1 132.4,293.9 135.4,306.7"/><polygon points="120.5,337.3 121.4,331.8 126.7,315.0 134.8,313.1 139.4,332.8"/><polygon points="116.9,354.1 118.3,345.3 137.7,340.8 139.6,348.8"/><polygon points="124.5,359.6 140.6,355.8 144.3,371.4 128.2,375.1"/><polygon points="114.9,379.6 114.9,361.3 115.1,360.4 118.1,359.7 122.3,377.9"/><polygon points="138.8,379.4 146.8,377.5 150.3,392.7 141.6,394.4"/><polygon points="116.0,390.2 116.0,385.6 132.8,381.7 135.2,394.7 117.4,397.9"/><polygon points="174.8,378.9 165.8,389.7 155.9,391.6 154.0,383.7"/><polygon points="172.5,356.9 175.8,371.3 153.7,376.5 150.3,362.0"/><polygon points="145.2,340.0 167.3,334.8 170.6,348.7 148.4,353.8"/><polygon points="503.7,278.6 499.8,298.8 497.4,305.7 475.4,287.2 491.9,267.6"/><polygon points="469.0,281.7 459.8,274.0 477.0,253.7 485.8,261.8"/><polygon points="453.4,268.6 444.6,261.2 463.5,241.0 470.9,247.9"/><polygon points="504.4,226.0 506.5,242.2 505.5,268.7 483.5,248.3"/><polygon points="498.8,203.2 502.9,215.3 477.4,242.5 469.2,234.9"/><polygon points="296.6,265.2 313.2,268.1 311.7,276.4 295.2,273.6"/><polygon points="310.8,282.1 309.4,290.1 292.8,287.3 294.2,279.2"/><polygon points="279.5,284.6 283.2,263.2 290.7,264.5 287.0,285.9"/><polygon points="289.8,292.2 294.9,293.1 292.1,309.2 282.7,302.8"/><polygon points="275.7,299.0 277.4,289.4 285.4,290.8 278.6,300.9"/><polygon points="300.2,294.3 308.4,295.7 306.0,309.8 299.0,313.5 297.1,312.2"/><polygon points="325.6,300.1 311.3,307.8 313.0,298.0"/><polygon points="331.6,286.2 330.0,295.8 314.7,293.2 316.4,283.6"/><polygon points="318.8,269.0 334.2,271.6 332.7,280.6 317.3,277.9"/><polygon points="284.2,257.0 286.1,245.9 304.0,249.0 295.0,258.9"/><polygon points="286.3,240.4 287.3,234.5 292.9,233.6 303.7,243.4"/><polygon points="310.8,250.6 325.8,264.2 302.1,260.2"/><polygon points="189.9,378.6 203.7,378.3 203.9,389.6 190.1,389.9"/><polygon points="204.4,395.1 204.6,402.6 189.9,402.8 189.7,395.4"/><polygon points="174.8,386.3 182.1,377.6 185.0,377.6 185.2,386.2"/><polygon points="175.9,402.9 170.6,392.8 172.1,391.0 184.4,390.8 184.6,402.7"/><polygon points="205.8,433.0 193.2,437.6 192.0,435.2 205.1,428.3 205.8,429.6"/><polygon points="190.7,430.6 185.3,420.3 196.5,414.4 202.0,424.7"/><polygon points="200.0,411.2 205.5,408.3 205.8,422.1"/><polygon points="182.4,415.8 178.3,408.0 198.0,407.6"/><polygon points="225.3,424.5 210.6,429.9 210.4,416.2 225.1,415.9"/><polygon points="240.2,414.5 237.9,421.1 230.3,423.9 230.1,414.7"/><polygon points="227.3,409.8 210.2,410.1 210.1,400.9 227.1,400.6"/><polygon points="244.9,399.8 241.3,410.1 232.8,410.2 232.6,400.0"/><polygon points="235.9,377.3 243.5,394.8 234.4,395.0 234.1,377.3"/><polygon points="229.5,395.2 224.5,395.3 224.2,377.4 229.2,377.3"/><polygon points="219.2,394.8 209.9,395.0 209.6,378.2 218.9,378.0"/><polygon points="153.9,470.6 145.5,463.0 137.9,452.1 154.7,452.9"/><polygon points="183.5,455.1 171.4,483.1 165.2,479.6 161.1,475.9 162.1,454.2"/></g><g fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M192.3,251.5 L227.9,257.4 L251.7,265.7" stroke="#6c604a" stroke-width="3.4"/><path d="M251.7,265.7 L279.5,266.1 L303.2,277.0" stroke="#6c604a" stroke-width="3.4"/><path d="M251.7,265.7 L252.1,303.6 L263.7,330.1" stroke="#6c604a" stroke-width="3.4"/><path d="M263.7,330.1 L268.9,355.8 L280.0,377.8" stroke="#6c604a" stroke-width="3.4"/><path d="M280.0,377.8 L272.4,403.1 L272.9,430.3" stroke="#6c604a" stroke-width="3.4"/><path d="M272.9,430.3 L246.7,439.1 L223.7,450.8" stroke="#6c604a" stroke-width="3.4"/><path d="M223.7,450.8 L209.6,467.1 L203.0,483.2" stroke="#6c604a" stroke-width="3.4"/><path d="M203.0,483.2 L181.3,470.8 L163.3,463.8" stroke="#6c604a" stroke-width="3.4"/><path d="M163.3,463.8 L160.2,449.9 L153.0,423.9" stroke="#6c604a" stroke-width="3.4"/><path d="M223.7,450.8 L215.9,431.2 L208.6,402.5" stroke="#6c604a" stroke-width="3.4"/><path d="M223.7,450.8 L242.0,468.6 L260.9,485.1" stroke="#6c604a" stroke-width="3.4"/><path d="M280.0,377.8 L309.5,390.9 L331.5,409.0" stroke="#6c604a" stroke-width="3.4"/><path d="M331.5,409.0 L355.7,422.6 L378.3,435.1" stroke="#6c604a" stroke-width="3.4"/><path d="M263.7,330.1 L235.9,337.0 L203.8,336.8" stroke="#6c604a" stroke-width="3.4"/><path d="M272.9,430.3 L296.5,451.5 L319.3,468.9" stroke="#6c604a" stroke-width="3.4"/><path d="M203.8,336.8 L172.5,338.7 L143.9,349.9" stroke="#6c604a" stroke-width="3.4"/><path d="M280.0,377.8 L309.4,359.7 L329.2,337.4" stroke="#6c604a" stroke-width="3.4"/><path d="M329.2,337.4 L354.6,351.7 L382.1,361.5" stroke="#6c604a" stroke-width="3.4"/><path d="M382.1,361.5 L406.1,373.6 L432.1,383.6" stroke="#6c604a" stroke-width="3.4"/><path d="M303.2,277.0 L315.4,250.8 L347.7,226.8" stroke="#6c604a" stroke-width="3.4"/><path d="M329.2,337.4 L349.3,314.1 L375.1,288.2" stroke="#6c604a" stroke-width="3.4"/><path d="M432.1,383.6 L451.2,350.7 L446.2,311.2" stroke="#6c604a" stroke-width="3.4"/><path d="M446.2,311.2 L469.0,287.2 L484.1,255.9" stroke="#6c604a" stroke-width="3.4"/><path d="M484.1,255.9 L469.5,228.4 L439.5,219.8" stroke="#6c604a" stroke-width="3.4"/><path d="M439.5,219.8 L440.8,192.3 L434.9,166.6" stroke="#6c604a" stroke-width="3.4"/><path d="M434.9,166.6 L401.4,162.4 L366.0,153.1" stroke="#6c604a" stroke-width="3.4"/><path d="M347.7,226.8 L313.9,206.8 L279.1,182.6" stroke="#6c604a" stroke-width="3.4"/><path d="M192.3,251.5 L148.8,293.9 L143.9,349.9" stroke="#6c604a" stroke-width="3.4"/><path d="M319.3,468.9 L289.9,483.2 L260.9,485.1" stroke="#6c604a" stroke-width="3.4"/><path d="M439.5,219.8 L392.4,225.1 L347.7,226.8" stroke="#6c604a" stroke-width="3.4"/><path d="M439.5,219.8 L404.5,253.5 L375.1,288.2" stroke="#6c604a" stroke-width="3.4"/><path d="M439.5,219.8 L425.9,261.6 L446.2,311.2" stroke="#6c604a" stroke-width="3.4"/><path d="M251.7,265.7 L263.0,225.5 L279.1,182.6" stroke="#6c604a" stroke-width="3.4"/><path d="M347.7,226.8 L366.5,257.9 L375.1,288.2" stroke="#6c604a" stroke-width="3.4"/><path d="M347.7,226.8 L387.9,203.5 L434.9,166.6" stroke="#6c604a" stroke-width="3.4"/><path d="M329.2,337.4 L315.7,307.8 L303.2,277.0" stroke="#6c604a" stroke-width="3.4"/><path d="M203.0,483.2 L229.8,494.0 L260.9,485.1" stroke="#6c604a" stroke-width="3.4"/><path d="M329.2,337.4 L127.1,300.4" stroke="#6c604a" stroke-width="4.8"/><path d="M329.2,337.4 L466.6,147.7" stroke="#6c604a" stroke-width="4.8"/><path d="M329.2,337.4 L364.9,473.5" stroke="#6c604a" stroke-width="4.8"/><path d="M192.3,251.5 L227.9,257.4 L251.7,265.7" stroke="#e4d9bc" stroke-width="1.8"/><path d="M251.7,265.7 L279.5,266.1 L303.2,277.0" stroke="#e4d9bc" stroke-width="1.8"/><path d="M251.7,265.7 L252.1,303.6 L263.7,330.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M263.7,330.1 L268.9,355.8 L280.0,377.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M280.0,377.8 L272.4,403.1 L272.9,430.3" stroke="#e4d9bc" stroke-width="1.8"/><path d="M272.9,430.3 L246.7,439.1 L223.7,450.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M223.7,450.8 L209.6,467.1 L203.0,483.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M203.0,483.2 L181.3,470.8 L163.3,463.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M163.3,463.8 L160.2,449.9 L153.0,423.9" stroke="#e4d9bc" stroke-width="1.8"/><path d="M223.7,450.8 L215.9,431.2 L208.6,402.5" stroke="#e4d9bc" stroke-width="1.8"/><path d="M223.7,450.8 L242.0,468.6 L260.9,485.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M280.0,377.8 L309.5,390.9 L331.5,409.0" stroke="#e4d9bc" stroke-width="1.8"/><path d="M331.5,409.0 L355.7,422.6 L378.3,435.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M263.7,330.1 L235.9,337.0 L203.8,336.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M272.9,430.3 L296.5,451.5 L319.3,468.9" stroke="#e4d9bc" stroke-width="1.8"/><path d="M203.8,336.8 L172.5,338.7 L143.9,349.9" stroke="#e4d9bc" stroke-width="1.8"/><path d="M280.0,377.8 L309.4,359.7 L329.2,337.4" stroke="#e4d9bc" stroke-width="1.8"/><path d="M329.2,337.4 L354.6,351.7 L382.1,361.5" stroke="#e4d9bc" stroke-width="1.8"/><path d="M382.1,361.5 L406.1,373.6 L432.1,383.6" stroke="#e4d9bc" stroke-width="1.8"/><path d="M303.2,277.0 L315.4,250.8 L347.7,226.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M329.2,337.4 L349.3,314.1 L375.1,288.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M432.1,383.6 L451.2,350.7 L446.2,311.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M446.2,311.2 L469.0,287.2 L484.1,255.9" stroke="#e4d9bc" stroke-width="1.8"/><path d="M484.1,255.9 L469.5,228.4 L439.5,219.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M439.5,219.8 L440.8,192.3 L434.9,166.6" stroke="#e4d9bc" stroke-width="1.8"/><path d="M434.9,166.6 L401.4,162.4 L366.0,153.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M347.7,226.8 L313.9,206.8 L279.1,182.6" stroke="#e4d9bc" stroke-width="1.8"/><path d="M192.3,251.5 L148.8,293.9 L143.9,349.9" stroke="#e4d9bc" stroke-width="1.8"/><path d="M319.3,468.9 L289.9,483.2 L260.9,485.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M439.5,219.8 L392.4,225.1 L347.7,226.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M439.5,219.8 L404.5,253.5 L375.1,288.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M439.5,219.8 L425.9,261.6 L446.2,311.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M251.7,265.7 L263.0,225.5 L279.1,182.6" stroke="#e4d9bc" stroke-width="1.8"/><path d="M347.7,226.8 L366.5,257.9 L375.1,288.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M347.7,226.8 L387.9,203.5 L434.9,166.6" stroke="#e4d9bc" stroke-width="1.8"/><path d="M329.2,337.4 L315.7,307.8 L303.2,277.0" stroke="#e4d9bc" stroke-width="1.8"/><path d="M203.0,483.2 L229.8,494.0 L260.9,485.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M329.2,337.4 L127.1,300.4" stroke="#e4d9bc" stroke-width="3.2"/><path d="M329.2,337.4 L466.6,147.7" stroke="#e4d9bc" stroke-width="3.2"/><path d="M329.2,337.4 L364.9,473.5" stroke="#e4d9bc" stroke-width="3.2"/></g><path d="M112.4,390.5 L112.4,361.1 M112.4,361.1 L117.4,330.8 M117.4,330.8 L124.3,309.1 M131.0,292.2 L141.2,270.5 M141.2,270.5 L159.2,241.7 M159.2,241.7 L180.9,214.6 M180.9,214.6 L205.7,189.8 M205.7,189.8 L233.2,167.8 M233.2,167.8 L262.9,149.2 M262.9,149.2 L294.0,134.5 M294.0,134.5 L326.0,124.2 M326.0,124.2 L357.9,118.7 M357.9,118.7 L388.8,118.3 M388.8,118.3 L417.8,123.1 M417.8,123.1 L444.0,133.0 M444.0,133.0 L458.9,142.8 M472.9,154.3 L484.9,166.7 M484.9,166.7 L498.5,189.2 M498.5,189.2 L507.1,214.6 M507.1,214.6 L510.8,242.1 M510.8,242.1 L509.6,270.7 M509.6,270.7 L503.8,299.9 M503.8,299.9 L493.9,328.9 M493.9,328.9 L480.0,357.3 M480.0,357.3 L462.7,384.5 M462.7,384.5 L442.2,410.2 M442.2,410.2 L418.8,433.9 M418.8,433.9 L392.9,455.1 M392.9,455.1 L372.5,468.5 M356.7,477.6 L335.1,488.5 M335.1,488.5 L304.4,499.5 M304.4,499.5 L273.2,506.1 M273.2,506.1 L242.6,507.9 M242.6,507.9 L213.4,504.7 M213.4,504.7 L186.5,496.3 M186.5,496.3 L162.7,483.1 M162.7,483.1 L143.0,465.2 M143.0,465.2 L127.8,443.3 M127.8,443.3 L117.5,418.2 M117.5,418.2 L112.4,390.5" fill="none" stroke="#3c3628" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><g fill="#3c3628" stroke="#1c1810" stroke-width="0.6"><circle cx="112.4" cy="390.5" r="2.6"/><circle cx="112.4" cy="361.1" r="2.6"/><circle cx="117.4" cy="330.8" r="2.6"/><circle cx="127.1" cy="300.4" r="2.6"/><circle cx="141.2" cy="270.5" r="2.6"/><circle cx="159.2" cy="241.7" r="2.6"/><circle cx="180.9" cy="214.6" r="2.6"/><circle cx="205.7" cy="189.8" r="2.6"/><circle cx="233.2" cy="167.8" r="2.6"/><circle cx="262.9" cy="149.2" r="2.6"/><circle cx="294.0" cy="134.5" r="2.6"/><circle cx="326.0" cy="124.2" r="2.6"/><circle cx="357.9" cy="118.7" r="2.6"/><circle cx="388.8" cy="118.3" r="2.6"/><circle cx="417.8" cy="123.1" r="2.6"/><circle cx="444.0" cy="133.0" r="2.6"/><circle cx="466.6" cy="147.7" r="2.6"/><circle cx="484.9" cy="166.7" r="2.6"/><circle cx="498.5" cy="189.2" r="2.6"/><circle cx="507.1" cy="214.6" r="2.6"/><circle cx="510.8" cy="242.1" r="2.6"/><circle cx="509.6" cy="270.7" r="2.6"/><circle cx="503.8" cy="299.9" r="2.6"/><circle cx="493.9" cy="328.9" r="2.6"/><circle cx="480.0" cy="357.3" r="2.6"/><circle cx="462.7" cy="384.5" r="2.6"/><circle cx="442.2" cy="410.2" r="2.6"/><circle cx="418.8" cy="433.9" r="2.6"/><circle cx="392.9" cy="455.1" r="2.6"/><circle cx="364.9" cy="473.5" r="2.6"/><circle cx="335.1" cy="488.5" r="2.6"/><circle cx="304.4" cy="499.5" r="2.6"/><circle cx="273.2" cy="506.1" r="2.6"/><circle cx="242.6" cy="507.9" r="2.6"/><circle cx="213.4" cy="504.7" r="2.6"/><circle cx="186.5" cy="496.3" r="2.6"/><circle cx="162.7" cy="483.1" r="2.6"/><circle cx="143.0" cy="465.2" r="2.6"/><circle cx="127.8" cy="443.3" r="2.6"/><circle cx="117.5" cy="418.2" r="2.6"/></g><g fill="#3c3628" stroke="#1c1810" stroke-width="0.8"><rect x="124.1" y="297.4" width="6.0" height="6.0"/><rect x="463.6" y="144.7" width="6.0" height="6.0"/><rect x="361.9" y="470.5" width="6.0" height="6.0"/></g><polygon points="329.2,330.9 330.8,335.2 335.4,335.4 331.8,338.3 333.1,342.7 329.2,340.1 325.4,342.7 326.7,338.3 323.1,335.4 327.6,335.2" fill="#3c3628" stroke="#f7f3ea" stroke-width="1.4" stroke-linejoin="round" paint-order="stroke"/><g font-family="Georgia, serif" font-size="8" text-anchor="middle"><text x="192.3" y="254.5" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="319.3" y="471.9" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="439.5" y="222.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="251.7" y="268.7" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Garrison</text><text x="432.1" y="386.6" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="378.3" y="438.1" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="347.7" y="229.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="223.7" y="453.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Noble</text><text x="329.2" y="340.4" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Citadel</text><text x="203.0" y="486.2" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="331.5" y="412.0" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="375.1" y="291.2" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="153.0" y="426.9" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Garrison</text><text x="272.9" y="433.3" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="434.9" y="169.6" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="260.9" y="488.1" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Temple</text><text x="446.2" y="314.2" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Garrison</text><text x="382.1" y="364.5" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="279.1" y="185.6" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Residential</text><text x="280.0" y="380.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Residential</text><text x="203.8" y="339.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="366.0" y="156.1" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Garrison</text><text x="263.7" y="333.1" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Residential</text><text x="143.9" y="352.9" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Garrison</text><text x="484.1" y="258.9" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Noble</text><text x="303.2" y="280.0" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Temple</text><text x="208.6" y="405.5" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Residential</text><text x="163.3" y="466.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Noble</text></g><text x="315.0" y="20" text-anchor="middle" font-family="Georgia, serif" font-size="16" font-weight="bold" stroke="#f7f3ea" stroke-width="3" paint-order="stroke" fill="#23211c">Caldwingate</text></svg>
|
|
@@ -215,6 +215,7 @@ def main() -> None:
|
|
|
215
215
|
emit("shantytown", render_settlement("shantytown", seed=5))
|
|
216
216
|
emit("metropolis", render_settlement("metropolis", seed=5))
|
|
217
217
|
emit("grid-city", render_settlement("grid_city", seed=7))
|
|
218
|
+
emit("fortress-town", render_settlement("fortress_town", seed=3))
|
|
218
219
|
emit("roads", render_roads(seed=7))
|
|
219
220
|
emit("regions", render_regions(seed=4))
|
|
220
221
|
emit("template-isthmus", render_template("isthmus", 0.5, 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.22.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"
|
|
@@ -38,6 +38,7 @@ from .regions import Region, RegionGenerator
|
|
|
38
38
|
from .rng import SeededRNG
|
|
39
39
|
from .roads import Road, RegionalRoadGenerator
|
|
40
40
|
from .settlement import (
|
|
41
|
+
Landmark,
|
|
41
42
|
Lot,
|
|
42
43
|
Settlement,
|
|
43
44
|
SettlementConfig,
|
|
@@ -60,7 +61,7 @@ from .terrain import (
|
|
|
60
61
|
compute_cell_polygons,
|
|
61
62
|
)
|
|
62
63
|
|
|
63
|
-
__version__ = "0.
|
|
64
|
+
__version__ = "0.22.0"
|
|
64
65
|
|
|
65
66
|
__all__ = [
|
|
66
67
|
"SeededRNG",
|
|
@@ -102,5 +103,6 @@ __all__ = [
|
|
|
102
103
|
"Lot",
|
|
103
104
|
"Street",
|
|
104
105
|
"Wall",
|
|
106
|
+
"Landmark",
|
|
105
107
|
"SETTLEMENT_PRESETS",
|
|
106
108
|
]
|
|
@@ -149,6 +149,12 @@ _ENUM_SPEC: list[tuple] = [
|
|
|
149
149
|
("layout", ("organic", "grid"), "organic",
|
|
150
150
|
"Street pattern: 'organic' = winding ward-to-ward roads (the classic look); "
|
|
151
151
|
"'grid' = a geometric street grid aligned to the town's long axis."),
|
|
152
|
+
("purpose", ("general", "trade", "fortress", "religious", "harbor",
|
|
153
|
+
"extraction", "transit"), "general",
|
|
154
|
+
"What the town exists for. Anything but 'general' seeds a central landmark "
|
|
155
|
+
"(a special ward the main roads focus on) and biases the ward-kind mix: "
|
|
156
|
+
"trade→market, fortress→citadel, religious→temple, harbor→docks, "
|
|
157
|
+
"extraction→mine, transit→plaza."),
|
|
152
158
|
]
|
|
153
159
|
|
|
154
160
|
# Ward kinds. One central market, optional dockside ward when coastal, and a
|
|
@@ -160,6 +166,29 @@ _OTHER_KINDS: list[str] = (
|
|
|
160
166
|
+ ["temple"] * 1 + ["garrison"] * 1
|
|
161
167
|
)
|
|
162
168
|
|
|
169
|
+
# Town purpose → the kind of the central landmark ward (absent ⇒ no landmark, the
|
|
170
|
+
# central ward stays a plain market). Some reuse existing ward kinds (market,
|
|
171
|
+
# temple, docks); citadel/mine/plaza are landmark-only kinds.
|
|
172
|
+
_LANDMARK_KIND: dict[str, str] = {
|
|
173
|
+
"trade": "market",
|
|
174
|
+
"fortress": "citadel",
|
|
175
|
+
"religious": "temple",
|
|
176
|
+
"harbor": "docks",
|
|
177
|
+
"extraction": "mine",
|
|
178
|
+
"transit": "plaza",
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# Town purpose → extra ward kinds folded into the weighted bag (biases the mix
|
|
182
|
+
# toward what the town is for). "general" has no entry ⇒ the bag is unchanged.
|
|
183
|
+
_PURPOSE_WARD_BIAS: dict[str, list[str]] = {
|
|
184
|
+
"trade": ["craftsmen"] * 3,
|
|
185
|
+
"fortress": ["garrison"] * 3,
|
|
186
|
+
"religious": ["temple"] * 3,
|
|
187
|
+
"harbor": ["craftsmen"] * 2,
|
|
188
|
+
"extraction": ["slums"] * 2 + ["craftsmen"] * 2,
|
|
189
|
+
"transit": ["residential"] * 2 + ["craftsmen"] * 1,
|
|
190
|
+
}
|
|
191
|
+
|
|
163
192
|
# Per-ward lot sizing: a multiplier on the base ``lot_size`` (bigger ⇒ larger,
|
|
164
193
|
# fewer plots), or ``None`` for an open ward with no buildings. Kinds absent from
|
|
165
194
|
# this map use the base size (factor 1.0).
|
|
@@ -188,14 +217,16 @@ def _block_jitter_factor(era: float, wealth: float) -> float:
|
|
|
188
217
|
return _clamp(1.0 - 1.3 * order, 0.25, 1.7)
|
|
189
218
|
|
|
190
219
|
|
|
191
|
-
def _ward_kind_pool(wealth: float) -> list[str]:
|
|
192
|
-
"""The weighted ward-kind bag, shifted by wealth
|
|
193
|
-
more noble/temple
|
|
220
|
+
def _ward_kind_pool(wealth: float, purpose: str = "general") -> list[str]:
|
|
221
|
+
"""The weighted ward-kind bag, shifted by wealth (poor ⇒ more slums, rich ⇒
|
|
222
|
+
more noble/temple) and biased by ``purpose``. Equals ``_OTHER_KINDS`` exactly
|
|
223
|
+
at ``wealth == 0.5`` and ``purpose == "general"``."""
|
|
194
224
|
noble = max(0, round(2 + (wealth - 0.5) * 5))
|
|
195
225
|
temple = max(0, round(1 + (wealth - 0.5) * 2))
|
|
196
226
|
slums = max(0, round(2 - (wealth - 0.5) * 6))
|
|
197
227
|
return (["residential"] * 5 + ["craftsmen"] * 3 + ["noble"] * noble
|
|
198
|
-
+ ["slums"] * slums + ["temple"] * temple + ["garrison"] * 1
|
|
228
|
+
+ ["slums"] * slums + ["temple"] * temple + ["garrison"] * 1
|
|
229
|
+
+ _PURPOSE_WARD_BIAS.get(purpose, []))
|
|
199
230
|
|
|
200
231
|
|
|
201
232
|
@dataclass
|
|
@@ -214,6 +245,8 @@ class SettlementConfig:
|
|
|
214
245
|
"""0..1 — ancient/organic ⇄ modern/grid-regular blocks."""
|
|
215
246
|
layout: str = "organic"
|
|
216
247
|
"""Street pattern: 'organic' (winding ward roads) or 'grid' (geometric grid)."""
|
|
248
|
+
purpose: str = "general"
|
|
249
|
+
"""What the town is for; non-'general' seeds a central landmark + biases wards."""
|
|
217
250
|
walled: bool = False
|
|
218
251
|
"""Whether the town has a wall (stored now, rendered in a later version)."""
|
|
219
252
|
coastal: bool = False
|
|
@@ -239,6 +272,7 @@ class SettlementConfig:
|
|
|
239
272
|
"wealth": self.wealth,
|
|
240
273
|
"era": self.era,
|
|
241
274
|
"layout": self.layout,
|
|
275
|
+
"purpose": self.purpose,
|
|
242
276
|
"walled": self.walled,
|
|
243
277
|
"coastal": self.coastal,
|
|
244
278
|
}
|
|
@@ -306,6 +340,12 @@ SETTLEMENT_PRESETS: dict[str, dict] = {
|
|
|
306
340
|
"metropolis": {"population": 30000, "wealth": 0.92, "era": 0.95, "irregularity": 0.18},
|
|
307
341
|
"grid_city": {"population": 16000, "wealth": 0.7, "era": 0.95,
|
|
308
342
|
"layout": "grid", "irregularity": 0.25},
|
|
343
|
+
"fortress_town": {"population": 5000, "purpose": "fortress", "walled": True,
|
|
344
|
+
"irregularity": 0.3},
|
|
345
|
+
"pilgrimage_site": {"population": 3000, "purpose": "religious",
|
|
346
|
+
"irregularity": 0.5},
|
|
347
|
+
"mining_camp": {"population": 1500, "purpose": "extraction", "wealth": 0.2,
|
|
348
|
+
"irregularity": 0.7},
|
|
309
349
|
}
|
|
310
350
|
|
|
311
351
|
|
|
@@ -339,6 +379,35 @@ class Ward:
|
|
|
339
379
|
)
|
|
340
380
|
|
|
341
381
|
|
|
382
|
+
@dataclass
|
|
383
|
+
class Landmark:
|
|
384
|
+
"""The town's defining feature (set by a non-'general' ``purpose``): the
|
|
385
|
+
central ward promoted to a special ``kind`` (citadel/temple/mine/…), which the
|
|
386
|
+
main roads focus on. ``ward`` is the id of that ward."""
|
|
387
|
+
|
|
388
|
+
ward: int
|
|
389
|
+
kind: str
|
|
390
|
+
center: Point
|
|
391
|
+
name: str
|
|
392
|
+
|
|
393
|
+
def to_dict(self) -> dict:
|
|
394
|
+
return {
|
|
395
|
+
"ward": self.ward,
|
|
396
|
+
"kind": self.kind,
|
|
397
|
+
"center": [self.center[0], self.center[1]],
|
|
398
|
+
"name": self.name,
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
@classmethod
|
|
402
|
+
def from_dict(cls, data: dict) -> "Landmark":
|
|
403
|
+
return cls(
|
|
404
|
+
ward=int(data["ward"]),
|
|
405
|
+
kind=data["kind"],
|
|
406
|
+
center=(float(data["center"][0]), float(data["center"][1])),
|
|
407
|
+
name=data["name"],
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
|
|
342
411
|
@dataclass
|
|
343
412
|
class Lot:
|
|
344
413
|
"""A building plot inside a ward (a convex polygon, the building footprint)."""
|
|
@@ -408,7 +477,8 @@ class Wall:
|
|
|
408
477
|
|
|
409
478
|
@dataclass
|
|
410
479
|
class Settlement:
|
|
411
|
-
"""Generated town layout: footprint, wards, lots, streets, gates, wall,
|
|
480
|
+
"""Generated town layout: footprint, wards, lots, streets, gates, wall,
|
|
481
|
+
landmark, coast."""
|
|
412
482
|
|
|
413
483
|
width: int
|
|
414
484
|
height: int
|
|
@@ -419,8 +489,10 @@ class Settlement:
|
|
|
419
489
|
streets: list[Street] = field(default_factory=list)
|
|
420
490
|
gates: list[Point] = field(default_factory=list)
|
|
421
491
|
wall: Wall | None = None
|
|
492
|
+
landmark: Landmark | None = None # set by a non-'general' purpose
|
|
422
493
|
walled: bool = False
|
|
423
494
|
coastal: bool = False
|
|
495
|
+
purpose: str = "general"
|
|
424
496
|
water_edge: tuple[Point, Point] | None = None # coastline segment, if coastal
|
|
425
497
|
|
|
426
498
|
# -- serialisation / interop ----------------------------------------
|
|
@@ -428,7 +500,7 @@ class Settlement:
|
|
|
428
500
|
def to_dict(self) -> dict:
|
|
429
501
|
"""A JSON-safe mapping of the whole town, round-tripping via :meth:`from_dict`."""
|
|
430
502
|
return {
|
|
431
|
-
"schema": "mapwright/settlement@
|
|
503
|
+
"schema": "mapwright/settlement@5",
|
|
432
504
|
"width": self.width,
|
|
433
505
|
"height": self.height,
|
|
434
506
|
"name": self.name,
|
|
@@ -438,8 +510,10 @@ class Settlement:
|
|
|
438
510
|
"streets": [st.to_dict() for st in self.streets],
|
|
439
511
|
"gates": [[x, y] for x, y in self.gates],
|
|
440
512
|
"wall": None if self.wall is None else self.wall.to_dict(),
|
|
513
|
+
"landmark": None if self.landmark is None else self.landmark.to_dict(),
|
|
441
514
|
"walled": self.walled,
|
|
442
515
|
"coastal": self.coastal,
|
|
516
|
+
"purpose": self.purpose,
|
|
443
517
|
"water_edge": (
|
|
444
518
|
None if self.water_edge is None
|
|
445
519
|
else [[self.water_edge[0][0], self.water_edge[0][1]],
|
|
@@ -460,8 +534,11 @@ class Settlement:
|
|
|
460
534
|
streets=[Street.from_dict(st) for st in data.get("streets", [])],
|
|
461
535
|
gates=[(float(x), float(y)) for x, y in data.get("gates", [])],
|
|
462
536
|
wall=(Wall.from_dict(data["wall"]) if data.get("wall") else None),
|
|
537
|
+
landmark=(Landmark.from_dict(data["landmark"])
|
|
538
|
+
if data.get("landmark") else None),
|
|
463
539
|
walled=bool(data.get("walled", False)),
|
|
464
540
|
coastal=bool(data.get("coastal", False)),
|
|
541
|
+
purpose=data.get("purpose", "general"),
|
|
465
542
|
water_edge=(None if we is None
|
|
466
543
|
else ((float(we[0][0]), float(we[0][1])),
|
|
467
544
|
(float(we[1][0]), float(we[1][1])))),
|
|
@@ -506,15 +583,23 @@ class SettlementGenerator:
|
|
|
506
583
|
|
|
507
584
|
seeds = self._ward_seeds(footprint, cfg.population)
|
|
508
585
|
polys = voronoi_cells(seeds, footprint)
|
|
509
|
-
wards = self._build_wards(polys, cfg.coastal, water_edge, culture,
|
|
510
|
-
|
|
586
|
+
wards, hub_id = self._build_wards(polys, cfg.coastal, water_edge, culture,
|
|
587
|
+
_ward_kind_pool(cfg.wealth, cfg.purpose),
|
|
588
|
+
cfg.purpose)
|
|
589
|
+
# The hub (central ward) is what main roads focus on — a landmark when the
|
|
590
|
+
# purpose set one, otherwise the plain market.
|
|
591
|
+
hub = wards[hub_id].center if wards else (cx, cy)
|
|
592
|
+
landmark = None
|
|
593
|
+
if wards and cfg.purpose in _LANDMARK_KIND:
|
|
594
|
+
w = wards[hub_id]
|
|
595
|
+
landmark = Landmark(ward=w.id, kind=w.kind, center=w.center, name=w.name)
|
|
511
596
|
# A grid town aligns both its lots and its streets to the footprint's axes.
|
|
512
597
|
grid_axes = None
|
|
513
598
|
if cfg.layout == "grid":
|
|
514
599
|
_, u, v = self._principal_axis(footprint)
|
|
515
600
|
grid_axes = (u, v)
|
|
516
601
|
lots = self._build_lots(wards, cfg, grid_axes)
|
|
517
|
-
streets, gates = self._build_streets(wards, footprint, cfg, water_edge)
|
|
602
|
+
streets, gates = self._build_streets(wards, footprint, cfg, water_edge, hub)
|
|
518
603
|
wall = self._build_wall(footprint, cfg.walled, cfg.coastal, water_edge, gates)
|
|
519
604
|
|
|
520
605
|
return Settlement(
|
|
@@ -527,6 +612,8 @@ class SettlementGenerator:
|
|
|
527
612
|
streets=streets,
|
|
528
613
|
gates=gates,
|
|
529
614
|
wall=wall,
|
|
615
|
+
landmark=landmark,
|
|
616
|
+
purpose=cfg.purpose,
|
|
530
617
|
walled=cfg.walled,
|
|
531
618
|
coastal=cfg.coastal,
|
|
532
619
|
water_edge=water_edge,
|
|
@@ -624,18 +711,22 @@ class SettlementGenerator:
|
|
|
624
711
|
water_edge: tuple[Point, Point] | None,
|
|
625
712
|
culture: str,
|
|
626
713
|
kind_pool: list[str],
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
714
|
+
purpose: str = "general",
|
|
715
|
+
) -> tuple[list[Ward], int]:
|
|
716
|
+
"""Name + classify each non-degenerate ward (central = market, or a purpose
|
|
717
|
+
landmark; coastal nearest-water = docks; rest drawn from the weighted
|
|
718
|
+
``kind_pool``). Returns the wards and the *hub* ward's id (the central
|
|
719
|
+
ward — a landmark when ``purpose`` set one, else the market)."""
|
|
630
720
|
valid = [(i, p) for i, p in enumerate(polys) if len(p) >= 3]
|
|
631
721
|
if not valid:
|
|
632
|
-
return []
|
|
722
|
+
return [], 0
|
|
633
723
|
centers = {i: polygon_centroid(p) for i, p in valid}
|
|
634
724
|
|
|
635
725
|
mean_x = sum(c[0] for c in centers.values()) / len(centers)
|
|
636
726
|
mean_y = sum(c[1] for c in centers.values()) / len(centers)
|
|
637
727
|
market = min(centers, key=lambda i: (centers[i][0] - mean_x) ** 2
|
|
638
728
|
+ (centers[i][1] - mean_y) ** 2)
|
|
729
|
+
hub_kind = _LANDMARK_KIND.get(purpose, _MARKET) # central ward's kind
|
|
639
730
|
|
|
640
731
|
docks = None
|
|
641
732
|
if coastal and water_edge is not None:
|
|
@@ -649,15 +740,17 @@ class SettlementGenerator:
|
|
|
649
740
|
+ (centers[i][1] - wy) ** 2)
|
|
650
741
|
|
|
651
742
|
wards: list[Ward] = []
|
|
743
|
+
hub_id = 0
|
|
652
744
|
for new_id, (i, poly) in enumerate(valid):
|
|
653
745
|
if i == market:
|
|
654
|
-
kind =
|
|
746
|
+
kind = hub_kind
|
|
747
|
+
hub_id = new_id
|
|
655
748
|
elif i == docks:
|
|
656
749
|
kind = _DOCKS
|
|
657
750
|
else:
|
|
658
751
|
kind = self._rng.choice(kind_pool)
|
|
659
752
|
wards.append(Ward(new_id, poly, centers[i], self._names.place(culture), kind))
|
|
660
|
-
return wards
|
|
753
|
+
return wards, hub_id
|
|
661
754
|
|
|
662
755
|
# -- lots (recursive bisection of each ward) -------------------------
|
|
663
756
|
|
|
@@ -760,10 +853,12 @@ class SettlementGenerator:
|
|
|
760
853
|
footprint: list[Point],
|
|
761
854
|
cfg: SettlementConfig,
|
|
762
855
|
water_edge: tuple[Point, Point] | None,
|
|
856
|
+
hub: Point | None = None,
|
|
763
857
|
) -> tuple[list[Street], list[Point]]:
|
|
764
858
|
"""Build the street network. ``layout="grid"`` lays a geometric grid aligned
|
|
765
859
|
to the town's long axis; otherwise the classic organic network (MST + a few
|
|
766
|
-
loops over adjacent wards, plus main roads from each gate to the
|
|
860
|
+
loops over adjacent wards, plus main roads from each gate to the ``hub`` —
|
|
861
|
+
the central landmark/market)."""
|
|
767
862
|
if cfg.layout == "grid":
|
|
768
863
|
return self._build_grid_streets(wards, footprint, cfg, water_edge)
|
|
769
864
|
coastal = cfg.coastal
|
|
@@ -799,11 +894,14 @@ class SettlementGenerator:
|
|
|
799
894
|
path = [centers[i], mid, centers[j]] if mid else [centers[i], centers[j]]
|
|
800
895
|
streets.append(Street(path, "minor"))
|
|
801
896
|
|
|
802
|
-
# Main roads: market ↔ each gate.
|
|
803
|
-
|
|
897
|
+
# Main roads: the hub (central landmark/market) ↔ each gate.
|
|
898
|
+
focus = hub
|
|
899
|
+
if focus is None:
|
|
900
|
+
focus = next((w.center for w in wards if w.kind == _MARKET),
|
|
901
|
+
wards[0].center)
|
|
804
902
|
gates = self._gates(footprint, coastal, water_edge)
|
|
805
903
|
for gate in gates:
|
|
806
|
-
streets.append(Street([
|
|
904
|
+
streets.append(Street([focus, gate], "main"))
|
|
807
905
|
return streets, gates
|
|
808
906
|
|
|
809
907
|
@staticmethod
|