Skip to content

Releases: sligara7/mapwright

v0.27.1 — review fixes: continuous rivers, robust config, perf

Choose a tag to compare

@sligara7 sligara7 released this 01 Jul 19:15

Bug fixes, cleanup, and a byte-identical performance win from a full-codebase
review. One behaviour change: rivers are now continuous (see Fixed).

Fixed

  • Rivers are continuous source→mouth polylines again. They were traced
    highest-flux-first, which shattered every river into 2-cell fragments — harmless
    on the rendered map (the pieces tile), but TerrainResult.rivers and its
    serialisation were fragmented, with per-fragment widths. Now traced
    headwaters-first, and River.width comes from the downstream (mouth) end.
    This changes rivers output for a given seed.
  • WorldMapConfig.from_dict no longer crashes on noisy payloads. A str,
    None, NaN, or inf value now coerces to a finite number (or the field
    default) and clamps, instead of raising TypeError — honouring the documented
    "safe for sloppy LLM/JSON input" contract.
  • edge_falloff was applied squared in multi-continent worlds; now applied
    once. (Only affected continents>1 with edge_falloff≠1.0; no preset uses that.)
  • Smart labels no longer misalign colour/style when a region or feature has an
    empty name, and features never get an empty name.

Changed

  • Lloyd relaxation vectorised (np.bincount) — ~24% faster world generation,
    byte-identical output. Removed dead energy bookkeeping from the label placer
    (and made its inner loop scan only overlapping labels); de-cubed the dungeon
    extra-corridor loop.

Added

  • to_json / from_json on Region, Road, CellSummary, and WorldMapConfig
    (parity with the other public dataclasses), and exported get_theme,
    theme_names, DEFAULT_THEME.

v0.27.0 — tectonic worlds + cartography pass

Choose a tag to compare

@sligara7 sligara7 released this 01 Jul 17:52

Additive/non-breaking. Named geographic Features + FeatureGenerator, simulated-annealing smart label placement (LabelPlacer), RegionalSVGRenderer cartography options (relief_style hillshade/hachure, scale_bar, compass), tectonic world simulation. Unblocks StoryFlow v1.4.0 F5 hex-overworld cartography.

v0.25.0 — Planet-scale worlds

Choose a tag to compare

@sligara7 sligara7 released this 07 Jun 00:54
f471b0a

Multi-continent generation reworked so a world reads as many distinct continents spread across a planet, with latitude-driven climate, ocean islands, and a one-line world preset.

Added

  • world preset — full planet in one line (continents=8, sea_level=0.64, continent_spread=0.95, mountain_density=0.7, polar_cold=0.5); best on a wide canvas, e.g. generate(240, 130).
  • polar_cold knob (0..1) — size of the polar ice caps. Latitude sets where the cold falls; this sets how much.

Changed

  • Continents are scattered swell-clusters of varying size (a few big among several small), reliably separated by open ocean; they may run to any map edge.
  • Per-plate Euler-pole rotation → mountain belts vary along their length; boundaries classified convergent/divergent/transform (mountains, rifts, fault valleys).
  • Oceans populated with archipelagos and curved volcanic island arcs.
  • Latitude-driven temperature with polar ice caps; gentler elevation lapse (no more snow-capped equatorial peaks). desert/arctic/tropical presets gain polar_cold.
  • Internal cell cap raised 1500 → 8000 for planet-scale detail (small canvases unaffected).

Compatibility

polar_cold is a trailing WorldMapConfig field — positional construction and existing JSON keys unaffected (to_dict() emits one new key). Single-continent, template, and elevation-hint worlds are byte-unchanged.

Full notes: see CHANGELOG.md.

v0.24.0

Choose a tag to compare

@sligara7 sligara7 released this 06 Jun 11:08
cc99761

Public-API hardening release. No runtime behavior changes — generated output for a given seed is unchanged.

Added

  • Type information (PEP 561). Ships a py.typed marker, so downstream projects' type checkers (mypy, pyright) now verify their use of mapwright's public API against its annotations.

Changed

  • The public contract is now pinned more tightly (no API change). The contract tests freeze the exact field names + order of every exported dataclass, and freeze the to_dict() key schema for every serialisable type — so an internal refactor that renames/reorders/drops a public field or changes a serialised key fails CI loudly instead of silently breaking consumers.

Full Changelog: https://github.com/sligara7/mapwright/blob/main/CHANGELOG.md

v0.23.1

Choose a tag to compare

@sligara7 sligara7 released this 06 Jun 10:44
3ff58ae

Bug-fix release.

Fixed

  • Atlas ocean decorations no longer silently vanish. AtlasRenderer used a symbol pick purely as an existence test (discarding the drawn symbol) then independently coin-flipped between decoration.ship and decoration.creature; packs supplying only one of the two slots dropped ~half their ocean decorations. The renderer now decides the slot once and stamps it, and no longer wastes RNG draws.
  • Terrain-shaped coastal towns no longer place their footprint in the water. The settlement minimum-core clamp ran on rays that had stopped just shy of water, pushing those vertices past the shoreline; it now applies only to rays not stopped by water.

Full Changelog: https://github.com/sligara7/mapwright/blob/main/CHANGELOG.md

v0.23.0 — Terrain-shaped settlements + non-circular continents

Choose a tag to compare

@sligara7 sligara7 released this 06 Jun 02:16

Continents and towns no longer come out roughly circular — both now follow the actual shape-forming process instead of a radial/convex shortcut.

Changed

  • Continents are no longer roughly circular. The single-continent heightmap drops the radial distance-from-centre falloff (which forced a disk) for a per-world ocean-facing direction, an off-centre landmass seeded from two cratonic sub-plates, and a noise-warped, directional sea-frame — so coasts are ragged and the continent runs off one edge like a real margin. Multi-continent / template / hint worlds are byte-identical (archipelago/islands unchanged).
  • Organic town outlines are concave, not oval (star-shaped polar curve with arms + bays); planned grid towns stay convex. The settlement RNG stream changed, so exact organic-town geometry differs from 0.22.0.

Added

  • Terrain-shaped settlements. SettlementGenerator.generate(..., terrain=field) grows the footprint until it meets water or ground too high to build — a coast hugs its shore, land between lakes grows fingers, open flats spread round.
  • New public world_terrain_field(world, region) derives the field from a generated map, and a TerrainField type alias. Gallery terrain-town showcase.

Full notes in CHANGELOG.md.

v0.22.0 — Settlement purpose + landmarks

Choose a tag to compare

@sligara7 sligara7 released this 03 Jun 02:51

Tell a town what it's for. A new purpose gives it a central landmark the main roads converge on, and biases its districts toward its role — fortress, temple town, mining camp, and more.

Added

  • SettlementConfig.purpose"general" (default), or "trade", "fortress", "religious", "harbor", "extraction", "transit". Anything but general:
    • promotes the central ward to a purpose-specific landmark kind — fortress→citadel, religious→temple, trade→market, harbor→docks, extraction→mine, transit→plaza;
    • focuses the main roads on that landmark (they radiate from it to the gates);
    • biases the ward-kind mix toward the purpose (e.g. fortress → more garrison wards);
    • is drawn with a ★ glyph over the landmark ward.
  • New public type Landmark and Settlement.landmark field.
  • New presets fortress_town, pilgrimage_site, mining_camp, and a fortress-town gallery showcase.

Compatibility

  • purpose="general" output is byte-identical to v0.21.0 (no landmark, unchanged ward bag and roads).
  • Settlement serialisation bumps to mapwright/settlement@5, back-compatible — older payloads load with no landmark and purpose="general".
  • Public API gains one name: Landmark (minor bump per SemVer).

Full changelog: v0.21.0...v0.22.0

v0.21.0 — Grid streets

Choose a tag to compare

@sligara7 sligara7 released this 03 Jun 02:50

Towns can now be planned, not just organic. A new layout knob lays a real geometric street grid aligned to the town's long axis, with matching rectangular blocks.

