@heroiclands/package-build 22.4.5 → 22.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 22.4.6
4
+
5
+ ### Patch Changes
6
+
7
+ `content-build map --chart <shortcode>` draws the chart from a place: the map from it at a larger horizon, north up, the hops chaining on as long as none turns back, each place on the ring of its summed days and each hop beyond the neighbours drawn fainter. `--horizon <days>` sets how far the rings reach, a year by default, and a place farther than that is left out.
8
+
3
9
  ## 22.4.5
4
10
 
5
11
  ### Patch Changes
@@ -47,7 +47,7 @@
47
47
  * npx content-build markdown [paths..] [--fix]
48
48
  * npx content-build manifest [root] [--out <dir>]
49
49
  * npx content-build site [--out <dir>]
50
- * npx content-build map [--tree] [--from <shortcode>] [--travel] [--out <dir>]
50
+ * npx content-build map [--tree] [--from <shortcode>] [--chart <shortcode>] [--travel] [--out <dir>]
51
51
  * npx content-build reachability <dir> [file] [--index <shortcode>]
52
52
  * npx content-build addresses diff --from <zip|dir> [--strict]
53
53
  *
@@ -146,6 +146,7 @@ import {
146
146
  } from "../engine/address-diff.mjs";
147
147
  import { emittedArtFor, itemPackJsonDirs } from "../engine/generate.mjs";
148
148
  import { buildMaps, FROM_ALL, MAP_DIR } from "../engine/map-build.mjs";
149
+ import { CHART_HORIZON_DAYS } from "../engine/map-layout.mjs";
149
150
  import { GRAPHVIZ_ENGINES } from "../engine/map-graphviz.mjs";
150
151
  import { loadMapWorld } from "../engine/map-places.mjs";
151
152
 
