mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-02-05 10:01:24 +01:00
feat: Implement HeightmapGenerator and Voronoi module
- Added HeightmapGenerator class for generating heightmaps with various tools (Hill, Pit, Range, Trough, Strait, etc.). - Introduced Voronoi class for creating Voronoi diagrams using Delaunator. - Updated index.html to include new modules. - Created index.ts to manage module imports. - Enhanced arrayUtils and graphUtils with type definitions and improved functionality. - Added utility functions for generating grids and calculating Voronoi cells.
This commit is contained in:
parent
fa493989b6
commit
4b5e9bfeea
8 changed files with 748 additions and 601 deletions
|
|
@ -78,7 +78,7 @@ export const getTypedArray = (maxValue: number) => {
|
|||
* @param {Array} [options.from] - An optional array to create the typed array from
|
||||
* @returns The created typed array
|
||||
*/
|
||||
export const createTypedArray = ({maxValue, length, from}: {maxValue: number; length: number; from?: ArrayLike<number>}) => {
|
||||
export const createTypedArray = ({maxValue, length, from}: {maxValue: number; length: number; from?: ArrayLike<number>}): Uint8Array | Uint16Array | Uint32Array => {
|
||||
const typedArray = getTypedArray(maxValue);
|
||||
if (!from) return new typedArray(length);
|
||||
return typedArray.from(from);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { color } from "d3";
|
|||
import { byId } from "./shorthands";
|
||||
import { rn } from "./numberUtils";
|
||||
import { createTypedArray } from "./arrayUtils";
|
||||
import { Cells, Vertices, Voronoi, Point } from "../modules/voronoi";
|
||||
|
||||
/**
|
||||
* Get boundary points on a regular square grid
|
||||
|
|
@ -12,14 +13,14 @@ import { createTypedArray } from "./arrayUtils";
|
|||
* @param {number} spacing - The spacing between points
|
||||
* @returns {Array} - An array of boundary points
|
||||
*/
|
||||
const getBoundaryPoints = (width: number, height: number, spacing: number) => {
|
||||
const getBoundaryPoints = (width: number, height: number, spacing: number): Point[] => {
|
||||
const offset = rn(-1 * spacing);
|
||||
const bSpacing = spacing * 2;
|
||||
const w = width - offset * 2;
|
||||
const h = height - offset * 2;
|
||||
const numberX = Math.ceil(w / bSpacing) - 1;
|
||||
const numberY = Math.ceil(h / bSpacing) - 1;
|
||||
const points = [];
|
||||
const points: Point[] = [];
|
||||
|
||||
for (let i = 0.5; i < numberX; i++) {
|
||||
let x = Math.ceil((w * i) / numberX + offset);
|
||||
|
|
@ -41,13 +42,13 @@ const getBoundaryPoints = (width: number, height: number, spacing: number) => {
|
|||
* @param {number} spacing - The spacing between points
|
||||
* @returns {Array} - An array of jittered grid points
|
||||
*/
|
||||
const getJitteredGrid = (width: number, height: number, spacing: number): number[][] => {
|
||||
const getJitteredGrid = (width: number, height: number, spacing: number): Point[] => {
|
||||
const radius = spacing / 2; // square radius
|
||||
const jittering = radius * 0.9; // max deviation
|
||||
const doubleJittering = jittering * 2;
|
||||
const jitter = () => Math.random() * doubleJittering - jittering;
|
||||
|
||||
let points: number[][] = [];
|
||||
let points: Point[] = [];
|
||||
for (let y = radius; y < height; y += spacing) {
|
||||
for (let x = radius; x < width; x += spacing) {
|
||||
const xj = Math.min(rn(x + jitter(), 2), width);
|
||||
|
|
@ -64,18 +65,18 @@ const getJitteredGrid = (width: number, height: number, spacing: number): number
|
|||
* @param {number} graphHeight - The height of the graph
|
||||
* @returns {Object} - An object containing spacing, cellsDesired, boundary points, grid points, cellsX, and cellsY
|
||||
*/
|
||||
const placePoints = (graphWidth: number, graphHeight: number) => {
|
||||
window.TIME && console.time("placePoints");
|
||||
const placePoints = (graphWidth: number, graphHeight: number): {spacing: number, cellsDesired: number, boundary: Point[], points: Point[], cellsX: number, cellsY: number} => {
|
||||
TIME && console.time("placePoints");
|
||||
const cellsDesired = +(byId("pointsInput")?.dataset.cells || 0);
|
||||
const spacing = rn(Math.sqrt((graphWidth * graphHeight) / cellsDesired), 2); // spacing between points before jirrering
|
||||
const spacing = rn(Math.sqrt((graphWidth * graphHeight) / cellsDesired), 2); // spacing between points before jittering
|
||||
|
||||
const boundary = getBoundaryPoints(graphWidth, graphHeight, spacing);
|
||||
const points = getJitteredGrid(graphWidth, graphHeight, spacing); // points of jittered square grid
|
||||
const cellsX = Math.floor((graphWidth + 0.5 * spacing - 1e-10) / spacing);
|
||||
const cellsY = Math.floor((graphHeight + 0.5 * spacing - 1e-10) / spacing);
|
||||
window.TIME && console.timeEnd("placePoints");
|
||||
const cellCountX = Math.floor((graphWidth + 0.5 * spacing - 1e-10) / spacing); // number of cells in x direction
|
||||
const cellCountY = Math.floor((graphHeight + 0.5 * spacing - 1e-10) / spacing); // number of cells in y direction
|
||||
TIME && console.timeEnd("placePoints");
|
||||
|
||||
return {spacing, cellsDesired, boundary, points, cellsX, cellsY};
|
||||
return {spacing, cellsDesired, boundary, points, cellsX: cellCountX, cellsY: cellCountY};
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -100,11 +101,22 @@ export const shouldRegenerateGrid = (grid: any, expectedSeed: number, graphWidth
|
|||
return grid.spacing !== newSpacing || grid.cellsX !== newCellsX || grid.cellsY !== newCellsY;
|
||||
}
|
||||
|
||||
interface Grid {
|
||||
spacing: number;
|
||||
cellsDesired: number;
|
||||
boundary: Point[];
|
||||
points: Point[];
|
||||
cellsX: number;
|
||||
cellsY: number;
|
||||
seed: string | number;
|
||||
cells: Cells;
|
||||
vertices: Vertices;
|
||||
}
|
||||
/**
|
||||
* Generates a Voronoi grid based on jittered grid points
|
||||
* @returns {Object} - The generated grid object containing spacing, cellsDesired, boundary, points, cellsX, cellsY, cells, vertices, and seed
|
||||
*/
|
||||
export const generateGrid = (seed: string, graphWidth: number, graphHeight: number) => {
|
||||
export const generateGrid = (seed: string, graphWidth: number, graphHeight: number): Grid => {
|
||||
Math.random = Alea(seed); // reset PRNG
|
||||
const {spacing, cellsDesired, boundary, points, cellsX, cellsY} = placePoints(graphWidth, graphHeight);
|
||||
const {cells, vertices} = calculateVoronoi(points, boundary);
|
||||
|
|
@ -117,19 +129,19 @@ export const generateGrid = (seed: string, graphWidth: number, graphHeight: numb
|
|||
* @param {Array} boundary - The boundary points to clip the Voronoi cells
|
||||
* @returns {Object} - An object containing Voronoi cells and vertices
|
||||
*/
|
||||
export const calculateVoronoi = (points: number[][], boundary: number[][]) => {
|
||||
window.TIME && console.time("calculateDelaunay");
|
||||
export const calculateVoronoi = (points: Point[], boundary: Point[]): {cells: Cells, vertices: Vertices} => {
|
||||
TIME && console.time("calculateDelaunay");
|
||||
const allPoints = points.concat(boundary);
|
||||
const delaunay = Delaunator.from(allPoints);
|
||||
window.TIME && console.timeEnd("calculateDelaunay");
|
||||
TIME && console.timeEnd("calculateDelaunay");
|
||||
|
||||
window.TIME && console.time("calculateVoronoi");
|
||||
const voronoi = new window.Voronoi(delaunay, allPoints, points.length);
|
||||
TIME && console.time("calculateVoronoi");
|
||||
const voronoi = new Voronoi(delaunay, allPoints, points.length);
|
||||
|
||||
const cells = voronoi.cells;
|
||||
cells.i = createTypedArray({maxValue: points.length, length: points.length}).map((_, i) => i); // array of indexes
|
||||
cells.i = createTypedArray({maxValue: points.length, length: points.length}).map((_, i) => i) as Uint32Array<ArrayBufferLike>; // array of indexes
|
||||
const vertices = voronoi.vertices;
|
||||
window.TIME && console.timeEnd("calculateVoronoi");
|
||||
TIME && console.timeEnd("calculateVoronoi");
|
||||
|
||||
return {cells, vertices};
|
||||
}
|
||||
|
|
@ -432,9 +444,8 @@ export const drawHeights = ({heights, width, height, scheme, renderOcean}: {heig
|
|||
}
|
||||
|
||||
declare global {
|
||||
var TIME: boolean;
|
||||
interface Window {
|
||||
TIME: boolean;
|
||||
Voronoi: any;
|
||||
|
||||
shouldRegenerateGrid: typeof shouldRegenerateGrid;
|
||||
generateGrid: typeof generateGrid;
|
||||
|
|
|
|||
|
|
@ -143,4 +143,94 @@ window.drawCellsValue = (data:any[]) => drawCellsValue(data, (window as any).pac
|
|||
window.drawPolygons = (data: any[]) => drawPolygons(data, (window as any).terrs, (window as any).grid);
|
||||
window.drawRouteConnections = () => drawRouteConnections((window as any).packedGraph);
|
||||
window.drawPoint = drawPoint;
|
||||
window.drawPath = drawPath;
|
||||
window.drawPath = drawPath;
|
||||
|
||||
|
||||
export {
|
||||
rn,
|
||||
lim,
|
||||
minmax,
|
||||
normalize,
|
||||
lerp,
|
||||
isVowel,
|
||||
trimVowels,
|
||||
getAdjective,
|
||||
nth,
|
||||
abbreviate,
|
||||
list,
|
||||
last,
|
||||
unique,
|
||||
deepCopy,
|
||||
getTypedArray,
|
||||
createTypedArray,
|
||||
TYPED_ARRAY_MAX_VALUES,
|
||||
rand,
|
||||
P,
|
||||
each,
|
||||
gauss,
|
||||
Pint,
|
||||
biased,
|
||||
generateSeed,
|
||||
getNumberInRange,
|
||||
ra,
|
||||
rw,
|
||||
convertTemperature,
|
||||
si,
|
||||
getIntegerFromSI,
|
||||
toHEX,
|
||||
getColors,
|
||||
getRandomColor,
|
||||
getMixedColor,
|
||||
C_12,
|
||||
getComposedPath,
|
||||
getNextId,
|
||||
rollups,
|
||||
distanceSquared,
|
||||
getIsolines,
|
||||
getPolesOfInaccessibility,
|
||||
connectVertices,
|
||||
findPath,
|
||||
getVertexPath,
|
||||
round,
|
||||
capitalize,
|
||||
splitInTwo,
|
||||
parseTransform,
|
||||
isValidJSON,
|
||||
safeParseJSON,
|
||||
sanitizeId,
|
||||
byId,
|
||||
shouldRegenerateGrid,
|
||||
generateGrid,
|
||||
findGridAll,
|
||||
findGridCell,
|
||||
findClosestCell,
|
||||
calculateVoronoi,
|
||||
findAllCellsInRadius,
|
||||
getPackPolygon,
|
||||
getGridPolygon,
|
||||
poissonDiscSampler,
|
||||
isLand,
|
||||
isWater,
|
||||
findAllInQuadtree,
|
||||
drawHeights,
|
||||
clipPoly,
|
||||
getSegmentId,
|
||||
debounce,
|
||||
throttle,
|
||||
parseError,
|
||||
getBase64,
|
||||
openURL,
|
||||
wiki,
|
||||
link,
|
||||
isCtrlClick,
|
||||
generateDate,
|
||||
getLongitude,
|
||||
getLatitude,
|
||||
getCoordinates,
|
||||
initializePrompt,
|
||||
drawCellsValue,
|
||||
drawPolygons,
|
||||
drawRouteConnections,
|
||||
drawPoint,
|
||||
drawPath
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue