diff --git a/index.html b/index.html
index 012df0bc..6c26b3b5 100644
--- a/index.html
+++ b/index.html
@@ -1858,18 +1858,6 @@
|
-
-
@@ -3957,18 +3945,20 @@
-
-
+ href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-template-editor"
+ target="_blank"
+ >
@@ -7698,7 +7689,6 @@
-
diff --git a/libs/lineclip.min.js b/libs/lineclip.min.js
deleted file mode 100644
index d1796476..00000000
--- a/libs/lineclip.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// lineclip by mourner, https://github.com/mapbox/lineclip
-"use strict";function lineclip(t,e,n){var r,i,u,o,s,h=t.length,c=bitCode(t[0],e),f=[];for(n=n||[],r=1;re[2]&&(n|=2),t[1]e[3]&&(n|=8),n}
\ No newline at end of file
diff --git a/modules/burgs-and-states.js b/modules/burgs-and-states.js
index dbdfd184..7f535254 100644
--- a/modules/burgs-and-states.js
+++ b/modules/burgs-and-states.js
@@ -2,6 +2,7 @@ import {TIME} from "/src/config/logging";
import {findCell} from "/src/utils/graphUtils";
import {layerIsOn} from "./ui/layers";
import {getColors, getRandomColor, getMixedColor} from "/src/utils/colorUtils";
+import {getMiddlePoint} from "@/utils/lineUtils";
import {rn, minmax} from "/src/utils/numberUtils";
window.BurgsAndStates = (function () {
diff --git a/modules/define-globals.js b/modules/define-globals.js
index 4b7ab2b2..eaa4c9e3 100644
--- a/modules/define-globals.js
+++ b/modules/define-globals.js
@@ -25,7 +25,6 @@ let svgWidth;
let svgHeight;
let options = {};
-let mapCoordinates = {};
let populationRate;
let distanceScale;
let urbanization;
diff --git a/modules/io/export.js b/modules/io/export.js
index 17432728..f68c84ee 100644
--- a/modules/io/export.js
+++ b/modules/io/export.js
@@ -1,6 +1,7 @@
import {getGridPolygon} from "/src/utils/graphUtils";
import {unique} from "/src/utils/arrayUtils";
import {tip} from "/src/scripts/tooltips";
+import {getCoordinates} from "@/utils/coordinateUtils";
import {rn} from "/src/utils/numberUtils";
// download map as SVG
diff --git a/modules/io/load.js b/modules/io/load.js
index 5c8c8a82..0e61a9b8 100644
--- a/modules/io/load.js
+++ b/modules/io/load.js
@@ -2,7 +2,10 @@ import {restoreDefaultEvents} from "/src/scripts/events";
import {calculateVoronoi, findCell} from "/src/utils/graphUtils";
import {last} from "/src/utils/arrayUtils";
import {tip} from "/src/scripts/tooltips";
+import {parseError} from "@/utils/errorUtils";
import {rn, minmax} from "/src/utils/numberUtils";
+import {link} from "@/utils/linkUtils";
+import {ldb} from "@/scripts/indexedDB";
function quickLoad() {
ldb.get("lastMap", blob => {
diff --git a/modules/io/save.js b/modules/io/save.js
index 2aadf6db..4b6a0e68 100644
--- a/modules/io/save.js
+++ b/modules/io/save.js
@@ -1,5 +1,6 @@
import {tip} from "/src/scripts/tooltips";
import {rn} from "/src/utils/numberUtils";
+import {ldb} from "@/scripts/indexedDB";
// functions to save project as .map file
diff --git a/modules/markers-generator.js b/modules/markers-generator.js
index 3bfc0006..1e9d6888 100644
--- a/modules/markers-generator.js
+++ b/modules/markers-generator.js
@@ -562,6 +562,14 @@ window.Markers = (function () {
notes.push({id, name, legend});
}
+ function generateDate(from = 100, to = 1000) {
+ return new Date(rand(from, to), rand(12), rand(31)).toLocaleDateString("en", {
+ year: "numeric",
+ month: "long",
+ day: "numeric"
+ });
+ }
+
function listDungeons({cells}) {
return cells.i.filter(i => !occupied[i] && cells.pop[i] && cells.pop[i] < 3);
}
diff --git a/modules/ocean-layers.js b/modules/ocean-layers.js
index 2cbba31c..578e58dd 100644
--- a/modules/ocean-layers.js
+++ b/modules/ocean-layers.js
@@ -1,5 +1,6 @@
-import {TIME} from "/src/config/logging";
-import {rn} from "/src/utils/numberUtils";
+import {TIME} from "@/config/logging";
+import {clipPoly} from "@/utils/lineUtils";
+import {rn} from "@/utils/numberUtils";
window.OceanLayers = (function () {
let cells, vertices, pointsN, used;
diff --git a/modules/submap.js b/modules/submap.js
index 5fb3668f..7045669d 100644
--- a/modules/submap.js
+++ b/modules/submap.js
@@ -1,4 +1,5 @@
import {findCell} from "/src/utils/graphUtils";
+import {getMiddlePoint} from "@/utils/lineUtils";
import {rn} from "/src/utils/numberUtils";
window.Submap = (function () {
diff --git a/modules/ui/battle-screen.js b/modules/ui/battle-screen.js
index 81f51b15..99de89a0 100644
--- a/modules/ui/battle-screen.js
+++ b/modules/ui/battle-screen.js
@@ -1,5 +1,6 @@
import {last} from "/src/utils/arrayUtils";
import {tip} from "/src/scripts/tooltips";
+import {wiki} from "@/utils/linkUtils";
import {rn, minmax} from "/src/utils/numberUtils";
export class Battle {
diff --git a/modules/ui/biomes-editor.js b/modules/ui/biomes-editor.js
index 342d05a5..9014f569 100644
--- a/modules/ui/biomes-editor.js
+++ b/modules/ui/biomes-editor.js
@@ -2,6 +2,7 @@ import {restoreDefaultEvents} from "/src/scripts/events";
import {findAll, findCell, getPackPolygon, isLand} from "/src/utils/graphUtils";
import {tip, showMainTip, clearMainTip} from "/src/scripts/tooltips";
import {getRandomColor} from "/src/utils/colorUtils";
+import {openURL} from "@/utils/linkUtils";
import {rn} from "/src/utils/numberUtils";
export function editBiomes() {
diff --git a/modules/ui/burg-editor.js b/modules/ui/burg-editor.js
index 5f153879..bd8d2f2a 100644
--- a/modules/ui/burg-editor.js
+++ b/modules/ui/burg-editor.js
@@ -1,6 +1,7 @@
import {findCell} from "/src/utils/graphUtils";
import {tip, clearMainTip} from "/src/scripts/tooltips";
import {rn} from "/src/utils/numberUtils";
+import {prompt} from "@/scripts/prompt";
export function editBurg(id) {
if (customization) return;
diff --git a/modules/ui/burgs-overview.js b/modules/ui/burgs-overview.js
index 5903391e..abd435ca 100644
--- a/modules/ui/burgs-overview.js
+++ b/modules/ui/burgs-overview.js
@@ -1,6 +1,7 @@
import {restoreDefaultEvents} from "/src/scripts/events";
import {findCell} from "/src/utils/graphUtils";
import {tip, clearMainTip} from "/src/scripts/tooltips";
+import {getCoordinates} from "@/utils/coordinateUtils";
import {rn} from "/src/utils/numberUtils";
export function overviewBurgs() {
@@ -500,8 +501,9 @@ export function overviewBurgs() {
data += rn(b.population * populationRate * urbanization) + ",";
// add geography data
- data += getLatitude(b.y, 2) + ",";
- data += getLongitude(b.x, 2) + ",";
+ const [lon, lat] = getCoordinates(b.x, b.y, 2);
+ data += lat + ",";
+ data += lon + ",";
data += parseInt(getHeight(pack.cells.h[b.cell])) + ",";
// add status data
diff --git a/modules/ui/coastline-editor.js b/modules/ui/coastline-editor.js
index 228e242b..e9073c0d 100644
--- a/modules/ui/coastline-editor.js
+++ b/modules/ui/coastline-editor.js
@@ -1,5 +1,6 @@
import {getPackPolygon} from "/src/utils/graphUtils";
import {tip} from "/src/scripts/tooltips";
+import {clipPoly} from "@/utils/lineUtils";
import {rn} from "/src/utils/numberUtils";
export function editCoastline(node = d3.event.target) {
diff --git a/modules/ui/emblems-editor.js b/modules/ui/emblems-editor.js
index 1a1b0b1a..3516706b 100644
--- a/modules/ui/emblems-editor.js
+++ b/modules/ui/emblems-editor.js
@@ -1,5 +1,6 @@
import {clearMainTip} from "/src/scripts/tooltips";
import {tip} from "/src/scripts/tooltips";
+import {openURL} from "@/utils/linkUtils";
import {rn} from "/src/utils/numberUtils";
export function editEmblem(type, id, el) {
diff --git a/modules/ui/general.js b/modules/ui/general.js
index 768322af..645c1cbd 100644
--- a/modules/ui/general.js
+++ b/modules/ui/general.js
@@ -1,5 +1,7 @@
import {findCell, findGridCell} from "/src/utils/graphUtils";
import {rn} from "/src/utils/numberUtils";
+import {link} from "@/utils/linkUtils";
+import {getCoordinates, toDMS} from "@/utils/coordinateUtils";
// fit full-screen map if window is resized
window.addEventListener("resize", function (e) {
@@ -29,8 +31,10 @@ function updateCellInfo(point, i, g) {
const x = (infoX.innerHTML = rn(point[0]));
const y = (infoY.innerHTML = rn(point[1]));
const f = cells.f[i];
- infoLat.innerHTML = toDMS(getLatitude(y, 4), "lat");
- infoLon.innerHTML = toDMS(getLongitude(x, 4), "lon");
+
+ const [lon, lat] = getCoordinates(x, y, 4);
+ infoLat.innerHTML = toDMS(lat, "lat");
+ infoLon.innerHTML = toDMS(lon, "lon");
infoCell.innerHTML = i;
infoArea.innerHTML = cells.area[i] ? si(getArea(cells.area[i])) + " " + getAreaUnit() : "n/a";
@@ -58,16 +62,6 @@ function updateCellInfo(point, i, g) {
infoBiome.innerHTML = biomesData.name[cells.biome[i]];
}
-// convert coordinate to DMS format
-function toDMS(coord, c) {
- const degrees = Math.floor(Math.abs(coord));
- const minutesNotTruncated = (Math.abs(coord) - degrees) * 60;
- const minutes = Math.floor(minutesNotTruncated);
- const seconds = Math.floor((minutesNotTruncated - minutes) * 60);
- const cardinal = c === "lat" ? (coord >= 0 ? "N" : "S") : coord >= 0 ? "E" : "W";
- return degrees + "° " + minutes + "′ " + seconds + "″ " + cardinal;
-}
-
// get surface elevation
function getElevation(f, h) {
if (f.land) return getHeight(h) + " (" + h + ")"; // land: usual height
diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js
index fa72a78f..ef16756b 100644
--- a/modules/ui/heightmap-editor.js
+++ b/modules/ui/heightmap-editor.js
@@ -4,6 +4,8 @@ import {last, createTypedArray} from "/src/utils/arrayUtils";
import {tip, showMainTip, clearMainTip} from "/src/scripts/tooltips";
import {byId} from "/src/utils/shorthands";
import {rn, minmax, lim} from "/src/utils/numberUtils";
+import {link} from "@/utils/linkUtils";
+import {prompt} from "@/scripts/prompt";
export function editHeightmap(options) {
const {mode, tool} = options || {};
diff --git a/modules/ui/layers.js b/modules/ui/layers.js
index 9362d363..a4fec778 100644
--- a/modules/ui/layers.js
+++ b/modules/ui/layers.js
@@ -5,7 +5,10 @@ import {last} from "/src/utils/arrayUtils";
import {stored, store} from "/src/utils/shorthands";
import {tip} from "/src/scripts/tooltips";
import {byId} from "/src/utils/shorthands";
+import {clipPoly} from "@/utils/lineUtils";
import {rn, minmax, normalize} from "/src/utils/numberUtils";
+import {isCtrlClick} from "@/utils/keyboardUtils";
+import {prompt} from "@/scripts/prompt";
let presets = {};
restoreCustomPresets(); // run on-load
diff --git a/modules/ui/military-overview.js b/modules/ui/military-overview.js
index 41b17563..1644b3aa 100644
--- a/modules/ui/military-overview.js
+++ b/modules/ui/military-overview.js
@@ -1,4 +1,5 @@
import {tip} from "/src/scripts/tooltips";
+import {wiki} from "@/utils/linkUtils";
import {rn} from "/src/utils/numberUtils";
export function overviewMilitary() {
diff --git a/modules/ui/namesbase-editor.js b/modules/ui/namesbase-editor.js
index 61a71015..7e3ee07f 100644
--- a/modules/ui/namesbase-editor.js
+++ b/modules/ui/namesbase-editor.js
@@ -1,5 +1,6 @@
import {unique} from "/src/utils/arrayUtils";
import {tip} from "/src/scripts/tooltips";
+import {openURL} from "@/utils/linkUtils";
import {rn} from "/src/utils/numberUtils";
export function editNamesbase() {
diff --git a/modules/ui/rivers-editor.js b/modules/ui/rivers-editor.js
index 4deeb4ae..c0003183 100644
--- a/modules/ui/rivers-editor.js
+++ b/modules/ui/rivers-editor.js
@@ -1,5 +1,6 @@
import {findCell, getPackPolygon} from "/src/utils/graphUtils";
import {tip, clearMainTip} from "/src/scripts/tooltips";
+import {getSegmentId} from "@/utils/lineUtils";
import {rn} from "/src/utils/numberUtils";
export function editRiver(id) {
diff --git a/modules/ui/routes-editor.js b/modules/ui/routes-editor.js
index d5427d57..d4d562b5 100644
--- a/modules/ui/routes-editor.js
+++ b/modules/ui/routes-editor.js
@@ -1,4 +1,5 @@
import {tip, showMainTip, clearMainTip} from "/src/scripts/tooltips";
+import {getSegmentId} from "@/utils/lineUtils";
import {rn} from "/src/utils/numberUtils";
export function editRoute(onClick) {
diff --git a/modules/ui/submap.js b/modules/ui/submap.js
index ef79a1bf..d5ad6054 100644
--- a/modules/ui/submap.js
+++ b/modules/ui/submap.js
@@ -1,5 +1,6 @@
import {byId} from "/src/utils/shorthands";
import {clearMainTip} from "/src/scripts/tooltips";
+import {parseError} from "@/utils/errorUtils";
import {rn, minmax} from "/src/utils/numberUtils";
window.UISubmap = (function () {
diff --git a/modules/ui/tools.js b/modules/ui/tools.js
index e1fff299..050d62df 100644
--- a/modules/ui/tools.js
+++ b/modules/ui/tools.js
@@ -3,8 +3,8 @@ import {findCell} from "/src/utils/graphUtils";
import {last} from "/src/utils/arrayUtils";
import {tip, clearMainTip} from "/src/scripts/tooltips";
import {rn} from "/src/utils/numberUtils";
-
-// module to control the Tools options (click to edit, to re-geenerate, tp add)
+import {isCtrlClick} from "@/utils/keyboardUtils";
+import {prompt} from "@/scripts/prompt";
toolsContent.addEventListener("click", function (event) {
if (customization) return tip("Please exit the customization mode first", false, "warning");
diff --git a/modules/ui/units-editor.js b/modules/ui/units-editor.js
index 4e94e03b..db08ee8b 100644
--- a/modules/ui/units-editor.js
+++ b/modules/ui/units-editor.js
@@ -1,6 +1,7 @@
import {restoreDefaultEvents} from "/src/scripts/events";
import {findCell} from "/src/utils/graphUtils";
import {tip} from "/src/scripts/tooltips";
+import {prompt} from "@/scripts/prompt";
export function editUnits() {
closeDialogs("#unitsEditor, .stable");
diff --git a/package.json b/package.json
index ebb5e08c..5065b242 100644
--- a/package.json
+++ b/package.json
@@ -10,5 +10,8 @@
"devDependencies": {
"typescript": "^4.7.4",
"vite": "^2.9.12"
+ },
+ "dependencies": {
+ "lineclip": "^1.1.5"
}
}
diff --git a/src/main.ts b/src/main.ts
index 83b25f64..4f245349 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -21,8 +21,10 @@ import {
isLand,
shouldRegenerateGrid
} from "./utils/graphUtils";
+import {parseError} from "@/utils/errorUtils";
import {rn, minmax, normalize} from "./utils/numberUtils";
import {createTypedArray} from "./utils/arrayUtils";
+import {clipPoly} from "@/utils/lineUtils";
import {byId} from "./utils/shorthands";
import "./components";
@@ -41,7 +43,7 @@ options = {
winds: [225, 45, 225, 315, 135, 315],
stateLabelsMode: "auto"
};
-mapCoordinates = {}; // map coordinates on globe
+
populationRate = +byId("populationRateInput").value;
distanceScale = +byId("distanceScaleInput").value;
urbanization = +byId("urbanizationInput").value;
@@ -368,7 +370,7 @@ async function generate(options) {
OceanLayers();
defineMapSize();
- calculateMapCoordinates();
+ window.mapCoordinates = calculateMapCoordinates();
calculateTemperatures();
generatePrecipitation();
@@ -674,7 +676,7 @@ function defineMapSize() {
}
// calculate map position on globe
-function calculateMapCoordinates() {
+function calculateMapCoordinates(): IMapCoordinates {
const size = +byId("mapSizeOutput").value;
const latShift = +byId("latitudeOutput").value;
@@ -683,7 +685,7 @@ function calculateMapCoordinates() {
const latS = rn(latN - latT, 1);
const lon = rn(Math.min(((graphWidth / graphHeight) * latT) / 2, 180));
- mapCoordinates = {latT, latN, latS, lonT: lon * 2, lonW: -lon, lonE: lon};
+ return {latT, latN, latS, lonT: lon * 2, lonW: -lon, lonE: lon};
}
// temperature model
diff --git a/src/modules/measurers.js b/src/modules/measurers.js
index fb2f75e9..21c98294 100644
--- a/src/modules/measurers.js
+++ b/src/modules/measurers.js
@@ -1,5 +1,6 @@
import {findCell} from "/src/utils/graphUtils";
import {last} from "/src/utils/arrayUtils";
+import {getSegmentId} from "@/utils/lineUtils";
import {rn} from "/src/utils/numberUtils";
export class Rulers {
diff --git a/src/scripts/indexedDB.js b/src/scripts/indexedDB.js
new file mode 100644
index 00000000..b69e6802
--- /dev/null
+++ b/src/scripts/indexedDB.js
@@ -0,0 +1,50 @@
+// indexedDB support: ldb object
+
+// @ts-ignore unimplemented historical interfaces
+const indexedDBfactory = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
+if (!indexedDBfactory) console.error("indexedDB not supported");
+
+let database;
+const databaseRequest = indexedDBfactory.open("d2", 1);
+
+databaseRequest.onsuccess = function () {
+ database = this.result;
+};
+
+databaseRequest.onerror = function (e) {
+ console.error("indexedDB request error", e);
+};
+
+databaseRequest.onupgradeneeded = function (event) {
+ database = null;
+ const store = databaseRequest.result.createObjectStore("s", {keyPath: "k"});
+ store.transaction.oncomplete = function (e) {
+ database = e.target.db;
+ };
+};
+
+function getValue(key, callback) {
+ if (!database) {
+ setTimeout(() => getValue(key, callback), 100);
+ return;
+ }
+
+ database.transaction("s").objectStore("s").get(key).onsuccess = function (e) {
+ const value = (e.target.result && e.target.result.v) || null;
+ callback(value);
+ };
+}
+
+function setValue(key) {
+ if (!database) {
+ setTimeout(() => setValue(key, value), 100);
+ return;
+ }
+
+ database
+ .transaction("s", "readwrite")
+ .objectStore("s")
+ .put({[key]: value});
+}
+
+export const ldb = {get: getValue, set: setValue};
diff --git a/src/scripts/prompt.ts b/src/scripts/prompt.ts
new file mode 100644
index 00000000..387d5f43
--- /dev/null
+++ b/src/scripts/prompt.ts
@@ -0,0 +1,44 @@
+import {ERROR} from "@/config/logging";
+
+// prompt replacer (prompt does not work in Electron)
+const $prompt: HTMLElement = document.getElementById("prompt")!;
+const $form: HTMLFormElement = $prompt.querySelector("#promptForm")!;
+const $input: HTMLInputElement = $prompt.querySelector("#promptInput")!;
+const $text: HTMLDivElement = $prompt.querySelector("#promptText")!;
+const $cancel: HTMLButtonElement = $prompt.querySelector("#promptCancel")!;
+
+const defaultText = "Please provide an input";
+const defaultOptions = {default: 1, step: 0.01, min: 0, max: 100, required: true};
+
+export function prompt(promptText = defaultText, options = defaultOptions, callback: (value: number | string) => void) {
+ if (options.default === undefined)
+ return ERROR && console.error("Prompt: options object does not have default value defined");
+
+ $text.innerHTML = promptText;
+ $input.type = typeof options.default === "number" ? "number" : "text";
+
+ if (options.step !== undefined) $input.step = String(options.step);
+ if (options.min !== undefined) $input.min = String(options.min);
+ if (options.max !== undefined) $input.max = String(options.max);
+
+ $input.required = options.required === false ? false : true;
+ $input.placeholder = "type a " + $input.type;
+ $input.value = String(options.default);
+ $prompt.style.display = "block";
+
+ $form.addEventListener(
+ "submit",
+ event => {
+ event.preventDefault();
+ $prompt.style.display = "none";
+
+ const value = $input.type === "number" ? Number($input.value) : $input.value;
+ if (callback) callback(value);
+ },
+ {once: true}
+ );
+}
+
+$cancel.addEventListener("click", () => {
+ $prompt.style.display = "none";
+});
diff --git a/src/types/common.d.ts b/src/types/common.d.ts
new file mode 100644
index 00000000..3d2fe1b3
--- /dev/null
+++ b/src/types/common.d.ts
@@ -0,0 +1 @@
+type UnknownObject = {[key: string]: unknown};
diff --git a/src/types/coordinates.d.ts b/src/types/coordinates.d.ts
new file mode 100644
index 00000000..0c29728a
--- /dev/null
+++ b/src/types/coordinates.d.ts
@@ -0,0 +1,8 @@
+interface IMapCoordinates {
+ latT: number;
+ latN: number;
+ latS: number;
+ lonT: number;
+ lonW: number;
+ lonE: number;
+}
diff --git a/src/types/modules.d.ts b/src/types/modules.d.ts
new file mode 100644
index 00000000..bfe96291
--- /dev/null
+++ b/src/types/modules.d.ts
@@ -0,0 +1,4 @@
+declare module "lineclip" {
+ export function polygon(points: number[][], bbox: number[], result?: number[][]): number[][];
+ export function lineclip(points: number[][], bbox: number[]): number[][];
+}
diff --git a/src/types/global.d.ts b/src/types/overrides.d.ts
similarity index 84%
rename from src/types/global.d.ts
rename to src/types/overrides.d.ts
index 6ceb3955..40f59a49 100644
--- a/src/types/global.d.ts
+++ b/src/types/overrides.d.ts
@@ -13,6 +13,9 @@ interface Window {
pack: IPack;
grig: IGrid;
d3: typeof d3;
+ graphHeight: number;
+ graphWidth: number;
+ mapCoordinates: IMapCoordinates;
}
interface Node {
diff --git a/src/types/point.d.ts b/src/types/point.d.ts
new file mode 100644
index 00000000..0c0bd0a8
--- /dev/null
+++ b/src/types/point.d.ts
@@ -0,0 +1,3 @@
+type TPoint = [number, number];
+
+type TPoints = TPoint[];
diff --git a/src/utils/coordinateUtils.ts b/src/utils/coordinateUtils.ts
new file mode 100644
index 00000000..b7c86d40
--- /dev/null
+++ b/src/utils/coordinateUtils.ts
@@ -0,0 +1,25 @@
+import {rn} from "./numberUtils";
+
+const {mapCoordinates, graphWidth, graphHeight} = window;
+
+function getLongitude(x: number, decimals = 2) {
+ return rn(mapCoordinates.lonW + (x / graphWidth) * mapCoordinates.lonT, decimals);
+}
+
+function getLatitude(y: number, decimals = 2) {
+ return rn(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT, decimals);
+}
+
+export function getCoordinates(x: number, y: number, decimals = 2) {
+ return [getLongitude(x, decimals), getLatitude(y, decimals)];
+}
+
+// convert coordinate to DMS format
+export function toDMS(coord: number, type: "lat" | "lon") {
+ const degrees = Math.floor(Math.abs(coord));
+ const minutesNotTruncated = (Math.abs(coord) - degrees) * 60;
+ const minutes = Math.floor(minutesNotTruncated);
+ const seconds = Math.floor((minutesNotTruncated - minutes) * 60);
+ const cardinal = type === "lat" ? (coord >= 0 ? "N" : "S") : coord >= 0 ? "E" : "W";
+ return `${degrees}° ${minutes}′ ${seconds}″ ${cardinal}`;
+}
diff --git a/src/utils/errorUtils.ts b/src/utils/errorUtils.ts
new file mode 100644
index 00000000..d385ae3f
--- /dev/null
+++ b/src/utils/errorUtils.ts
@@ -0,0 +1,8 @@
+// parse error to get the readable string
+export function parseError(error: Error) {
+ const errorString = error.toString() + " " + error.stack;
+ const regex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi;
+ const errorNoURL = errorString.replace(regex, url => "" + url.split("/").at(-1) + "");
+ const errorParsed = errorNoURL.replace(/at /gi, "
at ");
+ return errorParsed;
+}
diff --git a/src/utils/keyboardUtils.ts b/src/utils/keyboardUtils.ts
new file mode 100644
index 00000000..cadd4a5c
--- /dev/null
+++ b/src/utils/keyboardUtils.ts
@@ -0,0 +1,3 @@
+export function isCtrlClick(event: MouseEvent) {
+ return event.ctrlKey || event.metaKey;
+}
diff --git a/src/utils/lineUtils.ts b/src/utils/lineUtils.ts
new file mode 100644
index 00000000..99168c92
--- /dev/null
+++ b/src/utils/lineUtils.ts
@@ -0,0 +1,56 @@
+import {polygon} from "lineclip";
+
+const {graphWidth, graphHeight, pack} = window;
+
+// clip polygon by graph bbox
+export function clipPoly(points: TPoints) {
+ return polygon(points, [0, 0, graphWidth, graphHeight]);
+}
+
+// get segment of any point on polyline
+export function getSegmentId(points: TPoints, point: TPoint, step = 10) {
+ if (points.length === 2) return 1;
+ const d2 = (p1: TPoint, p2: TPoint) => (p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2;
+
+ let minSegment = 1;
+ let minDist = Infinity;
+
+ for (let i = 0; i < points.length - 1; i++) {
+ const p1 = points[i];
+ const p2 = points[i + 1];
+
+ const length = Math.sqrt(d2(p1, p2));
+ const segments = Math.ceil(length / step);
+ const dx = (p2[0] - p1[0]) / segments;
+ const dy = (p2[1] - p1[1]) / segments;
+
+ for (let s = 0; s < segments; s++) {
+ const x = p1[0] + s * dx;
+ const y = p1[1] + s * dy;
+ const dist2 = d2(point, [x, y]);
+
+ if (dist2 >= minDist) continue;
+ minDist = dist2;
+ minSegment = i + 1;
+ }
+ }
+
+ return minSegment;
+}
+
+// return center point of common edge of 2 pack cells
+export function getMiddlePoint(cell1: number, cell2: number) {
+ const {cells, vertices} = pack;
+
+ const commonVertices = cells.v[cell1].filter((vertex: number) =>
+ vertices.c[vertex].some((cellId: number) => cellId === cell2)
+ );
+
+ const [x1, y1] = vertices.p[commonVertices[0]];
+ const [x2, y2] = vertices.p[commonVertices[1]];
+
+ const x = (x1 + x2) / 2;
+ const y = (y1 + y2) / 2;
+
+ return [x, y];
+}
diff --git a/src/utils/linkUtils.ts b/src/utils/linkUtils.ts
new file mode 100644
index 00000000..67f43678
--- /dev/null
+++ b/src/utils/linkUtils.ts
@@ -0,0 +1,14 @@
+// open URL in a new tab or window
+export function openURL(url: string) {
+ window.open(url, "_blank");
+}
+
+// open project wiki-page
+export function wiki(page: string) {
+ window.open("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/" + page, "_blank");
+}
+
+// wrap URL into html anchor element
+export function link(url: string, text: string) {
+ return `${text}`;
+}
diff --git a/utils/commonUtils.js b/utils/commonUtils.js
deleted file mode 100644
index c18fb7b1..00000000
--- a/utils/commonUtils.js
+++ /dev/null
@@ -1,236 +0,0 @@
-import {rn} from "/src/utils/numberUtils";
-
-// clip polygon by graph bbox
-function clipPoly(points, secure = 0) {
- return polygonclip(points, [0, 0, graphWidth, graphHeight], secure);
-}
-
-// get segment of any point on polyline
-function getSegmentId(points, point, step = 10) {
- if (points.length === 2) return 1;
- const d2 = (p1, p2) => (p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2;
-
- let minSegment = 1;
- let minDist = Infinity;
-
- for (let i = 0; i < points.length - 1; i++) {
- const p1 = points[i];
- const p2 = points[i + 1];
-
- const length = Math.sqrt(d2(p1, p2));
- const segments = Math.ceil(length / step);
- const dx = (p2[0] - p1[0]) / segments;
- const dy = (p2[1] - p1[1]) / segments;
-
- for (let s = 0; s < segments; s++) {
- const x = p1[0] + s * dx;
- const y = p1[1] + s * dy;
- const dist2 = d2(point, [x, y]);
-
- if (dist2 >= minDist) continue;
- minDist = dist2;
- minSegment = i + 1;
- }
- }
-
- return minSegment;
-}
-
-// return center point of common edge of 2 pack cells
-function getMiddlePoint(cell1, cell2) {
- const {cells, vertices} = pack;
-
- const commonVertices = cells.v[cell1].filter(vertex => vertices.c[vertex].some(cell => cell === cell2));
- const [x1, y1] = vertices.p[commonVertices[0]];
- const [x2, y2] = vertices.p[commonVertices[1]];
-
- const x = (x1 + x2) / 2;
- const y = (y1 + y2) / 2;
-
- return [x, y];
-}
-
-function debounce(func, ms) {
- let isCooldown = false;
-
- return function () {
- if (isCooldown) return;
- func.apply(this, arguments);
- isCooldown = true;
- setTimeout(() => (isCooldown = false), ms);
- };
-}
-
-function throttle(func, ms) {
- let isThrottled = false;
- let savedArgs;
- let savedThis;
-
- function wrapper() {
- if (isThrottled) {
- savedArgs = arguments;
- savedThis = this;
- return;
- }
-
- func.apply(this, arguments);
- isThrottled = true;
-
- setTimeout(function () {
- isThrottled = false;
- if (savedArgs) {
- wrapper.apply(savedThis, savedArgs);
- savedArgs = savedThis = null;
- }
- }, ms);
- }
-
- return wrapper;
-}
-
-// parse error to get the readable string in Chrome and Firefox
-function parseError(error) {
- const isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
- const errorString = isFirefox ? error.toString() + " " + error.stack : error.stack;
- const regex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi;
- const errorNoURL = errorString.replace(regex, url => "" + url.split("/").at(-1) + "");
- const errorParsed = errorNoURL.replace(/at /gi, "
at ");
- return errorParsed;
-}
-
-function getBase64(url, callback) {
- const xhr = new XMLHttpRequest();
- xhr.onload = function () {
- const reader = new FileReader();
- reader.onloadend = function () {
- callback(reader.result);
- };
- reader.readAsDataURL(xhr.response);
- };
- xhr.open("GET", url);
- xhr.responseType = "blob";
- xhr.send();
-}
-
-// open URL in a new tab or window
-function openURL(url) {
- window.open(url, "_blank");
-}
-
-// open project wiki-page
-function wiki(page) {
- window.open("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/" + page, "_blank");
-}
-
-// wrap URL into html a element
-function link(URL, description) {
- return `${description}`;
-}
-
-function isCtrlClick(event) {
- // meta key is cmd key on MacOs
- return event.ctrlKey || event.metaKey;
-}
-
-function generateDate(from = 100, to = 1000) {
- return new Date(rand(from, to), rand(12), rand(31)).toLocaleDateString("en", {
- year: "numeric",
- month: "long",
- day: "numeric"
- });
-}
-
-function getLongitude(x, decimals = 2) {
- return rn(mapCoordinates.lonW + (x / graphWidth) * mapCoordinates.lonT, decimals);
-}
-
-function getLatitude(y, decimals = 2) {
- return rn(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT, decimals);
-}
-
-function getCoordinates(x, y, decimals = 2) {
- return [getLongitude(x, decimals), getLatitude(y, decimals)];
-}
-
-// prompt replacer (prompt does not work in Electron)
-void (function () {
- const prompt = document.getElementById("prompt");
- const form = prompt.querySelector("#promptForm");
-
- const defaultText = "Please provide an input";
- const defaultOptions = {default: 1, step: 0.01, min: 0, max: 100, required: true};
-
- window.prompt = function (promptText = defaultText, options = defaultOptions, callback) {
- if (options.default === undefined)
- return ERROR && console.error("Prompt: options object does not have default value defined");
-
- const input = prompt.querySelector("#promptInput");
- prompt.querySelector("#promptText").innerHTML = promptText;
-
- const type = typeof options.default === "number" ? "number" : "text";
- input.type = type;
-
- if (options.step !== undefined) input.step = options.step;
- if (options.min !== undefined) input.min = options.min;
- if (options.max !== undefined) input.max = options.max;
-
- input.required = options.required === false ? false : true;
- input.placeholder = "type a " + type;
- input.value = options.default;
- prompt.style.display = "block";
-
- form.addEventListener(
- "submit",
- event => {
- event.preventDefault();
- prompt.style.display = "none";
- const v = type === "number" ? +input.value : input.value;
- if (callback) callback(v);
- },
- {once: true}
- );
- };
-
- const cancel = prompt.querySelector("#promptCancel");
- cancel.addEventListener("click", () => {
- prompt.style.display = "none";
- });
-})();
-
-// indexedDB; ldb object
-void (function () {
- function e(t, o) {
- return n
- ? void (n.transaction("s").objectStore("s").get(t).onsuccess = function (e) {
- var t = (e.target.result && e.target.result.v) || null;
- o(t);
- })
- : void setTimeout(function () {
- e(t, o);
- }, 100);
- }
- var t = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
- if (!t) return void ERROR && console.error("indexedDB not supported");
- var n,
- o = {k: "", v: ""},
- r = t.open("d2", 1);
- (r.onsuccess = function (e) {
- n = this.result;
- }),
- (r.onerror = function (e) {
- ERROR && console.error("indexedDB request error"), INFO && console.log(e);
- }),
- (r.onupgradeneeded = function (e) {
- n = null;
- var t = e.target.result.createObjectStore("s", {keyPath: "k"});
- t.transaction.oncomplete = function (e) {
- n = e.target.db;
- };
- }),
- (window.ldb = {
- get: e,
- set: function (e, t) {
- (o.k = e), (o.v = t), n.transaction("s", "readwrite").objectStore("s").put(o);
- }
- });
-})();
diff --git a/utils/functionUtils.js b/utils/functionUtils.js
index 845673a8..c670496b 100644
--- a/utils/functionUtils.js
+++ b/utils/functionUtils.js
@@ -23,3 +23,55 @@ function nest(values, map, reduce, keys) {
return map(groups);
})(values, 0);
}
+
+function debounce(func, ms) {
+ let isCooldown = false;
+
+ return function () {
+ if (isCooldown) return;
+ func.apply(this, arguments);
+ isCooldown = true;
+ setTimeout(() => (isCooldown = false), ms);
+ };
+}
+
+function throttle(func, ms) {
+ let isThrottled = false;
+ let savedArgs;
+ let savedThis;
+
+ function wrapper() {
+ if (isThrottled) {
+ savedArgs = arguments;
+ savedThis = this;
+ return;
+ }
+
+ func.apply(this, arguments);
+ isThrottled = true;
+
+ setTimeout(function () {
+ isThrottled = false;
+ if (savedArgs) {
+ wrapper.apply(savedThis, savedArgs);
+ savedArgs = savedThis = null;
+ }
+ }, ms);
+ }
+
+ return wrapper;
+}
+
+function getBase64(url, callback) {
+ const xhr = new XMLHttpRequest();
+ xhr.onload = function () {
+ const reader = new FileReader();
+ reader.onloadend = function () {
+ callback(reader.result);
+ };
+ reader.readAsDataURL(xhr.response);
+ };
+ xhr.open("GET", url);
+ xhr.responseType = "blob";
+ xhr.send();
+}
diff --git a/yarn-error.log b/yarn-error.log
new file mode 100644
index 00000000..bc4297af
--- /dev/null
+++ b/yarn-error.log
@@ -0,0 +1,276 @@
+Arguments:
+ /opt/homebrew/Cellar/node/18.0.0/bin/node /opt/homebrew/Cellar/yarn/1.22.18/libexec/bin/yarn.js add @types/lineclip -D
+
+PATH:
+ /opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin:/opt/homebrew/sbin:/opt/yarn-[version]/bin:/opt/yarn-[version]/bin:/opt/homebrew/bin
+
+Yarn version:
+ 1.22.18
+
+Node version:
+ 18.0.0
+
+Platform:
+ darwin arm64
+
+Trace:
+ Error: https://registry.yarnpkg.com/@types%2flineclip: Not found
+ at params.callback [as _callback] (/opt/homebrew/Cellar/yarn/1.22.18/libexec/lib/cli.js:66138:18)
+ at self.callback (/opt/homebrew/Cellar/yarn/1.22.18/libexec/lib/cli.js:140883:22)
+ at Request.emit (node:events:527:28)
+ at Request. (/opt/homebrew/Cellar/yarn/1.22.18/libexec/lib/cli.js:141855:10)
+ at Request.emit (node:events:527:28)
+ at IncomingMessage. (/opt/homebrew/Cellar/yarn/1.22.18/libexec/lib/cli.js:141777:12)
+ at Object.onceWrapper (node:events:641:28)
+ at IncomingMessage.emit (node:events:539:35)
+ at endReadableNT (node:internal/streams/readable:1344:12)
+ at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
+
+npm manifest:
+ {
+ "name": "fantasy-map-generator",
+ "version": "1.87.04",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "devDependencies": {
+ "typescript": "^4.7.4",
+ "vite": "^2.9.12"
+ },
+ "dependencies": {
+ "lineclip": "^1.1.5"
+ }
+ }
+
+yarn manifest:
+ No manifest
+
+Lockfile:
+ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+ # yarn lockfile v1
+
+
+ esbuild-android-64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.47.tgz#ef95b42c67bcf4268c869153fa3ad1466c4cea6b"
+ integrity sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g==
+
+ esbuild-android-arm64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.47.tgz#4ebd7ce9fb250b4695faa3ee46fd3b0754ecd9e6"
+ integrity sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ==
+
+ esbuild-darwin-64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.47.tgz#e0da6c244f497192f951807f003f6a423ed23188"
+ integrity sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA==
+
+ esbuild-darwin-arm64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.47.tgz#cd40fd49a672fca581ed202834239dfe540a9028"
+ integrity sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw==
+
+ esbuild-freebsd-64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.47.tgz#8da6a14c095b29c01fc8087a16cb7906debc2d67"
+ integrity sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ==
+
+ esbuild-freebsd-arm64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.47.tgz#ad31f9c92817ff8f33fd253af7ab5122dc1b83f6"
+ integrity sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ==
+
+ esbuild-linux-32@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.47.tgz#de085e4db2e692ea30c71208ccc23fdcf5196c58"
+ integrity sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw==
+
+ esbuild-linux-64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.47.tgz#2a9321bbccb01f01b04cebfcfccbabeba3658ba1"
+ integrity sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw==
+
+ esbuild-linux-arm64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.47.tgz#b9da7b6fc4b0ca7a13363a0c5b7bb927e4bc535a"
+ integrity sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw==
+
+ esbuild-linux-arm@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.47.tgz#56fec2a09b9561c337059d4af53625142aded853"
+ integrity sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA==
+
+ esbuild-linux-mips64le@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.47.tgz#9db21561f8f22ed79ef2aedb7bbef082b46cf823"
+ integrity sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg==
+
+ esbuild-linux-ppc64le@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.47.tgz#dc3a3da321222b11e96e50efafec9d2de408198b"
+ integrity sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w==
+
+ esbuild-linux-riscv64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.47.tgz#9bd6dcd3dca6c0357084ecd06e1d2d4bf105335f"
+ integrity sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g==
+
+ esbuild-linux-s390x@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.47.tgz#a458af939b52f2cd32fc561410d441a51f69d41f"
+ integrity sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw==
+
+ esbuild-netbsd-64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.47.tgz#6388e785d7e7e4420cb01348d7483ab511b16aa8"
+ integrity sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ==
+
+ esbuild-openbsd-64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.47.tgz#309af806db561aa886c445344d1aacab850dbdc5"
+ integrity sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw==
+
+ esbuild-sunos-64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.47.tgz#3f19612dcdb89ba6c65283a7ff6e16f8afbf8aaa"
+ integrity sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ==
+
+ esbuild-windows-32@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.47.tgz#a92d279c8458d5dc319abcfeb30aa49e8f2e6f7f"
+ integrity sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ==
+
+ esbuild-windows-64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.47.tgz#2564c3fcf0c23d701edb71af8c52d3be4cec5f8a"
+ integrity sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ==
+
+ esbuild-windows-arm64@0.14.47:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.47.tgz#86d9db1a22d83360f726ac5fba41c2f625db6878"
+ integrity sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ==
+
+ esbuild@^0.14.27:
+ version "0.14.47"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.47.tgz#0d6415f6bd8eb9e73a58f7f9ae04c5276cda0e4d"
+ integrity sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA==
+ optionalDependencies:
+ esbuild-android-64 "0.14.47"
+ esbuild-android-arm64 "0.14.47"
+ esbuild-darwin-64 "0.14.47"
+ esbuild-darwin-arm64 "0.14.47"
+ esbuild-freebsd-64 "0.14.47"
+ esbuild-freebsd-arm64 "0.14.47"
+ esbuild-linux-32 "0.14.47"
+ esbuild-linux-64 "0.14.47"
+ esbuild-linux-arm "0.14.47"
+ esbuild-linux-arm64 "0.14.47"
+ esbuild-linux-mips64le "0.14.47"
+ esbuild-linux-ppc64le "0.14.47"
+ esbuild-linux-riscv64 "0.14.47"
+ esbuild-linux-s390x "0.14.47"
+ esbuild-netbsd-64 "0.14.47"
+ esbuild-openbsd-64 "0.14.47"
+ esbuild-sunos-64 "0.14.47"
+ esbuild-windows-32 "0.14.47"
+ esbuild-windows-64 "0.14.47"
+ esbuild-windows-arm64 "0.14.47"
+
+ fsevents@~2.3.2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+ integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
+ function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+ has@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+ integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+ dependencies:
+ function-bind "^1.1.1"
+
+ is-core-module@^2.9.0:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
+ integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
+ dependencies:
+ has "^1.0.3"
+
+ lineclip@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/lineclip/-/lineclip-1.1.5.tgz#2bf26067d94354feabf91e42768236db5616fd13"
+ integrity sha512-KlA/wRSjpKl7tS9iRUdlG72oQ7qZ1IlVbVgHwoO10TBR/4gQ86uhKow6nlzMAJJhjCWKto8OeoAzzIzKSmN25A==
+
+ nanoid@^3.3.4:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
+ integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
+
+ path-parse@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+ picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+ postcss@^8.4.13:
+ version "8.4.14"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf"
+ integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
+ dependencies:
+ nanoid "^3.3.4"
+ picocolors "^1.0.0"
+ source-map-js "^1.0.2"
+
+ resolve@^1.22.0:
+ version "1.22.1"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
+ integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
+ dependencies:
+ is-core-module "^2.9.0"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+ rollup@^2.59.0:
+ version "2.75.7"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.75.7.tgz#221ff11887ae271e37dcc649ba32ce1590aaa0b9"
+ integrity sha512-VSE1iy0eaAYNCxEXaleThdFXqZJ42qDBatAwrfnPlENEZ8erQ+0LYX4JXOLPceWfZpV1VtZwZ3dFCuOZiSyFtQ==
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+ source-map-js@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+ integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
+ supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+ typescript@^4.7.4:
+ version "4.7.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
+ integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
+
+ vite@^2.9.12:
+ version "2.9.12"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.12.tgz#b1d636b0a8ac636afe9d83e3792d4895509a941b"
+ integrity sha512-suxC36dQo9Rq1qMB2qiRorNJtJAdxguu5TMvBHOc/F370KvqAe9t48vYp+/TbPKRNrMh/J55tOUmkuIqstZaew==
+ dependencies:
+ esbuild "^0.14.27"
+ postcss "^8.4.13"
+ resolve "^1.22.0"
+ rollup "^2.59.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
diff --git a/yarn.lock b/yarn.lock
index cb43f076..e2ba76bd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -152,6 +152,11 @@ is-core-module@^2.9.0:
dependencies:
has "^1.0.3"
+lineclip@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/lineclip/-/lineclip-1.1.5.tgz#2bf26067d94354feabf91e42768236db5616fd13"
+ integrity sha512-KlA/wRSjpKl7tS9iRUdlG72oQ7qZ1IlVbVgHwoO10TBR/4gQ86uhKow6nlzMAJJhjCWKto8OeoAzzIzKSmN25A==
+
nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"