Added

  • SettlementConfig.layout"organic" (default) or "grid". In grid mode a town gets:
    • a geometric street grid aligned to the footprint's principal (long) axis — two families of parallel thoroughfares clipped to the footprint, the central line of each marked "main";
    • gates where the main streets pierce the perimeter (plus a harbour gate when coastal);
    • grid-aligned lots — blocks are bisected along the grid axes, so buildings come out rectangular and street-aligned;
    • walls that splice the mid-edge grid gates into the wall ring as real gatehouse gaps.
  • New preset grid_city and a grid-city gallery showcase.
  • New reusable geometry primitive clip_line_to_convex (Liang–Barsky line ↔ convex-polygon clipping).

Compatibility

  • The default (layout="organic") output is byte-identical to v0.20.0 — all grid logic is gated behind the new mode.
  • Purely additive: layout serialises via to_dict / json_schema (new enum-field support in SettlementConfig). No public-API names added.

Full changelog: 7d18d4e...v0.21.0

v0.20.0 — Settlement era & wealth (shanty ↔ skyscraper)

Choose a tag to compare

@sligara7 sligara7 released this 03 Jun 01:28

Settlement era + wealth — the shanty ↔ skyscraper axis

Two new 0..1 SettlementConfig knobs (mapwright-original, extending the age/era/wealth idea onto towns):

  • wealth — scales plot size (poor = cramped tiny lots ↔ rich = large estates/blocks) and the ward-kind mix (poor = slum-heavy ↔ rich = more noble/temple wards).
  • era — sets block regularity (ancient = organic, jittered ↔ modern = near-grid rectangular blocks).
from mapwright import SettlementGenerator, SettlementConfig, SeededRNG
shanty = SettlementGenerator(SeededRNG(5)).generate(95, 95,
            SettlementConfig(population=14000, wealth=0.08, era=0.3))
metro  = SettlementGenerator(SeededRNG(5)).generate(95, 95,
            SettlementConfig(population=14000, wealth=0.92, era=0.95))

Both knobs are neutral at 0.5, so the default output is byte-identical — the shaping factors are exact identities and the neutral ward bag equals the previous fixed mix. They reshape the RNG draws' magnitudes rather than desyncing the stream.

New presets shantytown and metropolis; the README gallery shows both. Purely additive; serialises via to_dict / json_schema.

This is the imaginative-realms Layout/Geometry descriptor in concrete form.


322 tests pass; ruff clean; existing settlement gallery byte-identical.

v0.19.0 — Caller-directed terrain shape (elevation_hint)

Choose a tag to compare

@sligara7 sligara7 released this 03 Jun 01:09

elevation_hint — art-direct the continent's shape

RegionalTerrainGenerator.generate(..., elevation_hint=…) lets a host (or an LLM) supply the macro land/sea/elevation shape while mapwright fills in organic coastlines, erosion, rivers and climate.

mask = [[0.0, 0.0, 0.0, 0.0],
        [0.0, 0.9, 0.6, 0.0],   # a coarse painted heightmap
        [0.0, 0.6, 0.3, 0.0],
        [0.0, 0.0, 0.0, 0.0]]
terrain = RegionalTerrainGenerator(SeededRNG(7)).generate(80, 56, elevation_hint=mask)
  • Pass a coarse 2D grid (rows north→south, cols west→east — easy for an LLM to emit) or a callable f(x_norm, y_norm) -> elevation over normalised [0, 1] coords.
  • Only relative ordering matters; sea_level still sets how much floods, and the full erosion/hydrology/biome pipeline runs on top — so rivers form and coasts stay organic.
  • Takes precedence over template; set edge_falloff=0 to allow land at the map borders. Non-finite (NaN/inf) hints raise rather than silently poisoning the map.

This is the most on-philosophy answer to "make shapes non-circular": the caller draws the shape, mapwright does the physics (the mapgen4 hint-map idea, clean-room). Pairs naturally with the media_service partnership — an LLM can sketch a continent and mapwright realises it.

Purely additive — the default (elevation_hint=None) output is byte-identical. The README gallery shows a hint-driven crescent continent.


315 tests pass; ruff clean.