mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-02-04 17:41:23 +01:00
Merge branch 'master' into refactor/migrate-first-modules
This commit is contained in:
commit
613e826133
55 changed files with 1473 additions and 214 deletions
|
|
@ -8493,6 +8493,7 @@
|
|||
|
||||
<script defer src="config/heightmap-templates.js"></script>
|
||||
<script defer src="config/precreated-heightmaps.js"></script>
|
||||
<script defer src="modules/ice.js?v=1.111.0"></script>
|
||||
<script defer src="modules/names-generator.js?v=1.106.0"></script>
|
||||
<script defer src="modules/cultures-generator.js?v=1.106.0"></script>
|
||||
<script defer src="modules/burgs-generator.js?v=1.109.5"></script>
|
||||
|
|
@ -8510,16 +8511,16 @@
|
|||
<script defer src="libs/lineclip.min.js?v1.105.0"></script>
|
||||
<script defer src="libs/simplify.js?v1.105.6"></script>
|
||||
<script defer src="modules/fonts.js?v=1.99.03"></script>
|
||||
<script defer src="modules/ui/layers.js?v=1.108.4"></script>
|
||||
<script defer src="modules/ui/layers.js?v=1.111.0"></script>
|
||||
<script defer src="modules/ui/measurers.js?v=1.99.00"></script>
|
||||
<script defer src="modules/ui/style-presets.js?v=1.100.00"></script>
|
||||
<script defer src="modules/ui/general.js?v=1.100.00"></script>
|
||||
<script defer src="modules/ui/options.js?v=1.106.2"></script>
|
||||
<script defer src="main.js?v=1.108.1"></script>
|
||||
<script defer src="main.js?v=1.111.0"></script>
|
||||
|
||||
<script defer src="modules/ui/style.js?v=1.108.4"></script>
|
||||
<script defer src="modules/ui/editors.js?v=1.108.5"></script>
|
||||
<script defer src="modules/ui/tools.js?v=1.108.5"></script>
|
||||
<script defer src="modules/ui/editors.js?v=1.111.0"></script>
|
||||
<script defer src="modules/ui/tools.js?v=1.111.0"></script>
|
||||
<script defer src="modules/ui/world-configurator.js?v=1.105.4"></script>
|
||||
<script defer src="modules/ui/heightmap-editor.js?v=1.105.2"></script>
|
||||
<script defer src="modules/ui/provinces-editor.js?v=1.108.1"></script>
|
||||
|
|
@ -8530,7 +8531,7 @@
|
|||
<script defer src="modules/ui/routes-editor.js?v=1.104.3"></script>
|
||||
<script defer src="modules/ui/routes-creator.js?v=1.104.3"></script>
|
||||
<script defer src="modules/ui/route-group-editor.js?v=1.103.8"></script>
|
||||
<script defer src="modules/ui/ice-editor.js?v=1.99.00"></script>
|
||||
<script defer src="modules/ui/ice-editor.js?v=1.111.0"></script>
|
||||
<script defer src="modules/ui/lakes-editor.js?v=1.106.0"></script>
|
||||
<script defer src="modules/ui/coastline-editor.js?v=1.99.00"></script>
|
||||
<script defer src="modules/ui/labels-editor.js?v=1.106.0"></script>
|
||||
|
|
@ -8544,12 +8545,12 @@
|
|||
<script defer src="modules/ui/ai-generator.js?v=1.108.8"></script>
|
||||
<script defer src="modules/ui/diplomacy-editor.js?v=1.99.00"></script>
|
||||
<script defer src="modules/ui/zones-editor.js?v=1.105.20"></script>
|
||||
<script defer src="modules/ui/burgs-overview.js?v=1.110.0"></script>
|
||||
<script defer src="modules/ui/routes-overview.js?v=1.110.0"></script>
|
||||
<script defer src="modules/ui/rivers-overview.js?v=1.110.0"></script>
|
||||
<script defer src="modules/ui/burgs-overview.js?v=1.111.0"></script>
|
||||
<script defer src="modules/ui/routes-overview.js?v=1.111.0"></script>
|
||||
<script defer src="modules/ui/rivers-overview.js?v=1.111.0"></script>
|
||||
<script defer src="modules/ui/military-overview.js?v=1.108.5"></script>
|
||||
<script defer src="modules/ui/regiments-overview.js?v=1.108.5"></script>
|
||||
<script defer src="modules/ui/markers-overview.js?v=1.110.0"></script>
|
||||
<script defer src="modules/ui/markers-overview.js?v=1.111.0"></script>
|
||||
<script defer src="modules/ui/regiment-editor.js?v=1.108.5"></script>
|
||||
<script defer src="modules/ui/battle-screen.js?v=1.108.5"></script>
|
||||
<script defer src="modules/ui/emblems-editor.js?v=1.99.00"></script>
|
||||
|
|
@ -8561,8 +8562,8 @@
|
|||
<script defer src="modules/coa-renderer.js?v=1.99.00"></script>
|
||||
<script defer src="libs/rgbquant.min.js"></script>
|
||||
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
|
||||
<script defer src="modules/io/save.js?v=1.107.4"></script>
|
||||
<script defer src="modules/io/load.js?v=1.109.4"></script>
|
||||
<script defer src="modules/io/save.js?v=1.111.0"></script>
|
||||
<script defer src="modules/io/load.js?v=1.111.0"></script>
|
||||
<script defer src="modules/io/cloud.js?v=1.106.0"></script>
|
||||
<script defer src="modules/io/export.js?v=1.108.13"></script>
|
||||
|
||||
|
|
@ -8578,5 +8579,6 @@
|
|||
<script defer src="modules/renderers/draw-burg-labels.js?v=1.109.4"></script>
|
||||
<script defer src="modules/renderers/draw-burg-icons.js?v=1.109.4"></script>
|
||||
<script defer src="modules/renderers/draw-relief-icons.js?v=1.108.4"></script>
|
||||
<script defer src="modules/renderers/draw-ice.js?v=1.111.0"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -560,9 +560,7 @@ class HeightmapGenerator {
|
|||
if(!ctx) {
|
||||
throw new Error("Could not get canvas context");
|
||||
}
|
||||
if(!this.heights) {
|
||||
throw new Error("Heights array is not initialized");
|
||||
}
|
||||
this.heights = this.heights || new Uint8Array(cellsX * cellsY);
|
||||
ctx.drawImage(img, 0, 0, cellsX, cellsY);
|
||||
const imageData = ctx.getImageData(0, 0, cellsX, cellsY);
|
||||
this.setGraph(graph);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { color, interpolate, interpolateRainbow, range, RGBColor, scaleSequential, shuffle } from "d3";
|
||||
import { color, interpolate, interpolateRainbow, range, RGBColor, scaleSequential, shuffler } from "d3";
|
||||
|
||||
/**
|
||||
* Convert RGB or RGBA color to HEX
|
||||
|
|
@ -35,11 +35,14 @@ export const C_12 = [
|
|||
|
||||
/**
|
||||
* Get an array of distinct colors
|
||||
* Uses shuffler with current Math.random to ensure seeded randomness works
|
||||
* @param {number} count - The count of colors to generate
|
||||
* @returns {string[]} - The array of HEX color strings
|
||||
*/
|
||||
export const getColors = (count: number): string[] => {
|
||||
const scaleRainbow = scaleSequential(interpolateRainbow);
|
||||
// Use shuffler() to create a shuffle function that uses the current Math.random
|
||||
const shuffle = shuffler(() => Math.random());
|
||||
const colors = shuffle(
|
||||
range(count).map(i => (i < 12 ? C_12[i] : color(scaleRainbow((i - 12) / (count - 12)))?.formatHex()))
|
||||
);
|
||||
|
|
|
|||
|
|
@ -206,13 +206,109 @@ export const findClosestCell = (x: number, y: number, radius = Infinity, packedG
|
|||
return found ? found[2] : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches a quadtree for all points within a given radius
|
||||
* Based on https://bl.ocks.org/lwthatcher/b41479725e0ff2277c7ac90df2de2b5e
|
||||
* @param {number} x - The x coordinate of the search center
|
||||
* @param {number} y - The y coordinate of the search center
|
||||
* @param {number} radius - The search radius
|
||||
* @param {Object} quadtree - The D3 quadtree to search
|
||||
* @returns {Array} - An array of found data points within the radius
|
||||
*/
|
||||
export const findAllInQuadtree = (x: number, y: number, radius: number, quadtree: any) => {
|
||||
const radiusSearchInit = (t: any, radius: number) => {
|
||||
t.result = [];
|
||||
(t.x0 = t.x - radius), (t.y0 = t.y - radius);
|
||||
(t.x3 = t.x + radius), (t.y3 = t.y + radius);
|
||||
t.radius = radius * radius;
|
||||
};
|
||||
|
||||
const radiusSearchVisit = (t: any, d2: number) => {
|
||||
t.node.data.scanned = true;
|
||||
if (d2 < t.radius) {
|
||||
do {
|
||||
t.result.push(t.node.data);
|
||||
t.node.data.selected = true;
|
||||
} while ((t.node = t.node.next));
|
||||
}
|
||||
};
|
||||
|
||||
class Quad {
|
||||
node: any;
|
||||
x0: number;
|
||||
y0: number;
|
||||
x1: number;
|
||||
y1: number;
|
||||
constructor(node: any, x0: number, y0: number, x1: number, y1: number) {
|
||||
this.node = node;
|
||||
this.x0 = x0;
|
||||
this.y0 = y0;
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
}
|
||||
}
|
||||
|
||||
const t: any = {x, y, x0: quadtree._x0, y0: quadtree._y0, x3: quadtree._x1, y3: quadtree._y1, quads: [], node: quadtree._root};
|
||||
if (t.node) t.quads.push(new Quad(t.node, t.x0, t.y0, t.x3, t.y3));
|
||||
radiusSearchInit(t, radius);
|
||||
|
||||
var i = 0;
|
||||
while ((t.q = t.quads.pop())) {
|
||||
i++;
|
||||
|
||||
// Stop searching if this quadrant can't contain a closer node.
|
||||
if (
|
||||
!(t.node = t.q.node) ||
|
||||
(t.x1 = t.q.x0) > t.x3 ||
|
||||
(t.y1 = t.q.y0) > t.y3 ||
|
||||
(t.x2 = t.q.x1) < t.x0 ||
|
||||
(t.y2 = t.q.y1) < t.y0
|
||||
)
|
||||
continue;
|
||||
|
||||
// Bisect the current quadrant.
|
||||
if (t.node.length) {
|
||||
t.node.explored = true;
|
||||
var xm: number = (t.x1 + t.x2) / 2,
|
||||
ym: number = (t.y1 + t.y2) / 2;
|
||||
|
||||
t.quads.push(
|
||||
new Quad(t.node[3], xm, ym, t.x2, t.y2),
|
||||
new Quad(t.node[2], t.x1, ym, xm, t.y2),
|
||||
new Quad(t.node[1], xm, t.y1, t.x2, ym),
|
||||
new Quad(t.node[0], t.x1, t.y1, xm, ym)
|
||||
);
|
||||
|
||||
// Visit the closest quadrant first.
|
||||
if ((t.i = (+(y >= ym) << 1) | +(x >= xm))) {
|
||||
t.q = t.quads[t.quads.length - 1];
|
||||
t.quads[t.quads.length - 1] = t.quads[t.quads.length - 1 - t.i];
|
||||
t.quads[t.quads.length - 1 - t.i] = t.q;
|
||||
}
|
||||
}
|
||||
|
||||
// Visit this point. (Visiting coincident points isn't necessary!)
|
||||
else {
|
||||
var dx = x - +quadtree._x.call(null, t.node.data),
|
||||
dy = y - +quadtree._y.call(null, t.node.data),
|
||||
d2 = dx * dx + dy * dy;
|
||||
radiusSearchVisit(t, d2);
|
||||
}
|
||||
}
|
||||
return t.result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of packed cell indexes within a specified radius from given x and y coordinates
|
||||
* @param {number} x - The x coordinate
|
||||
* @param {number} y - The y coordinate
|
||||
* @param {number} radius - The search radius
|
||||
* @param {Object} packedGraph - The packed graph containing cells with quadtree
|
||||
* @returns {number[]} - An array of cell indexes within the radius
|
||||
*/
|
||||
export const findAllCellsInRadius = (x: number, y: number, radius: number, packedGraph: any): number[] => {
|
||||
const found = packedGraph.cells.q.findAll(x, y, radius);
|
||||
// Use findAllInQuadtree directly instead of relying on prototype extension
|
||||
const found = findAllInQuadtree(x, y, radius, packedGraph.cells.q);
|
||||
return found.map((r: any) => r[2]);
|
||||
}
|
||||
|
||||
|
|
@ -325,88 +421,6 @@ export const isWater = (i: number, packedGraph: any) => {
|
|||
return packedGraph.cells.h[i] < 20;
|
||||
}
|
||||
|
||||
export const findAllInQuadtree = (x: number, y: number, radius: number, quadtree: any) => {
|
||||
const radiusSearchInit = (t: any, radius: number) => {
|
||||
t.result = [];
|
||||
(t.x0 = t.x - radius), (t.y0 = t.y - radius);
|
||||
(t.x3 = t.x + radius), (t.y3 = t.y + radius);
|
||||
t.radius = radius * radius;
|
||||
};
|
||||
|
||||
const radiusSearchVisit = (t: any, d2: number) => {
|
||||
t.node.data.scanned = true;
|
||||
if (d2 < t.radius) {
|
||||
do {
|
||||
t.result.push(t.node.data);
|
||||
t.node.data.selected = true;
|
||||
} while ((t.node = t.node.next));
|
||||
}
|
||||
};
|
||||
|
||||
class Quad {
|
||||
node: any;
|
||||
x0: number;
|
||||
y0: number;
|
||||
x1: number;
|
||||
y1: number;
|
||||
constructor(node: any, x0: number, y0: number, x1: number, y1: number) {
|
||||
this.node = node;
|
||||
this.x0 = x0;
|
||||
this.y0 = y0;
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
}
|
||||
}
|
||||
|
||||
const t: any = {x, y, x0: quadtree._x0, y0: quadtree._y0, x3: quadtree._x1, y3: quadtree._y1, quads: [], node: quadtree._root};
|
||||
if (t.node) t.quads.push(new Quad(t.node, t.x0, t.y0, t.x3, t.y3));
|
||||
radiusSearchInit(t, radius);
|
||||
|
||||
var i = 0;
|
||||
while ((t.q = t.quads.pop())) {
|
||||
i++;
|
||||
|
||||
// Stop searching if this quadrant can’t contain a closer node.
|
||||
if (
|
||||
!(t.node = t.q.node) ||
|
||||
(t.x1 = t.q.x0) > t.x3 ||
|
||||
(t.y1 = t.q.y0) > t.y3 ||
|
||||
(t.x2 = t.q.x1) < t.x0 ||
|
||||
(t.y2 = t.q.y1) < t.y0
|
||||
)
|
||||
continue;
|
||||
|
||||
// Bisect the current quadrant.
|
||||
if (t.node.length) {
|
||||
t.node.explored = true;
|
||||
var xm: number = (t.x1 + t.x2) / 2,
|
||||
ym: number = (t.y1 + t.y2) / 2;
|
||||
|
||||
t.quads.push(
|
||||
new Quad(t.node[3], xm, ym, t.x2, t.y2),
|
||||
new Quad(t.node[2], t.x1, ym, xm, t.y2),
|
||||
new Quad(t.node[1], xm, t.y1, t.x2, ym),
|
||||
new Quad(t.node[0], t.x1, t.y1, xm, ym)
|
||||
);
|
||||
|
||||
// Visit the closest quadrant first.
|
||||
if ((t.i = (+(y >= ym) << 1) | +(x >= xm))) {
|
||||
t.q = t.quads[t.quads.length - 1];
|
||||
t.quads[t.quads.length - 1] = t.quads[t.quads.length - 1 - t.i];
|
||||
t.quads[t.quads.length - 1 - t.i] = t.q;
|
||||
}
|
||||
}
|
||||
|
||||
// Visit this point. (Visiting coincident points isn’t necessary!)
|
||||
else {
|
||||
var dx = x - +quadtree._x.call(null, t.node.data),
|
||||
dy = y - +quadtree._y.call(null, t.node.data),
|
||||
d2 = dx * dx + dy * dy;
|
||||
radiusSearchVisit(t, d2);
|
||||
}
|
||||
}
|
||||
return t.result;
|
||||
}
|
||||
|
||||
// draw raster heightmap preview (not used in main generation)
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export const each = (n: number) => {
|
|||
|
||||
/**
|
||||
* Random Gaussian number generator
|
||||
* Uses randomNormal.source(Math.random) to ensure it uses the current PRNG
|
||||
* @param {number} expected - expected value
|
||||
* @param {number} deviation - standard deviation
|
||||
* @param {number} min - minimum value
|
||||
|
|
@ -46,7 +47,8 @@ export const each = (n: number) => {
|
|||
* @return {number} random number
|
||||
*/
|
||||
export const gauss = (expected = 100, deviation = 30, min = 0, max = 300, round = 0) => {
|
||||
return rn(minmax(randomNormal(expected, deviation)(), min, max), round);
|
||||
// Use .source() to get a version that uses the current Math.random (which may be seeded)
|
||||
return rn(minmax(randomNormal.source(() => Math.random())(expected, deviation)(), min, max), round);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
8
src/utils/stringUtils.test.ts
Normal file
8
src/utils/stringUtils.test.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { expect, describe, it } from 'vitest'
|
||||
import { round } from './stringUtils'
|
||||
|
||||
describe('round', () => {
|
||||
it('should be able to handle undefined input', () => {
|
||||
expect(round(undefined)).toBe("");
|
||||
});
|
||||
})
|
||||
|
|
@ -6,7 +6,7 @@ import { rn } from "./numberUtils";
|
|||
* @param {number} decimals - Number of decimal places (default is 1)
|
||||
* @returns {string} - The string with rounded numbers
|
||||
*/
|
||||
export const round = (inputString: string, decimals: number = 1) => {
|
||||
export const round = (inputString: string = "", decimals: number = 1) => {
|
||||
return inputString.replace(/[\d\.-][\d\.e-]*/g, (n: string) => {
|
||||
return rn(parseFloat(n), decimals).toString();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue