@connected-web/terrain-editor 0.1.2 → 0.1.4

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/dist/index.cjs CHANGED
@@ -34,11 +34,13 @@ __export(index_exports, {
34
34
  buildRimMesh: () => buildRimMesh,
35
35
  buildWynArchive: () => buildWynArchive,
36
36
  createHeightSampler: () => createHeightSampler,
37
+ createIconUrlMap: () => createIconUrlMap,
37
38
  createLayerBrowserStore: () => createLayerBrowserStore,
38
39
  createMaskEditor: () => createMaskEditor,
39
40
  createProjectStore: () => createProjectStore,
40
41
  createTerrainViewerHost: () => createTerrainViewerHost,
41
42
  createViewerOverlay: () => createViewerOverlay,
43
+ enhanceLocationsWithIconUrls: () => enhanceLocationsWithIconUrls,
42
44
  getDefaultTerrainTheme: () => getDefaultTerrainTheme,
43
45
  initTerrainViewer: () => initTerrainViewer,
44
46
  loadWynArchive: () => loadWynArchive,
@@ -1655,6 +1657,9 @@ async function initTerrainViewer(container, dataset, options = {}) {
1655
1657
  },
1656
1658
  setCameraOffset: (offset, focusId) => {
1657
1659
  cameraOffset.target = THREE2.MathUtils.clamp(offset, -0.45, 0.45);
1660
+ if (cameraTween) {
1661
+ return;
1662
+ }
1658
1663
  const targetId = focusId ?? currentFocusId;
1659
1664
  if (targetId) {
1660
1665
  const loc = currentLocations.find((item) => item.id === targetId);
@@ -3244,17 +3249,76 @@ function createMaskEditor(options) {
3244
3249
  markClean
3245
3250
  };
3246
3251
  }
3252
+
3253
+ // src/iconHelpers.ts
3254
+ function inferMimeType(path, fallback = "image/png") {
3255
+ const extension = path.split(".").pop()?.toLowerCase();
3256
+ if (!extension) return fallback;
3257
+ if (extension === "png") return "image/png";
3258
+ if (extension === "jpg" || extension === "jpeg") return "image/jpeg";
3259
+ if (extension === "webp") return "image/webp";
3260
+ if (extension === "gif") return "image/gif";
3261
+ if (extension === "svg") return "image/svg+xml";
3262
+ return fallback;
3263
+ }
3264
+ function toObjectUrl(entry) {
3265
+ const type = entry.type ?? inferMimeType(entry.path);
3266
+ const blob = new Blob([entry.data], { type });
3267
+ return URL.createObjectURL(blob);
3268
+ }
3269
+ function createIconUrlMap(files, options = {}) {
3270
+ const urls = /* @__PURE__ */ new Map();
3271
+ const prefix = options.prefix ?? "icons/";
3272
+ if (!files?.length) {
3273
+ return {
3274
+ urls,
3275
+ cleanup: () => void 0
3276
+ };
3277
+ }
3278
+ for (const entry of files) {
3279
+ if (!entry.path) continue;
3280
+ if (prefix && !entry.path.startsWith(prefix)) continue;
3281
+ const url = toObjectUrl(entry);
3282
+ urls.set(entry.path, url);
3283
+ }
3284
+ const cleanup = () => {
3285
+ for (const url of urls.values()) {
3286
+ URL.revokeObjectURL(url);
3287
+ }
3288
+ urls.clear();
3289
+ };
3290
+ return { urls, cleanup };
3291
+ }
3292
+ function enhanceLocationsWithIconUrls(locations, files, options) {
3293
+ const { urls, cleanup } = createIconUrlMap(files, options);
3294
+ const enhanced = locations.map((location) => {
3295
+ if (!location.icon) {
3296
+ return { ...location };
3297
+ }
3298
+ const iconUrl = urls.get(location.icon);
3299
+ if (!iconUrl) {
3300
+ return { ...location };
3301
+ }
3302
+ return {
3303
+ ...location,
3304
+ iconUrl
3305
+ };
3306
+ });
3307
+ return { locations: enhanced, cleanup };
3308
+ }
3247
3309
  // Annotate the CommonJS export names for ESM import in node:
3248
3310
  0 && (module.exports = {
3249
3311
  applyHeightField,
3250
3312
  buildRimMesh,
3251
3313
  buildWynArchive,
3252
3314
  createHeightSampler,
3315
+ createIconUrlMap,
3253
3316
  createLayerBrowserStore,
3254
3317
  createMaskEditor,
3255
3318
  createProjectStore,
3256
3319
  createTerrainViewerHost,
3257
3320
  createViewerOverlay,
3321
+ enhanceLocationsWithIconUrls,
3258
3322
  getDefaultTerrainTheme,
3259
3323
  initTerrainViewer,
3260
3324
  loadWynArchive,
package/dist/index.d.cts CHANGED
@@ -406,4 +406,34 @@ type MaskEditorOptions = {
406
406
  };
407
407
  declare function createMaskEditor(options: MaskEditorOptions): MaskEditorHandle;
408
408
 
409
- export { type BuildWynArchiveOptions, type Cleanup, type DeepPartial, type HeightSampler, type LayerBrowserEntry, type LayerBrowserState, type LayerBrowserStore, type LayerToggleState, type LegendLayer, type LoadWynArchiveOptions, type LoadedWynFile, type LocationPickPayload, type LocationViewState, type MarkerSpriteStateStyle, type MarkerSpriteTheme, type MarkerStemGeometryShape, type MarkerStemStateStyle, type MarkerStemTheme, type MaskEditorHandle, type MaskEditorState, type MaskImage, type MaskStroke, type MaskStrokeMode, type MaskStrokePoint, type TerrainDataset, type TerrainHandle, type TerrainLegend, type TerrainLocation, type TerrainProjectFileEntry, type TerrainProjectMetadata, type TerrainProjectSnapshot, type TerrainProjectStore, type TerrainTheme, type TerrainThemeOverrides, type TerrainViewMode, type TerrainViewerHostHandle, type TerrainViewerHostOptions, type ViewerLifecycleState, type ViewerOverlayCustomButton, type ViewerOverlayHandle, type ViewerOverlayLoadingState, type ViewerOverlayOptions, type WynArchiveProgressEvent, applyHeightField, buildRimMesh, buildWynArchive, createHeightSampler, createLayerBrowserStore, createMaskEditor, createProjectStore, createTerrainViewerHost, createViewerOverlay, getDefaultTerrainTheme, initTerrainViewer, loadWynArchive, loadWynArchiveFromArrayBuffer, loadWynArchiveFromFile, resolveTerrainTheme, sampleHeightValue };
409
+ type CreateIconUrlMapOptions = {
410
+ /**
411
+ * Directory prefix to match when building the icon map.
412
+ * Defaults to `icons/`, matching standard .wyn archives.
413
+ */
414
+ prefix?: string;
415
+ };
416
+ type IconUrlEntry = {
417
+ path: string;
418
+ url: string;
419
+ };
420
+ type IconUrlMapResult = {
421
+ /**
422
+ * Map of file paths (relative to the archive root) to object URLs.
423
+ */
424
+ urls: Map<string, string>;
425
+ /**
426
+ * Revoke all allocated object URLs.
427
+ */
428
+ cleanup: () => void;
429
+ };
430
+ type EnhancedLocationsResult<T extends TerrainLocation = TerrainLocation> = {
431
+ locations: Array<T & {
432
+ iconUrl?: string;
433
+ }>;
434
+ cleanup: () => void;
435
+ };
436
+ declare function createIconUrlMap(files?: TerrainProjectFileEntry[], options?: CreateIconUrlMapOptions): IconUrlMapResult;
437
+ declare function enhanceLocationsWithIconUrls<T extends TerrainLocation = TerrainLocation>(locations: T[], files?: TerrainProjectFileEntry[], options?: CreateIconUrlMapOptions): EnhancedLocationsResult<T>;
438
+
439
+ export { type BuildWynArchiveOptions, type Cleanup, type CreateIconUrlMapOptions, type DeepPartial, type EnhancedLocationsResult, type HeightSampler, type IconUrlEntry, type IconUrlMapResult, type LayerBrowserEntry, type LayerBrowserState, type LayerBrowserStore, type LayerToggleState, type LegendLayer, type LoadWynArchiveOptions, type LoadedWynFile, type LocationPickPayload, type LocationViewState, type MarkerSpriteStateStyle, type MarkerSpriteTheme, type MarkerStemGeometryShape, type MarkerStemStateStyle, type MarkerStemTheme, type MaskEditorHandle, type MaskEditorState, type MaskImage, type MaskStroke, type MaskStrokeMode, type MaskStrokePoint, type TerrainDataset, type TerrainHandle, type TerrainLegend, type TerrainLocation, type TerrainProjectFileEntry, type TerrainProjectMetadata, type TerrainProjectSnapshot, type TerrainProjectStore, type TerrainTheme, type TerrainThemeOverrides, type TerrainViewMode, type TerrainViewerHostHandle, type TerrainViewerHostOptions, type ViewerLifecycleState, type ViewerOverlayCustomButton, type ViewerOverlayHandle, type ViewerOverlayLoadingState, type ViewerOverlayOptions, type WynArchiveProgressEvent, applyHeightField, buildRimMesh, buildWynArchive, createHeightSampler, createIconUrlMap, createLayerBrowserStore, createMaskEditor, createProjectStore, createTerrainViewerHost, createViewerOverlay, enhanceLocationsWithIconUrls, getDefaultTerrainTheme, initTerrainViewer, loadWynArchive, loadWynArchiveFromArrayBuffer, loadWynArchiveFromFile, resolveTerrainTheme, sampleHeightValue };
package/dist/index.d.ts CHANGED
@@ -406,4 +406,34 @@ type MaskEditorOptions = {
406
406
  };
407
407
  declare function createMaskEditor(options: MaskEditorOptions): MaskEditorHandle;
408
408
 
409
- export { type BuildWynArchiveOptions, type Cleanup, type DeepPartial, type HeightSampler, type LayerBrowserEntry, type LayerBrowserState, type LayerBrowserStore, type LayerToggleState, type LegendLayer, type LoadWynArchiveOptions, type LoadedWynFile, type LocationPickPayload, type LocationViewState, type MarkerSpriteStateStyle, type MarkerSpriteTheme, type MarkerStemGeometryShape, type MarkerStemStateStyle, type MarkerStemTheme, type MaskEditorHandle, type MaskEditorState, type MaskImage, type MaskStroke, type MaskStrokeMode, type MaskStrokePoint, type TerrainDataset, type TerrainHandle, type TerrainLegend, type TerrainLocation, type TerrainProjectFileEntry, type TerrainProjectMetadata, type TerrainProjectSnapshot, type TerrainProjectStore, type TerrainTheme, type TerrainThemeOverrides, type TerrainViewMode, type TerrainViewerHostHandle, type TerrainViewerHostOptions, type ViewerLifecycleState, type ViewerOverlayCustomButton, type ViewerOverlayHandle, type ViewerOverlayLoadingState, type ViewerOverlayOptions, type WynArchiveProgressEvent, applyHeightField, buildRimMesh, buildWynArchive, createHeightSampler, createLayerBrowserStore, createMaskEditor, createProjectStore, createTerrainViewerHost, createViewerOverlay, getDefaultTerrainTheme, initTerrainViewer, loadWynArchive, loadWynArchiveFromArrayBuffer, loadWynArchiveFromFile, resolveTerrainTheme, sampleHeightValue };
409
+ type CreateIconUrlMapOptions = {
410
+ /**
411
+ * Directory prefix to match when building the icon map.
412
+ * Defaults to `icons/`, matching standard .wyn archives.
413
+ */
414
+ prefix?: string;
415
+ };
416
+ type IconUrlEntry = {
417
+ path: string;
418
+ url: string;
419
+ };
420
+ type IconUrlMapResult = {
421
+ /**
422
+ * Map of file paths (relative to the archive root) to object URLs.
423
+ */
424
+ urls: Map<string, string>;
425
+ /**
426
+ * Revoke all allocated object URLs.
427
+ */
428
+ cleanup: () => void;
429
+ };
430
+ type EnhancedLocationsResult<T extends TerrainLocation = TerrainLocation> = {
431
+ locations: Array<T & {
432
+ iconUrl?: string;
433
+ }>;
434
+ cleanup: () => void;
435
+ };
436
+ declare function createIconUrlMap(files?: TerrainProjectFileEntry[], options?: CreateIconUrlMapOptions): IconUrlMapResult;
437
+ declare function enhanceLocationsWithIconUrls<T extends TerrainLocation = TerrainLocation>(locations: T[], files?: TerrainProjectFileEntry[], options?: CreateIconUrlMapOptions): EnhancedLocationsResult<T>;
438
+
439
+ export { type BuildWynArchiveOptions, type Cleanup, type CreateIconUrlMapOptions, type DeepPartial, type EnhancedLocationsResult, type HeightSampler, type IconUrlEntry, type IconUrlMapResult, type LayerBrowserEntry, type LayerBrowserState, type LayerBrowserStore, type LayerToggleState, type LegendLayer, type LoadWynArchiveOptions, type LoadedWynFile, type LocationPickPayload, type LocationViewState, type MarkerSpriteStateStyle, type MarkerSpriteTheme, type MarkerStemGeometryShape, type MarkerStemStateStyle, type MarkerStemTheme, type MaskEditorHandle, type MaskEditorState, type MaskImage, type MaskStroke, type MaskStrokeMode, type MaskStrokePoint, type TerrainDataset, type TerrainHandle, type TerrainLegend, type TerrainLocation, type TerrainProjectFileEntry, type TerrainProjectMetadata, type TerrainProjectSnapshot, type TerrainProjectStore, type TerrainTheme, type TerrainThemeOverrides, type TerrainViewMode, type TerrainViewerHostHandle, type TerrainViewerHostOptions, type ViewerLifecycleState, type ViewerOverlayCustomButton, type ViewerOverlayHandle, type ViewerOverlayLoadingState, type ViewerOverlayOptions, type WynArchiveProgressEvent, applyHeightField, buildRimMesh, buildWynArchive, createHeightSampler, createIconUrlMap, createLayerBrowserStore, createMaskEditor, createProjectStore, createTerrainViewerHost, createViewerOverlay, enhanceLocationsWithIconUrls, getDefaultTerrainTheme, initTerrainViewer, loadWynArchive, loadWynArchiveFromArrayBuffer, loadWynArchiveFromFile, resolveTerrainTheme, sampleHeightValue };
package/dist/index.js CHANGED
@@ -1604,6 +1604,9 @@ async function initTerrainViewer(container, dataset, options = {}) {
1604
1604
  },
1605
1605
  setCameraOffset: (offset, focusId) => {
1606
1606
  cameraOffset.target = THREE2.MathUtils.clamp(offset, -0.45, 0.45);
1607
+ if (cameraTween) {
1608
+ return;
1609
+ }
1607
1610
  const targetId = focusId ?? currentFocusId;
1608
1611
  if (targetId) {
1609
1612
  const loc = currentLocations.find((item) => item.id === targetId);
@@ -3193,16 +3196,75 @@ function createMaskEditor(options) {
3193
3196
  markClean
3194
3197
  };
3195
3198
  }
3199
+
3200
+ // src/iconHelpers.ts
3201
+ function inferMimeType(path, fallback = "image/png") {
3202
+ const extension = path.split(".").pop()?.toLowerCase();
3203
+ if (!extension) return fallback;
3204
+ if (extension === "png") return "image/png";
3205
+ if (extension === "jpg" || extension === "jpeg") return "image/jpeg";
3206
+ if (extension === "webp") return "image/webp";
3207
+ if (extension === "gif") return "image/gif";
3208
+ if (extension === "svg") return "image/svg+xml";
3209
+ return fallback;
3210
+ }
3211
+ function toObjectUrl(entry) {
3212
+ const type = entry.type ?? inferMimeType(entry.path);
3213
+ const blob = new Blob([entry.data], { type });
3214
+ return URL.createObjectURL(blob);
3215
+ }
3216
+ function createIconUrlMap(files, options = {}) {
3217
+ const urls = /* @__PURE__ */ new Map();
3218
+ const prefix = options.prefix ?? "icons/";
3219
+ if (!files?.length) {
3220
+ return {
3221
+ urls,
3222
+ cleanup: () => void 0
3223
+ };
3224
+ }
3225
+ for (const entry of files) {
3226
+ if (!entry.path) continue;
3227
+ if (prefix && !entry.path.startsWith(prefix)) continue;
3228
+ const url = toObjectUrl(entry);
3229
+ urls.set(entry.path, url);
3230
+ }
3231
+ const cleanup = () => {
3232
+ for (const url of urls.values()) {
3233
+ URL.revokeObjectURL(url);
3234
+ }
3235
+ urls.clear();
3236
+ };
3237
+ return { urls, cleanup };
3238
+ }
3239
+ function enhanceLocationsWithIconUrls(locations, files, options) {
3240
+ const { urls, cleanup } = createIconUrlMap(files, options);
3241
+ const enhanced = locations.map((location) => {
3242
+ if (!location.icon) {
3243
+ return { ...location };
3244
+ }
3245
+ const iconUrl = urls.get(location.icon);
3246
+ if (!iconUrl) {
3247
+ return { ...location };
3248
+ }
3249
+ return {
3250
+ ...location,
3251
+ iconUrl
3252
+ };
3253
+ });
3254
+ return { locations: enhanced, cleanup };
3255
+ }
3196
3256
  export {
3197
3257
  applyHeightField,
3198
3258
  buildRimMesh,
3199
3259
  buildWynArchive,
3200
3260
  createHeightSampler,
3261
+ createIconUrlMap,
3201
3262
  createLayerBrowserStore,
3202
3263
  createMaskEditor,
3203
3264
  createProjectStore,
3204
3265
  createTerrainViewerHost,
3205
3266
  createViewerOverlay,
3267
+ enhanceLocationsWithIconUrls,
3206
3268
  getDefaultTerrainTheme,
3207
3269
  initTerrainViewer,
3208
3270
  loadWynArchive,
@@ -0,0 +1,198 @@
1
+ # WYN File Format
2
+
3
+ ## Overview
4
+
5
+ `.wyn` files are ZIP archives containing terrain data, layer masks, and optional metadata used by
6
+ `@connected-web/terrain-editor`. The loader expects JSON descriptors plus referenced PNG assets.
7
+ All asset paths are relative to the archive root.
8
+
9
+ ## Archive Layout
10
+
11
+ Required:
12
+
13
+ ```
14
+ legend.json
15
+ ```
16
+
17
+ Common (referenced by `legend.json`):
18
+
19
+ ```
20
+ layers/
21
+ icons/
22
+ ```
23
+
24
+ Optional:
25
+
26
+ ```
27
+ locations.json
28
+ theme.json
29
+ metadata.json
30
+ thumbnails/
31
+ ```
32
+
33
+ The archive can include any additional assets as long as paths match the JSON references.
34
+
35
+ ## legend.json (required)
36
+
37
+ Describes terrain dimensions, height/topology maps, and per-layer masks.
38
+
39
+ Schema: `schemas/legend.schema.json`
40
+
41
+ ```json
42
+ {
43
+ "size": [1024, 1536],
44
+ "sea_level": 0.35,
45
+ "heightmap": "layers/heightmap.png",
46
+ "topology": "layers/topology.png",
47
+ "biomes": {
48
+ "forest": { "mask": "layers/forest_mask.png", "rgb": [48, 92, 54], "label": "Forest" }
49
+ },
50
+ "overlays": {
51
+ "water": { "mask": "layers/water_mask.png", "rgb": [34, 92, 124], "label": "Water" }
52
+ }
53
+ }
54
+ ```
55
+
56
+ Schema:
57
+
58
+ - `size`: `[width, height]` in pixels for the map and mask assets.
59
+ - `sea_level`: Optional float in 0-1 heightmap range; used to offset terrain vertically.
60
+ - `heightmap`: Path to a PNG. The viewer samples the **red channel** (0-255) as height data.
61
+ - `topology`: Optional path to a PNG used for shaded legend/composite rendering. Falls back to
62
+ `heightmap` when omitted.
63
+ - `biomes`: Record of biome layers. Keys are layer ids used by the editor/viewer.
64
+ - `overlays`: Record of overlay layers. Keys are layer ids used by the editor/viewer.
65
+
66
+ Each layer entry:
67
+
68
+ - `mask`: Path to a PNG mask. The viewer converts the max RGB channel into alpha.
69
+ - `rgb`: `[r, g, b]` integers (0-255) used to colorize the legend composite.
70
+ - `label`: Optional display name in the editor UI.
71
+
72
+ ## locations.json (optional)
73
+
74
+ Array of location markers. Coordinates are expressed in legend pixel space.
75
+
76
+ Schema: `schemas/locations.schema.json`
77
+
78
+ ```json
79
+ [
80
+ {
81
+ "id": "loc-123",
82
+ "name": "Castle",
83
+ "icon": "icons/icon_castle.png",
84
+ "description": "Seat of power.",
85
+ "pixel": { "x": 514, "y": 728 },
86
+ "showBorder": true,
87
+ "view": {
88
+ "distance": 1.82,
89
+ "polar": 0.96,
90
+ "azimuth": 1.87,
91
+ "targetPixel": { "x": 514, "y": 728 }
92
+ }
93
+ }
94
+ ]
95
+ ```
96
+
97
+ Fields:
98
+
99
+ - `id`: Stable unique id string.
100
+ - `name`: Optional label for markers.
101
+ - `icon`: Optional icon source. If the value looks like a file path or image filename, the viewer
102
+ loads it as a texture; otherwise the first character is rendered as a glyph.
103
+ - `description`: Optional freeform text (ignored by the viewer, preserved by the editor).
104
+ - `pixel`: `{ x, y }` in legend pixel space. The viewer converts to UV/world coordinates.
105
+ - `showBorder`: Optional boolean for marker label border visibility.
106
+ - `view`: Optional camera view state:
107
+ - `distance`: Camera distance from target in world units.
108
+ - `polar`: Polar angle in radians.
109
+ - `azimuth`: Azimuthal angle in radians.
110
+ - `targetPixel`: Optional pixel coordinate to orbit around.
111
+
112
+ ## theme.json (optional)
113
+
114
+ Overrides for the default terrain marker theme.
115
+
116
+ Schema: `schemas/theme.schema.json`
117
+
118
+ ```json
119
+ {
120
+ "locationMarkers": {
121
+ "sprite": {
122
+ "fontFamily": "\"DM Sans\", sans-serif",
123
+ "fontWeight": "600",
124
+ "maxFontSize": 52,
125
+ "minFontSize": 22,
126
+ "paddingX": 20,
127
+ "paddingY": 10,
128
+ "borderRadius": 4,
129
+ "states": {
130
+ "default": {
131
+ "textColor": "#ffffff",
132
+ "backgroundColor": "rgba(8, 10, 18, 0.78)",
133
+ "borderColor": "rgba(255, 255, 255, 0.35)",
134
+ "borderThickness": 2,
135
+ "opacity": 0.85
136
+ },
137
+ "hover": { "backgroundColor": "#3c49af" },
138
+ "focus": { "backgroundColor": "#cb811a" }
139
+ }
140
+ },
141
+ "stem": {
142
+ "shape": "triangle",
143
+ "radius": 0.02,
144
+ "states": {
145
+ "default": { "color": "#d9c39c", "opacity": 0.2 },
146
+ "focus": { "color": "#c00c0c", "opacity": 0.75 }
147
+ }
148
+ }
149
+ }
150
+ }
151
+ ```
152
+
153
+ Notes:
154
+
155
+ - `theme.json` is a partial override; any missing values fall back to the default theme.
156
+ - `stem.shape` supports: `cylinder`, `triangle`, `square`, `pentagon`, `hexagon`.
157
+
158
+ ## metadata.json (optional)
159
+
160
+ Basic project metadata stored by the editor.
161
+
162
+ Schema: `schemas/metadata.schema.json`
163
+
164
+ ```json
165
+ {
166
+ "label": "wynnal-terrain.wyn",
167
+ "author": "J. Markavian",
168
+ "source": "archive"
169
+ }
170
+ ```
171
+
172
+ Fields:
173
+
174
+ - `label`: Display name for the project.
175
+ - `author`: Optional author string.
176
+ - `source`: `archive` or `scratch`.
177
+
178
+ ## Assets and Conventions
179
+
180
+ - Use PNGs for all image assets. The loader infers mime types by file extension.
181
+ - All referenced assets must exist inside the archive.
182
+ - The editor stores arbitrary files referenced by the JSON in the ZIP (see `buildWynArchive`).
183
+ - `legend.size` should match the pixel dimensions of mask assets.
184
+
185
+ ## Example Archive
186
+
187
+ ```
188
+ legend.json
189
+ locations.json
190
+ theme.json
191
+ metadata.json
192
+ layers/heightmap.png
193
+ layers/topology.png
194
+ layers/forest_mask.png
195
+ layers/water_mask.png
196
+ icons/icon_castle.png
197
+ thumbnails/thumbnail.png
198
+ ```
@@ -0,0 +1,66 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://connected-web.github.io/terrain-editor/schemas/legend.schema.json",
4
+ "title": "WYN Legend",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["size", "heightmap", "biomes", "overlays"],
8
+ "properties": {
9
+ "size": {
10
+ "type": "array",
11
+ "minItems": 2,
12
+ "maxItems": 2,
13
+ "items": {
14
+ "type": "integer",
15
+ "minimum": 1
16
+ }
17
+ },
18
+ "sea_level": {
19
+ "type": "number",
20
+ "minimum": 0,
21
+ "maximum": 1
22
+ },
23
+ "heightmap": {
24
+ "type": "string",
25
+ "minLength": 1
26
+ },
27
+ "topology": {
28
+ "type": "string",
29
+ "minLength": 1
30
+ },
31
+ "biomes": {
32
+ "type": "object",
33
+ "additionalProperties": { "$ref": "#/definitions/legendLayer" }
34
+ },
35
+ "overlays": {
36
+ "type": "object",
37
+ "additionalProperties": { "$ref": "#/definitions/legendLayer" }
38
+ }
39
+ },
40
+ "definitions": {
41
+ "legendLayer": {
42
+ "type": "object",
43
+ "additionalProperties": false,
44
+ "required": ["mask", "rgb"],
45
+ "properties": {
46
+ "mask": {
47
+ "type": "string",
48
+ "minLength": 1
49
+ },
50
+ "rgb": {
51
+ "type": "array",
52
+ "minItems": 3,
53
+ "maxItems": 3,
54
+ "items": {
55
+ "type": "integer",
56
+ "minimum": 0,
57
+ "maximum": 255
58
+ }
59
+ },
60
+ "label": {
61
+ "type": "string"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://connected-web.github.io/terrain-editor/schemas/locations.schema.json",
4
+ "title": "WYN Locations",
5
+ "type": "array",
6
+ "items": { "$ref": "#/definitions/location" },
7
+ "definitions": {
8
+ "location": {
9
+ "type": "object",
10
+ "additionalProperties": false,
11
+ "required": ["id", "pixel"],
12
+ "properties": {
13
+ "id": { "type": "string", "minLength": 1 },
14
+ "name": { "type": "string" },
15
+ "icon": { "type": "string" },
16
+ "description": { "type": "string" },
17
+ "showBorder": { "type": "boolean" },
18
+ "pixel": { "$ref": "#/definitions/pixel" },
19
+ "uv": { "$ref": "#/definitions/uv" },
20
+ "world": { "$ref": "#/definitions/world" },
21
+ "view": { "$ref": "#/definitions/view" }
22
+ }
23
+ },
24
+ "pixel": {
25
+ "type": "object",
26
+ "additionalProperties": false,
27
+ "required": ["x", "y"],
28
+ "properties": {
29
+ "x": { "type": "number" },
30
+ "y": { "type": "number" }
31
+ }
32
+ },
33
+ "uv": {
34
+ "type": "object",
35
+ "additionalProperties": false,
36
+ "required": ["u", "v"],
37
+ "properties": {
38
+ "u": { "type": "number" },
39
+ "v": { "type": "number" }
40
+ }
41
+ },
42
+ "world": {
43
+ "type": "object",
44
+ "additionalProperties": false,
45
+ "required": ["x", "y", "z"],
46
+ "properties": {
47
+ "x": { "type": "number" },
48
+ "y": { "type": "number" },
49
+ "z": { "type": "number" }
50
+ }
51
+ },
52
+ "view": {
53
+ "type": "object",
54
+ "additionalProperties": false,
55
+ "required": ["distance", "polar", "azimuth"],
56
+ "properties": {
57
+ "distance": { "type": "number" },
58
+ "polar": { "type": "number" },
59
+ "azimuth": { "type": "number" },
60
+ "targetPixel": { "$ref": "#/definitions/pixel" }
61
+ }
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://connected-web.github.io/terrain-editor/schemas/metadata.schema.json",
4
+ "title": "WYN Metadata",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "properties": {
8
+ "label": { "type": "string" },
9
+ "author": { "type": "string" },
10
+ "source": { "type": "string", "enum": ["archive", "scratch"] }
11
+ }
12
+ }
@@ -0,0 +1,83 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://connected-web.github.io/terrain-editor/schemas/theme.schema.json",
4
+ "title": "WYN Theme Overrides",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "properties": {
8
+ "locationMarkers": {
9
+ "type": "object",
10
+ "additionalProperties": false,
11
+ "properties": {
12
+ "sprite": { "$ref": "#/definitions/sprite" },
13
+ "stem": { "$ref": "#/definitions/stem" }
14
+ }
15
+ }
16
+ },
17
+ "definitions": {
18
+ "sprite": {
19
+ "type": "object",
20
+ "additionalProperties": false,
21
+ "properties": {
22
+ "fontFamily": { "type": "string" },
23
+ "fontWeight": { "type": "string" },
24
+ "maxFontSize": { "type": "number" },
25
+ "minFontSize": { "type": "number" },
26
+ "paddingX": { "type": "number" },
27
+ "paddingY": { "type": "number" },
28
+ "borderRadius": { "type": "number" },
29
+ "states": { "$ref": "#/definitions/spriteStates" }
30
+ }
31
+ },
32
+ "spriteStates": {
33
+ "type": "object",
34
+ "additionalProperties": false,
35
+ "properties": {
36
+ "default": { "$ref": "#/definitions/spriteState" },
37
+ "hover": { "$ref": "#/definitions/spriteState" },
38
+ "focus": { "$ref": "#/definitions/spriteState" }
39
+ }
40
+ },
41
+ "spriteState": {
42
+ "type": "object",
43
+ "additionalProperties": false,
44
+ "properties": {
45
+ "textColor": { "type": "string" },
46
+ "backgroundColor": { "type": "string" },
47
+ "borderColor": { "type": "string" },
48
+ "borderThickness": { "type": "number" },
49
+ "opacity": { "type": "number" }
50
+ }
51
+ },
52
+ "stem": {
53
+ "type": "object",
54
+ "additionalProperties": false,
55
+ "properties": {
56
+ "shape": {
57
+ "type": "string",
58
+ "enum": ["cylinder", "triangle", "square", "pentagon", "hexagon"]
59
+ },
60
+ "radius": { "type": "number" },
61
+ "scale": { "type": "number" },
62
+ "states": { "$ref": "#/definitions/stemStates" }
63
+ }
64
+ },
65
+ "stemStates": {
66
+ "type": "object",
67
+ "additionalProperties": false,
68
+ "properties": {
69
+ "default": { "$ref": "#/definitions/stemState" },
70
+ "hover": { "$ref": "#/definitions/stemState" },
71
+ "focus": { "$ref": "#/definitions/stemState" }
72
+ }
73
+ },
74
+ "stemState": {
75
+ "type": "object",
76
+ "additionalProperties": false,
77
+ "properties": {
78
+ "color": { "type": "string" },
79
+ "opacity": { "type": "number" }
80
+ }
81
+ }
82
+ }
83
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@connected-web/terrain-editor",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Reusable viewer/editor utilities for Wyn terrain files.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -11,14 +11,17 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.js",
13
13
  "require": "./dist/index.cjs"
14
- }
14
+ },
15
+ "./documentation/*": "./documentation/*",
16
+ "./schemas/*": "./documentation/schemas/*"
15
17
  },
16
18
  "files": [
17
19
  "dist",
20
+ "documentation",
18
21
  "README.md"
19
22
  ],
20
23
  "scripts": {
21
- "build": "tsup src/index.ts --dts --format cjs,esm --out-dir dist --clean --tsconfig tsconfig.json"
24
+ "build": "node ./scripts/copy-docs.mjs && tsup src/index.ts --dts --format cjs,esm --out-dir dist --clean --tsconfig tsconfig.json"
22
25
  },
23
26
  "dependencies": {
24
27
  "jszip": "^3.10.1",
@@ -31,5 +34,9 @@
31
34
  "three": "^0.181.1"
32
35
  },
33
36
  "license": "ISC",
34
- "author": "Connected Web"
37
+ "author": "Connected Web",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/connected-web/terrain-editor.git"
41
+ }
35
42
  }