@@ -1673,12 +1674,13 @@ function siteCommand() {
1673
1674
  *
1674
1675
  * Reads the content index — this package's and every declared dependency's —
1675
1676
  * so a consumer's map draws its dependencies' places beside its own, and
1676
- * writes under `build/map/`, always the `.dot` beside the `.svg`. Three
1677
+ * writes under `build/map/`, always the `.dot` beside the `.svg`. Four
1677
1678
  * drawings: the containment tree from `parents` (`--tree`, the author's
1678
1679
  * check, whose anomalies are reported as findings), the map from a place
1679
- * from its `borders` and `routes` (`--from`), and the whole route graph
1680
- * (`--travel`). GraphViz draws; when it is absent the command says what to
1681
- * install and stops.
1680
+ * from its `borders` and `routes` (`--from`), the chart from a place — the
1681
+ * same drawing at a larger horizon (`--chart`, `--horizon`) — and the whole
1682
+ * route graph (`--travel`). GraphViz draws; when it is absent the command
1683
+ * says what to install and stops.
1682
1684
  *
1683
1685
  * @returns {object} The yargs command module.
1684
1686
  */
@@ -1686,7 +1688,8 @@ function siteCommand() {
1686
1688
  function mapCommand() {
1687
1689
  return {
1688
1690
  command: "map",
1689
- describe: "Draw the containment tree, the map from a place, or the route graph",
1691
+ describe:
1692
+ "Draw the containment tree, the map or the chart from a place, or the route graph",
1690
1693
  builder: (yargs) => {
1691
1694
  yargs
1692
1695
  .option("tree", {
@@ -1709,6 +1712,21 @@ function mapCommand() {
1709
1712
  type: "string",
1710
1713
  array: true,
1711
1714
  })
1715
+ .option("chart", {
1716
+ describe:
1717
+ "Draw the chart from this place as `chart-<shortcode>.svg` — the map " +
1718
+ "from it at a larger horizon, the hops chaining on; repeatable, " +
1719
+ `or \`${FROM_ALL}\` for every place with a border or a route.`,
1720
+ type: "string",
1721
+ array: true,
1722
+ })
1723
+ .option("horizon", {
1724
+ describe:
1725
+ "The charts' horizon in days, a marker of the scale: rings are " +
1726
+ "drawn to it, and a place farther than it is left out.",
1727
+ type: "number",
1728
+ default: CHART_HORIZON_DAYS,
1729
+ })
1712
1730
  .option("travel", {
1713
1731
  describe: "Draw the whole route graph as `travel.svg`.",
1714
1732
  type: "boolean",
@@ -1783,6 +1801,8 @@ function mapCommand() {
1783
1801
  tree: argv.tree,
1784
1802
  root: argv.root,
1785
1803
  from: argv.from ?? [],
1804
+ chart: argv.chart ?? [],
1805
+ horizon: argv.horizon,
1786
1806
  travel: argv.travel,
1787
1807
  engine: argv.engine,
1788
1808
  rankdir: argv.rankdir,
package/docs/api.md CHANGED
@@ -443,36 +443,40 @@ The places a map is drawn from — this package's index records and every fetche
443
443
 
444
444
  ### `engine.mapLayout`
445
445
 
446
- The geometry of the map from a place, as arithmetic over the place records: north up, east right, the angle from the bearing and the radius from the days marker on log-spaced rings, two hops composed into one bearing only where they agree. Nothing here touches a file or GraphViz.
447
-
448
- | Export | Signature | Returns | Use it when |
449
- | --------------- | -------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------- |
450
- | `HORIZON_DAYS` | `const HORIZON_DAYS` | `number` | the horizon of the map from a place, 90 days; beyond it a place is a name at the rim |
451
- | `RING_DAYS` | `const RING_DAYS` | `readonly number[]` | the days markers that are rings — every marker up to the horizon |
452
- | `bearingAngle` | `bearingAngle(bearing)` | `number\|undefined` | the angle of a bearing in degrees counter-clockwise from east — `N` is 90, `E` 0 |
453
- | `bearingVector` | `bearingVector(bearing)` | `{x, y}\|undefined` | the unit vector of a bearing |
454
- | `bearingSteps` | `bearingSteps(a, b)` | `number\|undefined` | how many steps around the compass separate two bearings, the shorter way |
455
- | `rimRadius` | `rimRadius()` | `number` | the radius of the rim, one log step beyond the horizon |
456
- | `ringRadius` | `ringRadius(days)` | `number` | the radius of the ring a days value sits on — the rim for none, or for a value beyond the horizon |
457
- | `rings` | `rings()` | `object[]` | the rings inside out, each marker with its radius |
458
- | `daysMarker` | `daysMarker(total)` | `number\|undefined` | the smallest marker of the scale that covers a total of days |
459
- | `edgeLength` | `edgeLength(days)` | `number` | the length a route asks of `neato` in the route graph, inches, on the rings' log scale |
460
- | `composeHops` | `composeHops(first, second)` | `object\|undefined` | two hops composed into one bearing and one total where they agree — the same bearing or adjacent — else `undefined` |
461
- | `relationsOf` | `relationsOf(places, shortcode)` | `object` | every border and route touching one place, read from both ends, plus the targets that resolved to nothing |
462
- | `layoutFrom` | `layoutFrom(places, centre)` | `object` | laying out the map from a place: every node pinned in points, every edge, the rings and the rim |
463
- | `travelGraph` | `travelGraph(places)` | `object` | the whole route graph — one edge per pair, however many ends and modes state it, each with a length from its days |
446
+ The geometry of the map from a place and of the chart from one, as arithmetic over the place records: north up, east right, the angle from the bearing and the radius from the days marker on log-spaced rings, hops composed into one bearing as long as none turns back. Nothing here touches a file or GraphViz.
447
+
448
+ | Export | Signature | Returns | Use it when |
449
+ | -------------------- | ------------------------------------------ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
450
+ | `HORIZON_DAYS` | `const HORIZON_DAYS` | `number` | the horizon of the map from a place, 90 days; beyond it a place is a name at the rim |
451
+ | `CHART_HORIZON_DAYS` | `const CHART_HORIZON_DAYS` | `number` | the horizon of the chart from a place when none is asked for, 360 days — the end of the scale |
452
+ | `RING_DAYS` | `const RING_DAYS` | `readonly number[]` | the days markers that are rings on the map from a place — every marker up to its horizon |
453
+ | `bearingAngle` | `bearingAngle(bearing)` | `number\|undefined` | the angle of a bearing in degrees counter-clockwise from east — `N` is 90, `E` 0 |
454
+ | `bearingVector` | `bearingVector(bearing)` | `{x, y}\|undefined` | the unit vector of a bearing |
455
+ | `bearingSteps` | `bearingSteps(a, b)` | `number\|undefined` | how many steps around the compass separate two bearings, the shorter way |
456
+ | `rimRadius` | `rimRadius(horizon?)` | `number` | the radius of the rim, one log step beyond the horizon — the map from a place's by default |
457
+ | `ringRadius` | `ringRadius(days, horizon?)` | `number` | the radius of the ring a days value sits on — the rim for none, or for a value beyond the horizon |
458
+ | `rings` | `rings(horizon?)` | `object[]` | the rings inside out, each marker up to the horizon with its radius |
459
+ | `daysMarker` | `daysMarker(total)` | `number\|undefined` | the smallest marker of the scale that covers a total of days |
460
+ | `edgeLength` | `edgeLength(days)` | `number` | the length a route asks of `neato` in the route graph, inches, on the rings' log scale |
461
+ | `composeChain` | `composeChain(hops)` | `object\|undefined` | a chain of hops composed into one bearing and one total, each hop within an octant of the running direction, else `undefined` |
462
+ | `composeHops` | `composeHops(first, second)` | `object\|undefined` | two hops composed into one bearing and one total where they agree — the same bearing or adjacent — else `undefined` |
463
+ | `relationsOf` | `relationsOf(places, shortcode)` | `object` | every border and route touching one place, read from both ends, plus the targets that resolved to nothing |
464
+ | `layoutFrom` | `layoutFrom(places, centre)` | `object` | laying out the map from a place: every node pinned in points, every edge, the rings and the rim |
465
+ | `layoutChart` | `layoutChart(places, centre, { horizon })` | `object` | laying out the chart from a place: the map from it at a larger horizon, the hops chaining on as long as none turns back |
466
+ | `travelGraph` | `travelGraph(places)` | `object` | the whole route graph — one edge per pair, however many ends and modes state it, each with a length from its days |
464
467
 
465
468
  ### `engine.mapDot`
466
469
 
467
- The three drawings of `content-build map` as DOT text, sharing one style table: shape, colour and size by `subType`, an anomaly overriding colour alone, every label a `name.full`, every tooltip an address, and a `URL` where the place carries one so the SVG's names are links.
470
+ The drawings of `content-build map` as DOT text, sharing one style table: shape, colour and size by `subType`, an anomaly overriding colour alone, every label a `name.full`, every tooltip an address, and a `URL` where the place carries one so the SVG's names are links.
468
471
 
469
- | Export | Signature | Returns | Use it when |
470
- | ----------- | -------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------- |
471
- | `STYLE` | `const STYLE` | — | the shape, colour and size of each `subType`, and of a polity |
472
- | `dotString` | `dotString(value)` | `string` | quoting text for DOT |
473
- | `treeDot` | `treeDot({ places, polities, analysis, root, … })` | `string` | the containment tree — the whole world clustered by continent, or the subtree beneath `root` |
474
- | `fromDot` | `fromDot(layout, places, { scale, title })` | `string` | the map from a place, every node pinned for `neato -n2`, the rings drawn faintly with their markers |
475
- | `travelDot` | `travelDot(graph, places, { scale, title })` | `string` | the whole route graph for `neato`, each edge asking for a length from its days |
472
+ | Export | Signature | Returns | Use it when |
473
+ | ------------ | -------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
474
+ | `STYLE` | `const STYLE` | — | the shape, colour and size of each `subType`, and of a polity |
475
+ | `dotString` | `dotString(value)` | `string` | quoting text for DOT |
476
+ | `treeDot` | `treeDot({ places, polities, analysis, root, … })` | `string` | the containment tree — the whole world clustered by continent, or the subtree beneath `root` |
477
+ | `hopOpacity` | `hopOpacity(hop)` | `number` | how faint a place is drawn by its hop count — full to the second hop, a step down each hop beyond, floored |
478
+ | `fromDot` | `fromDot(layout, places, { scale, title })` | `string` | the map or the chart from a place, every node pinned for `neato -n2`, the rings drawn faintly with their markers |
479
+ | `travelDot` | `travelDot(graph, places, { scale, title })` | `string` | the whole route graph for `neato`, each edge asking for a length from its days |
476
480
 
477
481
  ### `engine.mapGraphviz`
478
482
 
@@ -489,12 +493,12 @@ GraphViz, as the map command reaches it: a build-time tool of that command alone
489
493
 
490
494
  `content-build map` as a function: the drawings written under an output directory, always the `.dot` beside the `.svg`, the tree's anomalies returned as findings, and a missing GraphViz either an error or — for a build that would rather skip — one warning.
491
495
 
492
- | Export | Signature | Returns | Use it when |
493
- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------------- |
494
- | `MAP_DIR` | `const MAP_DIR` | `string` | where the maps land below a package's root, `build/map` |
495
- | `FROM_ALL` | `const FROM_ALL` | `string` | the `--from` value that means every place with a relation, `all` |
496
- | `relatedPlaces` | `relatedPlaces(places)` | `string[]` | every place that states, or is named in, a border or a route |
497
- | `buildMaps` | `buildMaps({ world, outDir, tree, root, from, travel, engine, rankdir, nodesep, ranksep, scale, polities, locate, requireGraphviz })` | `{written, findings, skipped}` | drawing any of the three from a loaded world |
496
+ | Export | Signature | Returns | Use it when |
497
+ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ----------------------------------------------------------------------------- |
498
+ | `MAP_DIR` | `const MAP_DIR` | `string` | where the maps land below a package's root, `build/map` |
499
+ | `FROM_ALL` | `const FROM_ALL` | `string` | the `--from` or `--chart` value that means every place with a relation, `all` |
500
+ | `relatedPlaces` | `relatedPlaces(places)` | `string[]` | every place that states, or is named in, a border or a route |
501
+ | `buildMaps` | `buildMaps({ world, outDir, tree, root, from, chart, horizon, travel, engine, rankdir, nodesep, ranksep, scale, polities, locate, requireGraphviz })` | `{written, findings, skipped}` | drawing any of the four from a loaded world |
498
502
 
499
503
  ### `engine.systems`
500
504
 
package/docs/commands.md CHANGED
@@ -1864,7 +1864,8 @@ demo-book: error: the document tree named by `pdf.document` cannot be read
1864
1864
 
1865
1865
  **NAME**
1866
1866
 
1867
- Draw the containment tree, the map from a place, or the whole route graph.
1867
+ Draw the containment tree, the map or the chart from a place, or the whole
1868
+ route graph.
1868
1869
 
1869
1870
  **SYNOPSIS**
1870
1871
 
@@ -1872,6 +1873,7 @@ Draw the containment tree, the map from a place, or the whole route graph.
1872
1873
  content-build map --tree [--root <shortcode>] [--engine dot|twopi|neato] [--rankdir TB|LR|BT|RL]
1873
1874
  [--nodesep <inches>] [--ranksep <inches>] [--no-polities]
1874
1875
  content-build map --from <shortcode>.. | --from all
1876
+ content-build map --chart <shortcode>.. | --chart all [--horizon <days>]
1875
1877
  content-build map --travel
1876
1878
  content-build map [...] [--scale <factor>] [--base <url>] [--out <dir>]
1877
1879
  ```
@@ -1884,14 +1886,13 @@ index — so a consumer's map draws its dependencies' places beside its own
1884
1886
  and no note is opened except to locate a finding. Everything lands under
1885
1887
  `build/map/`, always the `.dot` beside the `.svg`, so a drawing can be
1886
1888
  re-rendered, diffed or read without the index. GraphViz draws: `dot` (or
1887
- `twopi`, `neato`) for the tree, `neato` for the other two. It is a
1888
- build-time tool of this command alone; when the engine a run needs is not
1889
- installed the command says so, names what to install, and stops. A build
1890
- that asks for maps as one step of a larger job — a site build — writes
1891
- the `.dot` files, emits one warning and skips the renderings rather than
1892
- failing.
1889
+ `twopi`, `neato`) for the tree, `neato` for the others. It is a build-time
1890
+ tool of this command alone; when the engine a run needs is not installed
1891
+ the command says so, names what to install, and stops. A build that asks
1892
+ for maps as one step of a larger job — a site build — writes the `.dot`
1893
+ files, emits one warning and skips the renderings rather than failing.
1893
1894
 
1894
- The three drawings, any of which may be asked for in one run:
1895
+ The four drawings, any of which may be asked for in one run:
1895
1896
 
1896
1897
  **`--tree`** — the author's check. The containment tree from `data.parents`,
1897
1898
  edges running child → parent, clustered by continent — a `region` parented
@@ -1950,6 +1951,39 @@ sailor's chart, a tax survey, partial and from somewhere, whose prose says
1950
1951
  whose it is — and never the survey. The two are two sources of the same
1951
1952
  thing on a place's page.
1952
1953
 
1954
+ **`--chart <shortcode>`** — the chart from a place, `chart-<shortcode>.svg`:
1955
+ the world-scale drawing chosen by viewpoint rather than projection — the
1956
+ sea from a port, the eastern roads from a caravan city. It is the same
1957
+ graph from the same place with a larger horizon: the centre pinned, north
1958
+ up and east right, the neighbours exactly as `--from` draws them, and
1959
+ beyond them the hops chaining on. Hops compose as a running vector sum
1960
+ weighted by their days, and a hop is accepted when its bearing lies within
1961
+ one octant — 45° either way — of the **running composed direction** at
1962
+ that point, not of the previous hop; so a path may bend a little at every
1963
+ hop, but never turn back, and a place reached only through a hop that
1964
+ turns back is omitted. Where several chains reach one place, the one with
1965
+ the fewest total days is drawn. The total is the sum of the hops' days,
1966
+ placed on the ring of the smallest marker not less than it. A border
1967
+ carries no days: on the first hop it sits on the innermost ring, as on the
1968
+ map from a place; beyond the first hop it makes the total unknown, the
1969
+ place goes to the rim marked unknown, and the chart states nothing beyond
1970
+ it. `all` draws the chart from every place that states or is named in a
1971
+ border or a route.
1972
+
1973
+ _The horizon._ `--horizon <days>`, a marker of the scale, `360` by default:
1974
+ one ring per marker up to and including it, on the same log spacing as the
1975
+ map from a place, so a chart to a year's travel is simply a larger page
1976
+ with more rings. A place whose total exceeds the horizon is omitted — the
1977
+ chart is already the larger view, and there is no "beyond" rim, only the
1978
+ unknown one, a log step past the horizon. `--horizon` applies to charts
1979
+ alone; the map from a place keeps its horizon of 90 days and its rim.
1980
+
1981
+ _The dimming._ The neighbours are drawn full, the second hop as the map
1982
+ from a place draws it — grey border and label, pale fill, faint edge —
1983
+ and each hop beyond a step fainter, never below half opacity, so the
1984
+ farthest place still reads. An edge is dimmed with its far end, and
1985
+ labelled with its bearing and days like any other.
1986
+
1953
1987
  **`--travel`** — the whole route graph, `travel.svg`, for the author: one
1954
1988
  undirected edge per pair of places, a border dashed, each route asking
1955
1989
  `neato` for a length on the same log scale as the rings so the god's-eye
@@ -1964,25 +1998,28 @@ page can inline the SVG. Without it the names are plain text.
1964
1998
 
1965
1999
  **OPTIONS**
1966
2000
 
1967
- | Option | Type | Default | Description |
1968
- | ------------ | ------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
1969
- | `--tree` | boolean | `false` | Draw the containment tree: `tree.svg`, one `tree-<shortcode>.svg` per continent, and the findings. |
1970
- | `--root` | string | — | With `--tree`, draw the subtree beneath this place as `tree-<shortcode>.svg` and report nothing. |
1971
- | `--from` | string | — | Draw the map from this place as `from-<shortcode>.svg`; repeatable, or `all` for every place with a border or a route. |
1972
- | `--travel` | boolean | `false` | Draw the whole route graph as `travel.svg`. |
1973
- | `--engine` | choice | `dot` | The tree's layout engine — `dot`, `twopi` (radial, rooted on the world or the `--root`) or `neato`. The other two drawings are always `neato`. |
1974
- | `--rankdir` | choice | `TB` | The tree's rank direction — `TB`, `LR`, `BT` or `RL`; `dot` engine only. |
1975
- | `--nodesep` | number | by `--scale` | The tree's GraphViz `nodesep`, in inches. |
1976
- | `--ranksep` | number | by engine | The tree's GraphViz `ranksep`, in inches. |
1977
- | `--scale` | number | `1` | Multiply every node's width, height, font size and border weight, the legend's included. |
1978
- | `--polities` | boolean | `true` | Draw polities beside their regions in the tree. `--no-polities` drops them. |
1979
- | `--base` | string | — | The site base this package's pages are served under, ending in a slash; given, every name is a link to its page. |
1980
- | `--out` | string | `build/map` | Directory to write into, relative to the working directory. |
2001
+ | Option | Type | Default | Description |
2002
+ | ------------ | ------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
2003
+ | `--tree` | boolean | `false` | Draw the containment tree: `tree.svg`, one `tree-<shortcode>.svg` per continent, and the findings. |
2004
+ | `--root` | string | — | With `--tree`, draw the subtree beneath this place as `tree-<shortcode>.svg` and report nothing. |
2005
+ | `--from` | string | — | Draw the map from this place as `from-<shortcode>.svg`; repeatable, or `all` for every place with a border or a route. |
2006
+ | `--chart` | string | — | Draw the chart from this place as `chart-<shortcode>.svg` — the map from it at a larger horizon; repeatable, or `all`. |
2007
+ | `--horizon` | number | `360` | The charts' horizon in days, a marker of the scale: rings are drawn to it, and a place farther than it is left out. |
2008
+ | `--travel` | boolean | `false` | Draw the whole route graph as `travel.svg`. |
2009
+ | `--engine` | choice | `dot` | The tree's layout engine — `dot`, `twopi` (radial, rooted on the world or the `--root`) or `neato`. The other drawings are always `neato`. |
2010
+ | `--rankdir` | choice | `TB` | The tree's rank direction — `TB`, `LR`, `BT` or `RL`; `dot` engine only. |
2011
+ | `--nodesep` | number | by `--scale` | The tree's GraphViz `nodesep`, in inches. |
2012
+ | `--ranksep` | number | by engine | The tree's GraphViz `ranksep`, in inches. |
2013
+ | `--scale` | number | `1` | Multiply every node's width, height, font size and border weight, the legend's included. |
2014
+ | `--polities` | boolean | `true` | Draw polities beside their regions in the tree. `--no-polities` drops them. |
2015
+ | `--base` | string | — | The site base this package's pages are served under, ending in a slash; given, every name is a link to its page. |
2016
+ | `--out` | string | `build/map` | Directory to write into, relative to the working directory. |
1981
2017
 
1982
2018
  **EXIT STATUS**
1983
2019
 
1984
- 1 when no drawing is asked for, when a `--from` or `--root` names no
1985
- place, when the GraphViz engine a drawing needs is not installed, when a
2020
+ 1 when no drawing is asked for, when a `--from`, `--chart` or `--root`
2021
+ names no place, when `--horizon` is not a marker of the days scale, when
2022
+ the GraphViz engine a drawing needs is not installed, when a
1986
2023
  dependency's index cannot be read, when a note the index cannot record is
1987
2024
  met, on any error finding from `--tree` (a place with no parent, a parent
1988
2025
  that does not resolve, a cycle), or on any other thrown error. Otherwise 0
@@ -1999,6 +2036,9 @@ assets/content/Regions/Marches/Lost_Fort.md:9:14: error: place "lostfort" names
1999
2036
  $ content-build map --from dunharargn --base /thalorna/
2000
2037
  […] 394 place(s) → 1 drawing(s) under build/map
2001
2038
 
2039
+ $ content-build map --chart takheperurgn --horizon 90
2040
+ […] 394 place(s) → 1 drawing(s) under build/map
2041
+
2002
2042
  $ content-build map --travel
2003
2043
  […] GraphViz's `neato` is not installed, and `content-build map` draws with it; install GraphViz (`brew install graphviz` on macOS, `apt-get install graphviz` on Debian or Ubuntu) and run the command again
2004
2044
  ```
@@ -14,7 +14,7 @@
14
14
  /**
15
15
  * `content-build map`: the drawings, written under `build/map/`.
16
16
  *
17
- * Three modes, each a function of the world the content index describes:
17
+ * Four modes, each a function of the world the content index describes:
18
18
  *
19
19
  * - **`tree`** — the containment tree from `parents`, the author's check.
20
20
  * `tree.svg` for the whole world and `tree-<continent>.svg` per continent,
@@ -23,6 +23,9 @@
23
23
  * - **`from`** — the map from a place, `from-<shortcode>.svg`, for each
24
24
  * place named, or for every place that takes part in a border or a route
25
25
  * with `all`.
26
+ * - **`chart`** — the chart from a place, `chart-<shortcode>.svg`: the same
27
+ * drawing at a larger horizon, the hops chaining on as long as none turns
28
+ * back. `all` as for `from`.
26
29
  * - **`travel`** — the whole route graph, `travel.svg`.
27
30
  *
28
31
  * Every rendering is written beside the `.dot` it was drawn from, and the
@@ -42,7 +45,8 @@ import path from "node:path";
42
45
 
43
46
  import { fromDot, travelDot, treeDot } from "./map-dot.mjs";
44
47
  import { findGraphviz, graphvizMissingMessage, renderDot } from "./map-graphviz.mjs";
45
- import { layoutFrom, travelGraph } from "./map-layout.mjs";
48
+ import { CHART_HORIZON_DAYS, layoutChart, layoutFrom, travelGraph } from "./map-layout.mjs";
49
+ import { TRAVEL_DAYS } from "./place-relations.mjs";
46
50
  import { analyzeContainment, containmentFindings } from "./map-places.mjs";
47
51
 
48
52
  /**
@@ -53,7 +57,7 @@ import { analyzeContainment, containmentFindings } from "./map-places.mjs";
53
57
  export const MAP_DIR = "build/map";
54
58
 
55
59
  /**
56
- * The value of `--from` that means every place with a relation.
60
+ * The value of `--from` or `--chart` that means every place with a relation.
57
61
  *
58
62
  * @type {string}
59
63
  */
@@ -61,7 +65,7 @@ export const FROM_ALL = "all";
61
65
 
62
66
  /**
63
67
  * Every place that states, or is named in, a border or a route — the places
64
- * `--from all` draws.
68
+ * `--from all` and `--chart all` draw.
65
69
  *
66
70
  * @param {Map<string, import("./map-places.mjs").MapPlace>} places - Every place.
67
71
  * @returns {string[]} Shortcodes, sorted.
@@ -89,10 +93,14 @@ export function relatedPlaces(places) {
89
93
  * @param {string} [opts.root] - With `tree`, the subtree beneath this place.
90
94
  * @param {string[]} [opts.from] - Draw the map from each of these places, or
91
95
  * from every related place for {@link FROM_ALL}.
96
+ * @param {string[]} [opts.chart] - Draw the chart from each of these places,
97
+ * or from every related place for {@link FROM_ALL}.
98
+ * @param {number} [opts.horizon] - The charts' horizon in days, a marker of
99
+ * the scale; the end of the scale by default.
92
100
  * @param {boolean} [opts.travel] - Draw the whole route graph.
93
101
  * @param {string} [opts.engine="dot"] - The tree's engine: `dot`, `twopi` or
94
- * `neato`. The map from a place is always `neato` with every node pinned,
95
- * and the route graph always `neato`.
102
+ * `neato`. The map and the chart from a place are always `neato` with
103
+ * every node pinned, and the route graph always `neato`.
96
104
  * @param {string} [opts.rankdir="TB"] - The tree's rank direction, `dot` only.
97
105
  * @param {number} [opts.nodesep] - The tree's `nodesep`, inches.
98
106
  * @param {number} [opts.ranksep] - The tree's `ranksep`, inches.
@@ -107,8 +115,8 @@ export function relatedPlaces(places) {
107
115
  * column?: number, severity: "error"|"warning", message: string}>,
108
116
  * skipped: boolean}} What was written, what was found, and whether the
109
117
  * rendering was skipped for want of GraphViz.
110
- * @throws {Error} When no mode is asked for, a named place is not one, or
111
- * GraphViz is required and absent.
118
+ * @throws {Error} When no mode is asked for, a named place is not one, the
119
+ * horizon is not a marker of the scale, or GraphViz is required and absent.
112
120
  */
113
121
  export function buildMaps({
114
122
  world,
@@ -116,6 +124,8 @@ export function buildMaps({
116
124
  tree = false,
117
125
  root,
118
126
  from = [],
127
+ chart = [],
128
+ horizon = CHART_HORIZON_DAYS,
119
129
  travel = false,
120
130
  engine = "dot",
121
131
  rankdir = "TB",
@@ -126,8 +136,15 @@ export function buildMaps({
126
136
  locate = findGraphviz,
127
137
  requireGraphviz = true,
128
138
  }) {
129
- if (!tree && from.length === 0 && !travel) {
130
- throw new Error("name a drawing: --tree, --from <shortcode> or --travel");
139
+ if (!tree && from.length === 0 && chart.length === 0 && !travel) {
140
+ throw new Error(
141
+ "name a drawing: --tree, --from <shortcode>, --chart <shortcode> or --travel",
142
+ );
143
+ }
144
+ if (!TRAVEL_DAYS.includes(horizon)) {
145
+ throw new Error(
146
+ `--horizon must be a marker of the days scale — one of ${TRAVEL_DAYS.join(", ")} — and ${horizon} is not`,
147
+ );
131
148
  }
132
149
  const { places } = world;
133
150
 
@@ -135,7 +152,7 @@ export function buildMaps({
135
152
  // so a missing one is reported before a directory is touched.
136
153
  const engines = new Set();
137
154
  if (tree) engines.add(engine);
138
- if (from.length || travel) engines.add("neato");
155
+ if (from.length || chart.length || travel) engines.add("neato");
139
156
  /** @type {Map<string, string>} */
140
157
  const binaries = new Map();
141
158
  const findings = [];
@@ -212,6 +229,19 @@ export function buildMaps({
212
229
  emit(`from-${centre}`, fromDot(layout, places, { scale }), "neato", ["-n2"]);
213
230
  }
214
231
 
232
+ const viewpoints = chart.includes(FROM_ALL) ? relatedPlaces(places) : chart;
233
+ for (const centre of viewpoints) {
234
+ const layout = layoutChart(places, centre, { horizon });
235
+ for (const { from: place, to } of layout.unresolved) {
236
+ findings.push({
237
+ file: unresolvedFile(world, place),
238
+ severity: "warning",
239
+ message: `place "${place}" names "${to}" in a border or a route, and no place declares that shortcode; the chart centred on "${centre}" leaves it out`,
240
+ });
241
+ }
242
+ emit(`chart-${centre}`, fromDot(layout, places, { scale }), "neato", ["-n2"]);
243
+ }
244
+
215
245
  if (travel) {
216
246
  const graph = travelGraph(places);
217
247
  for (const { from: place, to } of graph.unresolved) {