@edv4h/usketch-plugin-map 0.2.0
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/LICENSE +21 -0
- package/README.md +44 -0
- package/dist/autotile.d.ts +42 -0
- package/dist/autotile.d.ts.map +1 -0
- package/dist/autotile.js +96 -0
- package/dist/autotile.js.map +1 -0
- package/dist/gen-state.d.ts +20 -0
- package/dist/gen-state.d.ts.map +1 -0
- package/dist/gen-state.js +16 -0
- package/dist/gen-state.js.map +1 -0
- package/dist/generate.d.ts +28 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +93 -0
- package/dist/generate.js.map +1 -0
- package/dist/generators/index.d.ts +34 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +138 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/noise.d.ts +6 -0
- package/dist/generators/noise.d.ts.map +1 -0
- package/dist/generators/noise.js +39 -0
- package/dist/generators/noise.js.map +1 -0
- package/dist/generators/rng.d.ts +7 -0
- package/dist/generators/rng.d.ts.map +1 -0
- package/dist/generators/rng.js +31 -0
- package/dist/generators/rng.js.map +1 -0
- package/dist/icons.d.ts +17 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +949 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/lod.d.ts +27 -0
- package/dist/lod.d.ts.map +1 -0
- package/dist/lod.js +73 -0
- package/dist/lod.js.map +1 -0
- package/dist/map-icon-shape.d.ts +14 -0
- package/dist/map-icon-shape.d.ts.map +1 -0
- package/dist/map-icon-shape.js +106 -0
- package/dist/map-icon-shape.js.map +1 -0
- package/dist/map-layer.d.ts +24 -0
- package/dist/map-layer.d.ts.map +1 -0
- package/dist/map-layer.js +167 -0
- package/dist/map-layer.js.map +1 -0
- package/dist/map-tool-id.d.ts +3 -0
- package/dist/map-tool-id.d.ts.map +1 -0
- package/dist/map-tool-id.js +3 -0
- package/dist/map-tool-id.js.map +1 -0
- package/dist/map-tool.d.ts +3 -0
- package/dist/map-tool.d.ts.map +1 -0
- package/dist/map-tool.js +308 -0
- package/dist/map-tool.js.map +1 -0
- package/dist/palette.d.ts +8 -0
- package/dist/palette.d.ts.map +1 -0
- package/dist/palette.js +92 -0
- package/dist/palette.js.map +1 -0
- package/dist/plugin.d.ts +18 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +137 -0
- package/dist/plugin.js.map +1 -0
- package/dist/range-erase-state.d.ts +9 -0
- package/dist/range-erase-state.d.ts.map +1 -0
- package/dist/range-erase-state.js +12 -0
- package/dist/range-erase-state.js.map +1 -0
- package/dist/range-erase-tool.d.ts +4 -0
- package/dist/range-erase-tool.d.ts.map +1 -0
- package/dist/range-erase-tool.js +58 -0
- package/dist/range-erase-tool.js.map +1 -0
- package/dist/range-erase.d.ts +14 -0
- package/dist/range-erase.d.ts.map +1 -0
- package/dist/range-erase.js +74 -0
- package/dist/range-erase.js.map +1 -0
- package/dist/reactive-store.d.ts +7 -0
- package/dist/reactive-store.d.ts.map +1 -0
- package/dist/reactive-store.js +33 -0
- package/dist/reactive-store.js.map +1 -0
- package/dist/render-config.d.ts +19 -0
- package/dist/render-config.d.ts.map +1 -0
- package/dist/render-config.js +16 -0
- package/dist/render-config.js.map +1 -0
- package/dist/svg-nodes.d.ts +12 -0
- package/dist/svg-nodes.d.ts.map +1 -0
- package/dist/svg-nodes.js +34 -0
- package/dist/svg-nodes.js.map +1 -0
- package/dist/team/enter-banner.d.ts +6 -0
- package/dist/team/enter-banner.d.ts.map +1 -0
- package/dist/team/enter-banner.js +111 -0
- package/dist/team/enter-banner.js.map +1 -0
- package/dist/team/team-layer.d.ts +6 -0
- package/dist/team/team-layer.d.ts.map +1 -0
- package/dist/team/team-layer.js +121 -0
- package/dist/team/team-layer.js.map +1 -0
- package/dist/team/team-map-shape.d.ts +20 -0
- package/dist/team/team-map-shape.d.ts.map +1 -0
- package/dist/team/team-map-shape.js +74 -0
- package/dist/team/team-map-shape.js.map +1 -0
- package/dist/team/team-ops.d.ts +50 -0
- package/dist/team/team-ops.d.ts.map +1 -0
- package/dist/team/team-ops.js +176 -0
- package/dist/team/team-ops.js.map +1 -0
- package/dist/team/team-state.d.ts +9 -0
- package/dist/team/team-state.d.ts.map +1 -0
- package/dist/team/team-state.js +13 -0
- package/dist/team/team-state.js.map +1 -0
- package/dist/terrain.d.ts +15 -0
- package/dist/terrain.d.ts.map +1 -0
- package/dist/terrain.js +309 -0
- package/dist/terrain.js.map +1 -0
- package/dist/tilemap-shape.d.ts +16 -0
- package/dist/tilemap-shape.d.ts.map +1 -0
- package/dist/tilemap-shape.js +77 -0
- package/dist/tilemap-shape.js.map +1 -0
- package/dist/tool-state.d.ts +12 -0
- package/dist/tool-state.d.ts.map +1 -0
- package/dist/tool-state.js +14 -0
- package/dist/tool-state.js.map +1 -0
- package/dist/ui/palette.d.ts +7 -0
- package/dist/ui/palette.d.ts.map +1 -0
- package/dist/ui/palette.js +178 -0
- package/dist/ui/palette.js.map +1 -0
- package/dist/ui/range-erase-palette.d.ts +5 -0
- package/dist/ui/range-erase-palette.d.ts.map +1 -0
- package/dist/ui/range-erase-palette.js +42 -0
- package/dist/ui/range-erase-palette.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EdV4H
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# @edv4h/usketch-plugin-map
|
|
2
|
+
|
|
3
|
+
RPG マップ・タイルペイントプラグイン。community ページ(ワールドマップ空間)で、
|
|
4
|
+
手描きワイヤーフレーム調の地形タイルを塗り、ランドマーク/オブジェクト/マーカーの
|
|
5
|
+
アイコンを配置できる。
|
|
6
|
+
|
|
7
|
+
## 特徴
|
|
8
|
+
|
|
9
|
+
- **地形タイルペイント**: 12 種の地形(草原/森/水辺/砂漠/山/道/雪原/沼地/溶岩/石床/畑/花畑)を
|
|
10
|
+
40×40 グリッドにブラシで塗る。隣接判定で外周(辺)が一段濃くなるオートタイル。
|
|
11
|
+
ブラシ / 消しゴム / 塗りつぶし(flood-fill)。
|
|
12
|
+
- **アイコン 36 種**: ランドマーク12・オブジェクト12・マーカー12 をパレットから選んでスタンプ配置。
|
|
13
|
+
- **Tweaks**: カラフル⇔モノクロ、揺らぎ線⇔クリーン線、線の太さ。パレットと Control HUD の両方から。
|
|
14
|
+
|
|
15
|
+
## アーキテクチャ
|
|
16
|
+
|
|
17
|
+
- **地形の描画は MapLayer**(`ctx.layers`, 低 `order` で全 shape の背面)。
|
|
18
|
+
- **地形データは data-only の `tilemap` shape** に保持 → shape ストア経由で Yjs 同期・Undo が無料。
|
|
19
|
+
`tilemap` は描画を持たず(`render` は空の `<g/>` を返す)/ `hitTest:()=>false` / `locked` で
|
|
20
|
+
選択対象にならない substrate。
|
|
21
|
+
- **アイコンは通常の `map-icon` shape**(選択・移動・リサイズ可、前面)。
|
|
22
|
+
- SVG 素材は `dangerouslySetInnerHTML` を使わず、パース済みノード木(`svg-nodes.tsx`)を
|
|
23
|
+
`React.createElement` で描画(XSS 安全・リポジトリ方針に準拠)。
|
|
24
|
+
|
|
25
|
+
## 使い方
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { createMapPlugin } from "@edv4h/usketch-plugin-map";
|
|
29
|
+
|
|
30
|
+
const plugins = [
|
|
31
|
+
// ...community base plugins
|
|
32
|
+
createMapPlugin({ tile: 40, defaultColorMode: "color", defaultLineStyle: "wobble" }),
|
|
33
|
+
];
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
ツール `map`(ショートカット `m`、アバターのラジアルメニューにも表示)を選ぶとパレットが開く。
|
|
37
|
+
|
|
38
|
+
## オプション(`createMapPlugin`)
|
|
39
|
+
|
|
40
|
+
| option | default | 説明 |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `tile` | `40` | タイル辺(world 単位) |
|
|
43
|
+
| `defaultColorMode` | `"color"` | 初期配色(`color` / `mono`) |
|
|
44
|
+
| `defaultLineStyle` | `"wobble"` | 初期線種(`wobble` / `clean`) |
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { BoundingBox } from "@edv4h/usketch-shared";
|
|
2
|
+
import type { TerrainKey } from "./terrain.js";
|
|
3
|
+
export type Cells = Record<string, TerrainKey>;
|
|
4
|
+
/** Sparse-map key for a cell. */
|
|
5
|
+
export declare function cellKey(col: number, row: number): string;
|
|
6
|
+
export declare function parseCellKey(key: string): [col: number, row: number];
|
|
7
|
+
/** World point → grid cell (origin at world 0,0; tiles are `tile` wide/tall). */
|
|
8
|
+
export declare function worldToCell(x: number, y: number, tile: number): [col: number, row: number];
|
|
9
|
+
/**
|
|
10
|
+
* Terrain at a cell, treating unset cells as the `empty` fallback (e.g. "water"),
|
|
11
|
+
* so callers can judge off-map / unpainted space as sea. Returns `undefined` only
|
|
12
|
+
* when the cell is unset and no fallback is given.
|
|
13
|
+
*/
|
|
14
|
+
export declare function terrainAtCell(cells: Cells, col: number, row: number, empty?: TerrainKey | null): TerrainKey | undefined;
|
|
15
|
+
/** Bounding box (world units) enclosing all painted cells. Empty → zero box. */
|
|
16
|
+
export declare function cellsBounds(cells: Cells, tile: number): BoundingBox;
|
|
17
|
+
export interface ExposedEdges {
|
|
18
|
+
n: boolean;
|
|
19
|
+
e: boolean;
|
|
20
|
+
s: boolean;
|
|
21
|
+
w: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Which sides of cell (c,r) border a different terrain. Unset neighbours count as
|
|
25
|
+
* `empty` (the fallback terrain), so e.g. a water tile next to unpainted space
|
|
26
|
+
* with `empty="water"` is NOT an exposed edge — no spurious coastline against the
|
|
27
|
+
* empty-terrain background. `empty` omitted → unset neighbours are always different.
|
|
28
|
+
*/
|
|
29
|
+
export declare function exposedEdges(cells: Cells, col: number, row: number, empty?: TerrainKey | null): ExposedEdges;
|
|
30
|
+
export interface CellBox {
|
|
31
|
+
minC: number;
|
|
32
|
+
minR: number;
|
|
33
|
+
maxC: number;
|
|
34
|
+
maxR: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Connected region (4-neighbour) of cells sharing the start cell's current
|
|
38
|
+
* terrain. Returns the keys to repaint. When the start cell is empty, `box`
|
|
39
|
+
* (in cell coordinates) is required to keep the flood bounded.
|
|
40
|
+
*/
|
|
41
|
+
export declare function floodFill(cells: Cells, startCol: number, startRow: number, box?: CellBox): string[];
|
|
42
|
+
//# sourceMappingURL=autotile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autotile.d.ts","sourceRoot":"","sources":["../src/autotile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE/C,iCAAiC;AACjC,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAGpE;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAE1F;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,GACvB,UAAU,GAAG,SAAS,CAExB;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAmBnE;AAED,MAAM,WAAW,YAAY;IAC5B,CAAC,EAAE,OAAO,CAAC;IACX,CAAC,EAAE,OAAO,CAAC;IACX,CAAC,EAAE,OAAO,CAAC;IACX,CAAC,EAAE,OAAO,CAAC;CACX;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,GACvB,YAAY,CASd;AAED,MAAM,WAAW,OAAO;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACxB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,OAAO,GACX,MAAM,EAAE,CAuBV"}
|
package/dist/autotile.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/** Sparse-map key for a cell. */
|
|
2
|
+
export function cellKey(col, row) {
|
|
3
|
+
return `${col},${row}`;
|
|
4
|
+
}
|
|
5
|
+
export function parseCellKey(key) {
|
|
6
|
+
const i = key.indexOf(",");
|
|
7
|
+
return [Number(key.slice(0, i)), Number(key.slice(i + 1))];
|
|
8
|
+
}
|
|
9
|
+
/** World point → grid cell (origin at world 0,0; tiles are `tile` wide/tall). */
|
|
10
|
+
export function worldToCell(x, y, tile) {
|
|
11
|
+
return [Math.floor(x / tile), Math.floor(y / tile)];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Terrain at a cell, treating unset cells as the `empty` fallback (e.g. "water"),
|
|
15
|
+
* so callers can judge off-map / unpainted space as sea. Returns `undefined` only
|
|
16
|
+
* when the cell is unset and no fallback is given.
|
|
17
|
+
*/
|
|
18
|
+
export function terrainAtCell(cells, col, row, empty) {
|
|
19
|
+
return cells[cellKey(col, row)] ?? empty ?? undefined;
|
|
20
|
+
}
|
|
21
|
+
/** Bounding box (world units) enclosing all painted cells. Empty → zero box. */
|
|
22
|
+
export function cellsBounds(cells, tile) {
|
|
23
|
+
let minC = Infinity;
|
|
24
|
+
let minR = Infinity;
|
|
25
|
+
let maxC = -Infinity;
|
|
26
|
+
let maxR = -Infinity;
|
|
27
|
+
for (const key of Object.keys(cells)) {
|
|
28
|
+
const [c, r] = parseCellKey(key);
|
|
29
|
+
if (c < minC)
|
|
30
|
+
minC = c;
|
|
31
|
+
if (r < minR)
|
|
32
|
+
minR = r;
|
|
33
|
+
if (c > maxC)
|
|
34
|
+
maxC = c;
|
|
35
|
+
if (r > maxR)
|
|
36
|
+
maxR = r;
|
|
37
|
+
}
|
|
38
|
+
if (minC === Infinity)
|
|
39
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
40
|
+
return {
|
|
41
|
+
x: minC * tile,
|
|
42
|
+
y: minR * tile,
|
|
43
|
+
width: (maxC - minC + 1) * tile,
|
|
44
|
+
height: (maxR - minR + 1) * tile,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Which sides of cell (c,r) border a different terrain. Unset neighbours count as
|
|
49
|
+
* `empty` (the fallback terrain), so e.g. a water tile next to unpainted space
|
|
50
|
+
* with `empty="water"` is NOT an exposed edge — no spurious coastline against the
|
|
51
|
+
* empty-terrain background. `empty` omitted → unset neighbours are always different.
|
|
52
|
+
*/
|
|
53
|
+
export function exposedEdges(cells, col, row, empty) {
|
|
54
|
+
const self = cells[cellKey(col, row)];
|
|
55
|
+
const diff = (c, r) => terrainAtCell(cells, c, r, empty) !== self;
|
|
56
|
+
return {
|
|
57
|
+
n: diff(col, row - 1),
|
|
58
|
+
e: diff(col + 1, row),
|
|
59
|
+
s: diff(col, row + 1),
|
|
60
|
+
w: diff(col - 1, row),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Connected region (4-neighbour) of cells sharing the start cell's current
|
|
65
|
+
* terrain. Returns the keys to repaint. When the start cell is empty, `box`
|
|
66
|
+
* (in cell coordinates) is required to keep the flood bounded.
|
|
67
|
+
*/
|
|
68
|
+
export function floodFill(cells, startCol, startRow, box) {
|
|
69
|
+
const target = cells[cellKey(startCol, startRow)]; // TerrainKey | undefined (empty)
|
|
70
|
+
if (target === undefined && !box)
|
|
71
|
+
return [];
|
|
72
|
+
const inBox = (c, r) => !box || (c >= box.minC && c <= box.maxC && r >= box.minR && r <= box.maxR);
|
|
73
|
+
if (!inBox(startCol, startRow))
|
|
74
|
+
return [];
|
|
75
|
+
const out = [];
|
|
76
|
+
const seen = new Set();
|
|
77
|
+
const stack = [[startCol, startRow]];
|
|
78
|
+
while (stack.length) {
|
|
79
|
+
const next = stack.pop();
|
|
80
|
+
if (!next)
|
|
81
|
+
break;
|
|
82
|
+
const [c, r] = next;
|
|
83
|
+
const key = cellKey(c, r);
|
|
84
|
+
if (seen.has(key))
|
|
85
|
+
continue;
|
|
86
|
+
if (!inBox(c, r))
|
|
87
|
+
continue;
|
|
88
|
+
if (cells[key] !== target)
|
|
89
|
+
continue;
|
|
90
|
+
seen.add(key);
|
|
91
|
+
out.push(key);
|
|
92
|
+
stack.push([c + 1, r], [c - 1, r], [c, r + 1], [c, r - 1]);
|
|
93
|
+
}
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=autotile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autotile.js","sourceRoot":"","sources":["../src/autotile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CellBox } from "./autotile.js";
|
|
2
|
+
/** A drag-preview rectangle in WORLD coordinates (null when not dragging). */
|
|
3
|
+
export interface WorldRect {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
w: number;
|
|
7
|
+
h: number;
|
|
8
|
+
}
|
|
9
|
+
export interface GenState {
|
|
10
|
+
algorithmId: string;
|
|
11
|
+
seed: number;
|
|
12
|
+
params: Record<string, number>;
|
|
13
|
+
/** Live drag rectangle for the range-select preview. */
|
|
14
|
+
pendingRect: WorldRect | null;
|
|
15
|
+
/** Last box generated into, for the "再生成" (regenerate) button. */
|
|
16
|
+
lastBox: CellBox | null;
|
|
17
|
+
}
|
|
18
|
+
export declare const genStateStore: import("./reactive-store.js").ReactiveStore<GenState>;
|
|
19
|
+
export declare function useGenState(): GenState;
|
|
20
|
+
//# sourceMappingURL=gen-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gen-state.d.ts","sourceRoot":"","sources":["../src/gen-state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAI7C,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV;AAED,MAAM,WAAW,QAAQ;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,wDAAwD;IACxD,WAAW,EAAE,SAAS,GAAG,IAAI,CAAC;IAC9B,kEAAkE;IAClE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACxB;AAED,eAAO,MAAM,aAAa,uDAMxB,CAAC;AAEH,wBAAgB,WAAW,IAAI,QAAQ,CAEtC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Map-generation UI state (algorithm / seed / params / drag-preview), shared
|
|
2
|
+
// between the palette and the map tool. Module-scoped, app-local (not synced).
|
|
3
|
+
import { useSyncExternalStore } from "react";
|
|
4
|
+
import { defaultParams, GENERATORS } from "./generators/index.js";
|
|
5
|
+
import { createReactiveStore } from "./reactive-store.js";
|
|
6
|
+
export const genStateStore = createReactiveStore({
|
|
7
|
+
algorithmId: GENERATORS[0].id,
|
|
8
|
+
seed: 12345,
|
|
9
|
+
params: defaultParams(GENERATORS[0]),
|
|
10
|
+
pendingRect: null,
|
|
11
|
+
lastBox: null,
|
|
12
|
+
});
|
|
13
|
+
export function useGenState() {
|
|
14
|
+
return useSyncExternalStore(genStateStore.subscribe, genStateStore.get, genStateStore.get);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=gen-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gen-state.js","sourceRoot":"","sources":["../src/gen-state.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,+EAA+E;AAC/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAoB1D,MAAM,CAAC,MAAM,aAAa,GAAG,mBAAmB,CAAW;IAC1D,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE;IAC7B,IAAI,EAAE,KAAK;IACX,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACpC,WAAW,EAAE,IAAI;IACjB,OAAO,EAAE,IAAI;CACb,CAAC,CAAC;AAEH,MAAM,UAAU,WAAW;IAC1B,OAAO,oBAAoB,CAAC,aAAa,CAAC,SAAS,EAAE,aAAa,CAAC,GAAG,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;AAC5F,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BoardStore, CommandRegistry } from "@edv4h/usketch-shared";
|
|
2
|
+
import { type CellBox } from "./autotile.js";
|
|
3
|
+
export interface GenerateDeps {
|
|
4
|
+
store: BoardStore;
|
|
5
|
+
commands: CommandRegistry;
|
|
6
|
+
tile: number;
|
|
7
|
+
}
|
|
8
|
+
/** The single shared tilemap, creating it (live) if none exists yet. */
|
|
9
|
+
export declare function resolveTilemap(store: BoardStore, tile: number): {
|
|
10
|
+
id: string;
|
|
11
|
+
created: boolean;
|
|
12
|
+
};
|
|
13
|
+
/** Clamp a box to a maximum dimension, centered, so generation stays bounded. */
|
|
14
|
+
export declare function clampBox(box: CellBox): CellBox;
|
|
15
|
+
/** The visible viewport as a cell box ("全体" = current view on the infinite canvas). */
|
|
16
|
+
export declare function viewportCellBox(store: BoardStore, tile: number): CellBox;
|
|
17
|
+
export interface GenerateRequest {
|
|
18
|
+
generatorId: string;
|
|
19
|
+
seed: number;
|
|
20
|
+
params: Record<string, number>;
|
|
21
|
+
box: CellBox;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generate terrain into `box`, replacing existing cells inside it (cells outside
|
|
25
|
+
* are untouched), and commit as one undoable command.
|
|
26
|
+
*/
|
|
27
|
+
export declare function generateIntoBox(deps: GenerateDeps, req: GenerateRequest): void;
|
|
28
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAW,eAAe,EAAa,MAAM,uBAAuB,CAAC;AAE7F,OAAO,EAAE,KAAK,OAAO,EAAoC,MAAM,eAAe,CAAC;AAQ/E,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACb;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAOhG;AAED,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAc9C;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAcxE;AAED,MAAM,WAAW,eAAe;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,GAAG,EAAE,OAAO,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,eAAe,GAAG,IAAI,CAoC9E"}
|
package/dist/generate.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createAddShapeCommand } from "@edv4h/usketch-store";
|
|
2
|
+
import { cellKey, cellsBounds } from "./autotile.js";
|
|
3
|
+
import { genStateStore } from "./gen-state.js";
|
|
4
|
+
import { GENERATORS_BY_ID } from "./generators/index.js";
|
|
5
|
+
import { isTileMap, makeTileMap } from "./tilemap-shape.js";
|
|
6
|
+
/** Hard cap so a zoomed-out "view" generation can't explode into millions of cells. */
|
|
7
|
+
const MAX_BOX_DIM = 256;
|
|
8
|
+
/** The single shared tilemap, creating it (live) if none exists yet. */
|
|
9
|
+
export function resolveTilemap(store, tile) {
|
|
10
|
+
for (const [, s] of store.getShapes()) {
|
|
11
|
+
if (isTileMap(s))
|
|
12
|
+
return { id: s.id, created: false };
|
|
13
|
+
}
|
|
14
|
+
const tm = makeTileMap(tile);
|
|
15
|
+
store.addShape(tm);
|
|
16
|
+
return { id: tm.id, created: true };
|
|
17
|
+
}
|
|
18
|
+
/** Clamp a box to a maximum dimension, centered, so generation stays bounded. */
|
|
19
|
+
export function clampBox(box) {
|
|
20
|
+
const w = box.maxC - box.minC + 1;
|
|
21
|
+
const h = box.maxR - box.minR + 1;
|
|
22
|
+
if (w <= MAX_BOX_DIM && h <= MAX_BOX_DIM)
|
|
23
|
+
return box;
|
|
24
|
+
const cc = Math.round((box.minC + box.maxC) / 2);
|
|
25
|
+
const cr = Math.round((box.minR + box.maxR) / 2);
|
|
26
|
+
const half = Math.floor(MAX_BOX_DIM / 2);
|
|
27
|
+
// Exactly MAX_BOX_DIM cells wide/tall when clamping (inclusive bounds).
|
|
28
|
+
return {
|
|
29
|
+
minC: w > MAX_BOX_DIM ? cc - half : box.minC,
|
|
30
|
+
maxC: w > MAX_BOX_DIM ? cc - half + MAX_BOX_DIM - 1 : box.maxC,
|
|
31
|
+
minR: h > MAX_BOX_DIM ? cr - half : box.minR,
|
|
32
|
+
maxR: h > MAX_BOX_DIM ? cr - half + MAX_BOX_DIM - 1 : box.maxR,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** The visible viewport as a cell box ("全体" = current view on the infinite canvas). */
|
|
36
|
+
export function viewportCellBox(store, tile) {
|
|
37
|
+
const vp = store.getViewport();
|
|
38
|
+
const w = typeof window !== "undefined" ? window.innerWidth : 800;
|
|
39
|
+
const h = typeof window !== "undefined" ? window.innerHeight : 600;
|
|
40
|
+
const left = -vp.x / vp.zoom;
|
|
41
|
+
const top = -vp.y / vp.zoom;
|
|
42
|
+
const right = (w - vp.x) / vp.zoom;
|
|
43
|
+
const bottom = (h - vp.y) / vp.zoom;
|
|
44
|
+
return clampBox({
|
|
45
|
+
minC: Math.floor(left / tile),
|
|
46
|
+
minR: Math.floor(top / tile),
|
|
47
|
+
maxC: Math.floor((right - 1) / tile),
|
|
48
|
+
maxR: Math.floor((bottom - 1) / tile),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Generate terrain into `box`, replacing existing cells inside it (cells outside
|
|
53
|
+
* are untouched), and commit as one undoable command.
|
|
54
|
+
*/
|
|
55
|
+
export function generateIntoBox(deps, req) {
|
|
56
|
+
const gen = GENERATORS_BY_ID.get(req.generatorId);
|
|
57
|
+
if (!gen)
|
|
58
|
+
return;
|
|
59
|
+
const box = clampBox(req.box);
|
|
60
|
+
if (box.maxC < box.minC || box.maxR < box.minR)
|
|
61
|
+
return;
|
|
62
|
+
const { id, created } = resolveTilemap(deps.store, deps.tile);
|
|
63
|
+
const shape = deps.store.getShape(id);
|
|
64
|
+
if (!shape)
|
|
65
|
+
return;
|
|
66
|
+
const tile = shape.tile ?? deps.tile;
|
|
67
|
+
const prev = { ...shape.cells };
|
|
68
|
+
const next = { ...shape.cells };
|
|
69
|
+
// Clear the target box, then lay down the generated field.
|
|
70
|
+
for (let r = box.minR; r <= box.maxR; r++) {
|
|
71
|
+
for (let c = box.minC; c <= box.maxC; c++)
|
|
72
|
+
delete next[cellKey(c, r)];
|
|
73
|
+
}
|
|
74
|
+
Object.assign(next, gen.generate({ box, seed: req.seed, params: req.params }));
|
|
75
|
+
const prevBounds = cellsBounds(prev, tile);
|
|
76
|
+
const nextBounds = cellsBounds(next, tile);
|
|
77
|
+
if (created) {
|
|
78
|
+
// Newly created this action: commit as an add so undo deletes it and redo
|
|
79
|
+
// re-adds it (updateShape on a deleted id would no-op, breaking redo).
|
|
80
|
+
const finalShape = { ...shape, cells: next, ...nextBounds };
|
|
81
|
+
deps.store.deleteShape(id);
|
|
82
|
+
deps.commands.execute(createAddShapeCommand(deps.store, finalShape));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const command = {
|
|
86
|
+
execute: () => deps.store.updateShape(id, { cells: next, ...nextBounds }),
|
|
87
|
+
undo: () => deps.store.updateShape(id, { cells: prev, ...prevBounds }),
|
|
88
|
+
};
|
|
89
|
+
deps.commands.execute(command);
|
|
90
|
+
}
|
|
91
|
+
genStateStore.set({ lastBox: box });
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type CellBox, type Cells } from "../autotile.js";
|
|
2
|
+
import type { TerrainKey } from "../terrain.js";
|
|
3
|
+
export interface GenParam {
|
|
4
|
+
name: string;
|
|
5
|
+
label: string;
|
|
6
|
+
type: "number";
|
|
7
|
+
min: number;
|
|
8
|
+
max: number;
|
|
9
|
+
step: number;
|
|
10
|
+
default: number;
|
|
11
|
+
}
|
|
12
|
+
export interface GenContext {
|
|
13
|
+
box: CellBox;
|
|
14
|
+
seed: number;
|
|
15
|
+
params: Record<string, number>;
|
|
16
|
+
}
|
|
17
|
+
export interface MapGenerator {
|
|
18
|
+
id: string;
|
|
19
|
+
label: string;
|
|
20
|
+
params: readonly GenParam[];
|
|
21
|
+
/** Assign a TerrainKey to every cell in `ctx.box`. */
|
|
22
|
+
generate(ctx: GenContext): Cells;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Map a normalised elevation [0,1] to a terrain band, given a sea level. Land
|
|
26
|
+
* bands are taken over the remaining height above sea level (`h` in [0,1]) so
|
|
27
|
+
* every band stays reachable at any sea level.
|
|
28
|
+
*/
|
|
29
|
+
export declare function elevationToTerrain(e: number, seaLevel: number): TerrainKey;
|
|
30
|
+
export declare const GENERATORS: readonly MapGenerator[];
|
|
31
|
+
export declare const GENERATORS_BY_ID: ReadonlyMap<string, MapGenerator>;
|
|
32
|
+
/** Default param values for a generator (used to seed gen-state). */
|
|
33
|
+
export declare function defaultParams(gen: MapGenerator): Record<string, number>;
|
|
34
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generators/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAW,MAAM,gBAAgB,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD,MAAM,WAAW,QAAQ;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IAC1B,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,KAAK,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAQ1E;AA0HD,eAAO,MAAM,UAAU,EAAE,SAAS,YAAY,EAI7C,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAE9D,CAAC;AAEF,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIvE"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Extensible map-generator registry. Each generator turns a bounded cell box
|
|
2
|
+
// into a full Cells map (every cell assigned a terrain, water included) from a
|
|
3
|
+
// seed + numeric params, deterministically.
|
|
4
|
+
import { cellKey } from "../autotile.js";
|
|
5
|
+
import { fbm } from "./noise.js";
|
|
6
|
+
/**
|
|
7
|
+
* Map a normalised elevation [0,1] to a terrain band, given a sea level. Land
|
|
8
|
+
* bands are taken over the remaining height above sea level (`h` in [0,1]) so
|
|
9
|
+
* every band stays reachable at any sea level.
|
|
10
|
+
*/
|
|
11
|
+
export function elevationToTerrain(e, seaLevel) {
|
|
12
|
+
if (e < seaLevel)
|
|
13
|
+
return "water";
|
|
14
|
+
const h = (e - seaLevel) / Math.max(1e-6, 1 - seaLevel);
|
|
15
|
+
if (h < 0.08)
|
|
16
|
+
return "sand";
|
|
17
|
+
if (h < 0.35)
|
|
18
|
+
return "grass";
|
|
19
|
+
if (h < 0.65)
|
|
20
|
+
return "forest";
|
|
21
|
+
if (h < 0.85)
|
|
22
|
+
return "mtn";
|
|
23
|
+
return "snow";
|
|
24
|
+
}
|
|
25
|
+
const P = (name, label, def, min, max, step) => ({
|
|
26
|
+
name,
|
|
27
|
+
label,
|
|
28
|
+
type: "number",
|
|
29
|
+
default: def,
|
|
30
|
+
min,
|
|
31
|
+
max,
|
|
32
|
+
step,
|
|
33
|
+
});
|
|
34
|
+
function fillField(ctx, mode) {
|
|
35
|
+
const { box, seed, params } = ctx;
|
|
36
|
+
const scale = params.scale ?? 0.08;
|
|
37
|
+
const seaLevel = params.seaLevel ?? 0.4;
|
|
38
|
+
const falloff = params.falloff ?? 0;
|
|
39
|
+
const size = params.size ?? 3;
|
|
40
|
+
const cols = box.maxC - box.minC + 1;
|
|
41
|
+
const rows = box.maxR - box.minR + 1;
|
|
42
|
+
// Pass 1: raw elevation + range for normalisation.
|
|
43
|
+
const raw = new Array(cols * rows);
|
|
44
|
+
let min = Infinity;
|
|
45
|
+
let max = -Infinity;
|
|
46
|
+
let i = 0;
|
|
47
|
+
for (let r = box.minR; r <= box.maxR; r++) {
|
|
48
|
+
for (let c = box.minC; c <= box.maxC; c++) {
|
|
49
|
+
const e = fbm(seed, c, r, scale);
|
|
50
|
+
raw[i++] = e;
|
|
51
|
+
if (e < min)
|
|
52
|
+
min = e;
|
|
53
|
+
if (e > max)
|
|
54
|
+
max = e;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const span = max - min;
|
|
58
|
+
// Pass 2: normalise → (optional) island falloff → terrain band. A degenerate
|
|
59
|
+
// flat field (span 0, e.g. a 1×1 box) maps to a neutral 0.5 so `seaLevel`
|
|
60
|
+
// still has an effect instead of forcing everything to water.
|
|
61
|
+
const normalize = (e) => (span > 0 ? (e - min) / span : 0.5);
|
|
62
|
+
const cx = (box.minC + box.maxC) / 2;
|
|
63
|
+
const cy = (box.minR + box.maxR) / 2;
|
|
64
|
+
const hw = Math.max(1, (box.maxC - box.minC) / 2);
|
|
65
|
+
const hh = Math.max(1, (box.maxR - box.minR) / 2);
|
|
66
|
+
const cells = {};
|
|
67
|
+
i = 0;
|
|
68
|
+
for (let r = box.minR; r <= box.maxR; r++) {
|
|
69
|
+
for (let c = box.minC; c <= box.maxC; c++) {
|
|
70
|
+
let e = normalize(raw[i++]); // normalised to [0,1]
|
|
71
|
+
let forceWater = false;
|
|
72
|
+
if (mode !== "plain") {
|
|
73
|
+
const nx = (c - cx) / hw;
|
|
74
|
+
const ny = (r - cy) / hh;
|
|
75
|
+
const d = Math.hypot(nx, ny); // 0 at centre, 1 at mid-edge, ~1.41 at a corner
|
|
76
|
+
if (mode === "sink") {
|
|
77
|
+
// Subtractive edge falloff → archipelago of islands.
|
|
78
|
+
e -= falloff * Math.min(1, d) ** 2;
|
|
79
|
+
}
|
|
80
|
+
else if (d >= 1) {
|
|
81
|
+
// island: at/beyond the box edge is always sea, independent of
|
|
82
|
+
// seaLevel (guarantees the island is fully surrounded by water).
|
|
83
|
+
forceWater = true;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// Multiplicative radial mask → a single landmass. Higher `size`
|
|
87
|
+
// keeps land closer to the edge (a bigger island).
|
|
88
|
+
e *= Math.max(0, 1 - d ** size);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
cells[cellKey(c, r)] = forceWater ? "water" : elevationToTerrain(e, seaLevel);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return cells;
|
|
95
|
+
}
|
|
96
|
+
const noiseGenerator = {
|
|
97
|
+
id: "noise",
|
|
98
|
+
label: "ノイズ地形(大陸)",
|
|
99
|
+
params: [
|
|
100
|
+
P("scale", "地形の細かさ", 0.08, 0.02, 0.3, 0.01),
|
|
101
|
+
P("seaLevel", "海面", 0.3, 0, 0.9, 0.02),
|
|
102
|
+
],
|
|
103
|
+
generate: (ctx) => fillField(ctx, "plain"),
|
|
104
|
+
};
|
|
105
|
+
const islandGenerator = {
|
|
106
|
+
id: "island",
|
|
107
|
+
label: "島(海に囲まれた1つの島)",
|
|
108
|
+
params: [
|
|
109
|
+
P("scale", "地形の細かさ", 0.09, 0.02, 0.3, 0.01),
|
|
110
|
+
P("seaLevel", "海面", 0.3, 0, 0.9, 0.02),
|
|
111
|
+
P("size", "島の大きさ", 3, 1.5, 6, 0.5),
|
|
112
|
+
],
|
|
113
|
+
generate: (ctx) => fillField(ctx, "island"),
|
|
114
|
+
};
|
|
115
|
+
const islandsGenerator = {
|
|
116
|
+
id: "islands",
|
|
117
|
+
label: "群島(島々)",
|
|
118
|
+
params: [
|
|
119
|
+
P("scale", "地形の細かさ", 0.1, 0.02, 0.3, 0.01),
|
|
120
|
+
P("seaLevel", "海面", 0.4, 0, 0.9, 0.02),
|
|
121
|
+
P("falloff", "島らしさ(縁を海に)", 0.5, 0, 1, 0.05),
|
|
122
|
+
],
|
|
123
|
+
generate: (ctx) => fillField(ctx, "sink"),
|
|
124
|
+
};
|
|
125
|
+
export const GENERATORS = [
|
|
126
|
+
noiseGenerator,
|
|
127
|
+
islandGenerator,
|
|
128
|
+
islandsGenerator,
|
|
129
|
+
];
|
|
130
|
+
export const GENERATORS_BY_ID = new Map(GENERATORS.map((g) => [g.id, g]));
|
|
131
|
+
/** Default param values for a generator (used to seed gen-state). */
|
|
132
|
+
export function defaultParams(gen) {
|
|
133
|
+
const out = {};
|
|
134
|
+
for (const p of gen.params)
|
|
135
|
+
out[p.name] = p.default;
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generators/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fractal Brownian motion: sum of octaves of value noise. `scale` sets the base
|
|
3
|
+
* frequency (world-cell → noise space). Returns a value normalised to [0,1].
|
|
4
|
+
*/
|
|
5
|
+
export declare function fbm(seed: number, cx: number, cy: number, scale: number, octaves?: number): number;
|
|
6
|
+
//# sourceMappingURL=noise.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noise.d.ts","sourceRoot":"","sources":["../../src/generators/noise.ts"],"names":[],"mappings":"AAuBA;;;GAGG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,SAAI,GAAG,MAAM,CAa5F"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Deterministic value-noise elevation field (fBm) — no external deps.
|
|
2
|
+
// Elevation is sampled per cell and normalised to [0,1].
|
|
3
|
+
import { hash2 } from "./rng.js";
|
|
4
|
+
function smoothstep(t) {
|
|
5
|
+
return t * t * (3 - 2 * t);
|
|
6
|
+
}
|
|
7
|
+
/** Bilinear value noise at (x,y) using a seeded integer lattice. Range ~[0,1). */
|
|
8
|
+
function valueNoise2D(seed, x, y) {
|
|
9
|
+
const x0 = Math.floor(x);
|
|
10
|
+
const y0 = Math.floor(y);
|
|
11
|
+
const fx = smoothstep(x - x0);
|
|
12
|
+
const fy = smoothstep(y - y0);
|
|
13
|
+
const v00 = hash2(seed, x0, y0);
|
|
14
|
+
const v10 = hash2(seed, x0 + 1, y0);
|
|
15
|
+
const v01 = hash2(seed, x0, y0 + 1);
|
|
16
|
+
const v11 = hash2(seed, x0 + 1, y0 + 1);
|
|
17
|
+
const top = v00 + (v10 - v00) * fx;
|
|
18
|
+
const bottom = v01 + (v11 - v01) * fx;
|
|
19
|
+
return top + (bottom - top) * fy;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* fractal Brownian motion: sum of octaves of value noise. `scale` sets the base
|
|
23
|
+
* frequency (world-cell → noise space). Returns a value normalised to [0,1].
|
|
24
|
+
*/
|
|
25
|
+
export function fbm(seed, cx, cy, scale, octaves = 4) {
|
|
26
|
+
let amp = 1;
|
|
27
|
+
let freq = scale;
|
|
28
|
+
let sum = 0;
|
|
29
|
+
let norm = 0;
|
|
30
|
+
for (let o = 0; o < octaves; o++) {
|
|
31
|
+
// Vary the lattice per octave so they don't align.
|
|
32
|
+
sum += amp * valueNoise2D(seed + o * 1013, cx * freq, cy * freq);
|
|
33
|
+
norm += amp;
|
|
34
|
+
amp *= 0.5;
|
|
35
|
+
freq *= 2;
|
|
36
|
+
}
|
|
37
|
+
return norm > 0 ? sum / norm : 0;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=noise.js.map
|