libpov_2025/biome-ui.js

681 lines
18 KiB
JavaScript
Raw Normal View History

2023-10-20 13:31:23 +02:00
"use strict";
2023-10-20 14:39:49 +02:00
const MIN_Y_DEFAULT = -31000
const MAX_Y_DEFAULT = 31000
const LIMIT_MIN = -37.5
const LIMIT_MAX = 137.5
const DRAW_MIN = LIMIT_MIN - 10
const DRAW_MAX = LIMIT_MAX + 10
const GRID_STEP = 10
2023-10-21 11:35:22 +02:00
const MIDPOINT = 50
2023-10-20 14:39:49 +02:00
// Symbol for storing the biome ID in site objects
// for the Voronoi script
const biomeIDSymbol = Symbol("Biome ID");
2023-10-20 16:31:11 +02:00
let lastBiomeID = 0;
2023-10-20 18:38:31 +02:00
let biomePoints = [];
function addBiome(biomeDef) {
biomeDef.id = lastBiomeID;
biomePoints.push(biomeDef);
lastBiomeID++;
}
2023-10-21 11:35:22 +02:00
// Add a default biome at the midpoint
addBiome({name: generateBiomeName(MIDPOINT, MIDPOINT), heat:MIDPOINT, humidity:MIDPOINT, min_y: MIN_Y_DEFAULT, max_y: MAX_Y_DEFAULT})
2023-10-20 13:31:23 +02:00
2023-10-20 17:22:48 +02:00
function getViewY() {
if (!inputViewY) {
return 0;
}
return inputViewY.value;
}
2023-10-20 13:31:23 +02:00
const pointColor = "#913636";
const pointColorSelected = "#e19696";
const edgeColor = "#0f2c2e";
const gridColor = "#00000040";
const axisColor = "#000000";
2023-10-20 17:30:05 +02:00
const clearColor = "#ecddba";
2023-10-20 13:31:23 +02:00
const cellColors = [
"#64988e",
"#3d7085",
"#345644",
"#6b7f5c",
2023-10-21 12:08:24 +02:00
"#868750",
"#a7822c",
"#a06e38",
2023-10-20 13:31:23 +02:00
"#ad5f52",
"#692f11",
"#89542f",
"#796e63",
"#a17d5e",
2023-10-21 12:08:24 +02:00
"#5a3f20",
"#836299",
2023-10-20 13:31:23 +02:00
];
2023-10-20 16:31:11 +02:00
// returns the biome point by its given ID
// or null if it couldn't be found
function getBiomeByID(id) {
for(let b=0; b<biomePoints.length; b++) {
let biome = biomePoints[b];
if (biome.id === id) {
return biome;
}
2023-10-20 13:31:23 +02:00
}
2023-10-20 16:31:11 +02:00
return null;
}
// Returns a biome name for displaying it, based on heat and humidity values.
function generateBiomeName(heat, humidity) {
return "("+heat+","+humidity+")";
2023-10-20 13:31:23 +02:00
}
function biomePointToVoronoiPoint(point) {
// Apart from x and y, we also add the biome ID to the Voronoi point
// so we can re-identify to which biome it belongs to when the
// object is returned from the Voronoi script output.
let newPoint = { x: point.heat, y: point.humidity, [biomeIDSymbol]: point.id }
2023-10-20 13:31:23 +02:00
return newPoint;
}
function voronoiPointToBiomePoint(point) {
let newPoint = { heat: point.x, humidity: point.y, id: point[biomeIDSymbol] }
2023-10-20 13:31:23 +02:00
return newPoint;
}
2023-10-21 11:35:22 +02:00
function putPointName(context, point) {
let x = point.heat
let y = point.humidity - 1
if (x < LIMIT_MIN || x > LIMIT_MAX || y < LIMIT_MIN || y > LIMIT_MAX) {
return;
}
if (x > MIDPOINT) {
context.textAlign = "right";
x = x-2;
} else {
context.textAlign = "left";
x = x+2;
}
2023-10-21 12:08:24 +02:00
if (y < MIDPOINT) {
context.textBaseline = "top";
} else {
context.textBaseline = "alphabetic";
}
2023-10-21 11:35:22 +02:00
context.font = "35% sans-serif"
context.fillText(point.name, x, y);
}
2023-10-20 13:31:23 +02:00
function putPoint(context, point) {
2023-10-20 15:23:44 +02:00
const ARROW_SIZE_SIDE = 2.25;
const ARROW_SIZE_CORNER = 2.5;
let x = point.heat
let y = point.humidity
// Point is out of bounds: Draw an arrow at the border
if (x < LIMIT_MIN || x > LIMIT_MAX || y < LIMIT_MIN || y > LIMIT_MAX) {
context.beginPath();
// top left corner
if (x < LIMIT_MIN && y < LIMIT_MIN) {
context.moveTo(LIMIT_MIN, LIMIT_MIN);
context.lineTo(LIMIT_MIN + ARROW_SIZE_CORNER, LIMIT_MIN);
context.lineTo(LIMIT_MIN, LIMIT_MIN + ARROW_SIZE_CORNER);
context.closePath();
context.fill();
// top right corner
} else if (x > LIMIT_MAX && y < LIMIT_MIN) {
context.moveTo(LIMIT_MAX, LIMIT_MIN);
context.lineTo(LIMIT_MAX - ARROW_SIZE_CORNER, LIMIT_MIN);
context.lineTo(LIMIT_MAX, LIMIT_MIN + ARROW_SIZE_CORNER);
context.closePath();
context.fill();
// bottom left corner
} else if (x < LIMIT_MIN && y > LIMIT_MAX) {
context.moveTo(LIMIT_MIN, LIMIT_MAX);
context.lineTo(LIMIT_MIN + ARROW_SIZE_CORNER, LIMIT_MAX);
context.lineTo(LIMIT_MIN, LIMIT_MAX - ARROW_SIZE_CORNER);
context.closePath();
context.fill();
// top right corner
} else if (x > LIMIT_MAX && y > LIMIT_MAX) {
context.moveTo(LIMIT_MAX, LIMIT_MAX);
context.lineTo(LIMIT_MAX - ARROW_SIZE_CORNER, LIMIT_MAX);
context.lineTo(LIMIT_MAX, LIMIT_MAX - ARROW_SIZE_CORNER);
context.closePath();
context.fill();
// left side
} else if (x < LIMIT_MIN) {
context.moveTo(LIMIT_MIN, y);
context.lineTo(LIMIT_MIN + ARROW_SIZE_SIDE, y + ARROW_SIZE_SIDE);
context.lineTo(LIMIT_MIN + ARROW_SIZE_SIDE, y - ARROW_SIZE_SIDE);
context.closePath();
context.fill();
// right side
} else if (x > LIMIT_MAX) {
context.moveTo(LIMIT_MAX, y);
context.lineTo(LIMIT_MAX - ARROW_SIZE_SIDE, y + ARROW_SIZE_SIDE);
context.lineTo(LIMIT_MAX - ARROW_SIZE_SIDE, y - ARROW_SIZE_SIDE);
context.closePath();
context.fill();
// top side
} else if (y < LIMIT_MIN) {
context.moveTo(x, LIMIT_MIN);
context.lineTo(x - ARROW_SIZE_SIDE, LIMIT_MIN + ARROW_SIZE_SIDE);
context.lineTo(x + ARROW_SIZE_SIDE, LIMIT_MIN + ARROW_SIZE_SIDE);
context.closePath();
context.fill();
// bottom side
} else if (y > LIMIT_MAX) {
context.moveTo(x, LIMIT_MAX);
context.lineTo(x - ARROW_SIZE_SIDE, LIMIT_MAX - ARROW_SIZE_SIDE);
context.lineTo(x + ARROW_SIZE_SIDE, LIMIT_MAX - ARROW_SIZE_SIDE);
context.closePath();
context.fill();
}
// Point is in bounds: Draw a dot
} else {
context.beginPath();
context.moveTo(0, 0);
context.arc(x, y, 2, 0, Math.PI * 2);
context.closePath();
context.fill();
}
2023-10-20 13:31:23 +02:00
};
function putGrid(context) {
context.lineWidth = 0.5;
context.strokeStyle = gridColor;
for (let x=0; x<=LIMIT_MAX; x+=GRID_STEP) {
context.beginPath();
context.moveTo(x, LIMIT_MIN);
context.lineTo(x, LIMIT_MAX);
context.stroke();
}
for (let x=-GRID_STEP; x>=LIMIT_MIN; x-=GRID_STEP) {
context.beginPath();
context.moveTo(x, LIMIT_MIN);
context.lineTo(x, LIMIT_MAX);
context.stroke();
}
for (let y=0; y<=LIMIT_MAX; y+=GRID_STEP) {
context.beginPath();
context.moveTo(LIMIT_MIN, y);
context.lineTo(LIMIT_MAX, y);
context.stroke();
}
for (let y=-GRID_STEP; y>=LIMIT_MIN; y-=GRID_STEP) {
context.beginPath();
context.moveTo(LIMIT_MIN, y);
context.lineTo(LIMIT_MAX, y);
context.stroke();
}
}
function putAxes(context) {
context.lineWidth = 0.75;
// heat axis (horizontal)
context.beginPath();
context.moveTo(LIMIT_MIN,0)
context.lineTo(LIMIT_MAX,0)
context.closePath();
context.stroke();
// humidity axis ()
context.beginPath();
context.moveTo(0,LIMIT_MIN)
context.lineTo(0,LIMIT_MAX)
context.closePath();
context.stroke();
}
// Cache diagram object for performance boost
let cachedVoronoiDiagram = null;
2023-10-20 17:22:48 +02:00
function getVoronoiDiagram(points, recalculate) {
2023-10-20 13:31:23 +02:00
if ((cachedVoronoiDiagram === null) || recalculate) {
2023-10-20 15:32:14 +02:00
let vbbox = {xl: LIMIT_MIN, xr: LIMIT_MAX, yt: LIMIT_MIN, yb: LIMIT_MAX};
2023-10-20 13:31:23 +02:00
let sites = []
2023-10-20 17:22:48 +02:00
for (let p of points) {
2023-10-20 13:31:23 +02:00
sites.push(biomePointToVoronoiPoint(p));
}
let voronoi = new Voronoi();
let diagram = null;
if (cachedVoronoiDiagram && recalculate) {
diagram = cachedVoronoiDiagram;
// This should improve performance
voronoi.recycle(diagram);
}
diagram = voronoi.compute(sites, vbbox);
cachedVoronoiDiagram = diagram;
return diagram;
} else {
return cachedVoronoiDiagram;
}
}
function getDrawContext() {
let canvas = document.getElementById("voronoiCanvas");
// TODO: Check for getContext support of browser
2023-10-20 16:31:11 +02:00
return canvas.getContext("2d");
2023-10-20 13:31:23 +02:00
}
function drawInit() {
let context = getDrawContext();
context.scale(voronoiCanvas.width/(LIMIT_MAX-LIMIT_MIN), voronoiCanvas.height/(LIMIT_MAX-LIMIT_MIN));
context.translate(-LIMIT_MIN, -LIMIT_MIN);
}
function getRenderedPoints(y) {
2023-10-20 17:22:48 +02:00
let points = [];
for (let p=0; p<biomePoints.length; p++) {
let point = biomePoints[p];
if (y >= point.min_y && y <= point.max_y) {
points.push(point);
}
}
return points;
}
function draw(y, recalculate) {
let context = getDrawContext();
// Clear draw area
context.fillStyle = clearColor;
context.fillRect(DRAW_MIN, DRAW_MIN, DRAW_MAX-DRAW_MIN, DRAW_MAX-DRAW_MIN);
let points = getRenderedPoints(y);
2023-10-20 17:22:48 +02:00
let diagram = getVoronoiDiagram(points, recalculate);
2023-10-20 16:31:11 +02:00
// Render cell colors
2023-10-20 13:31:23 +02:00
let colors = cellColors;
for (let c=0; c<diagram.cells.length; c++) {
let cell = diagram.cells[c];
// We use the biomeID to select a color
let biomeID = cell.site[biomeIDSymbol];
// This works because the biome ID is a number
let ccol = biomeID % colors.length;
2023-10-20 13:31:23 +02:00
context.fillStyle = colors[ccol];
context.beginPath();
for (let h=0; h<cell.halfedges.length; h++) {
let halfedge = cell.halfedges[h]
let start = halfedge.getStartpoint()
let end = halfedge.getEndpoint()
if (h === 0) {
context.moveTo(start.x, start.y);
} else {
context.lineTo(start.x, start.y);
}
context.lineTo(end.x, end.y);
}
context.closePath();
context.fill();
}
2023-10-20 16:31:11 +02:00
// If there's only 1 cell, we have to manually colorize it because
// the Voronoi script doesn't return that area in this special case.
2023-10-20 17:22:48 +02:00
if (points.length === 1 && diagram.cells.length === 1) {
2023-10-20 16:31:11 +02:00
// 1 cell means the whole area is filled
2023-10-21 11:08:39 +02:00
context.fillStyle = colors[points[0].id % colors.length];
2023-10-20 13:31:23 +02:00
context.fillRect(DRAW_MIN, DRAW_MIN, DRAW_MAX-DRAW_MIN, DRAW_MAX-DRAW_MIN);
}
2023-10-20 17:24:06 +02:00
if (points.length > 0) {
//putAxes(context);
putGrid(context);
}
2023-10-20 13:31:23 +02:00
2023-10-20 16:31:11 +02:00
// Render Voronoi cell edges
2023-10-21 11:35:22 +02:00
context.lineWidth = 1;
2023-10-20 13:31:23 +02:00
for (let e=0; e<diagram.edges.length; e++) {
let edge = diagram.edges[e];
2023-10-20 15:32:14 +02:00
if (edge.rSite === null) {
context.strokeStyle = "transparent";
} else {
context.strokeStyle = edgeColor;
}
2023-10-20 13:31:23 +02:00
context.beginPath();
context.moveTo(edge.va.x, edge.va.y);
context.lineTo(edge.vb.x, edge.vb.y);
context.closePath();
context.stroke();
}
2023-10-20 20:11:00 +02:00
let selElemID = null;
if (biomeSelector.selectedIndex !== -1) {
let selElem = biomeSelector.options[biomeSelector.selectedIndex];
2023-10-20 20:11:00 +02:00
let strID = selElem.id;
if (strID.startsWith("biome_list_element_")) {
let slice = strID.slice(19);
if (slice) {
selElemID = +slice;
}
}
}
2023-10-20 16:31:11 +02:00
// Render biome points
2023-10-21 12:08:24 +02:00
for (let point of points) {
2023-10-20 20:11:00 +02:00
let pointID = point.id;
// Highlight selected point
if (selElemID !== null && pointID === selElemID) {
2023-10-20 13:31:23 +02:00
context.fillStyle = pointColorSelected;
} else {
context.fillStyle = pointColor;
}
2023-10-20 20:11:00 +02:00
putPoint(context, point);
2023-10-21 12:08:24 +02:00
}
// Render biome point names
for (let point of points) {
let pointID = point.id;
// Highlight selected point
if (selElemID !== null && pointID === selElemID) {
context.fillStyle = "#FF8888FF";
} else {
context.fillStyle = "#FFFFFFAA";
}
2023-10-21 11:35:22 +02:00
putPointName(context, point);
2023-10-20 13:31:23 +02:00
}
2023-10-20 16:31:11 +02:00
// Render a special message if there are no biomes
2023-10-20 17:22:48 +02:00
if (points.length === 0) {
context.textAlign = "center";
context.fillStyle = "black";
context.textBaseline = "middle";
let msg;
if (biomePoints.length === 0) {
msg = "No biomes.";
} else {
msg = "No biomes in this Y layer.";
}
2023-10-21 11:35:22 +02:00
context.fillText(msg, MIDPOINT, MIDPOINT);
}
2023-10-20 13:31:23 +02:00
}
function rewriteBiomeSelector() {
biomeSelector.innerHTML = "";
for (let b=0; b<biomePoints.length; b++) {
let num = b+1;
let newElem = document.createElement("option");
newElem.value = num;
2023-10-20 20:11:00 +02:00
newElem.id = "biome_list_element_" + biomePoints[b].id;
2023-10-20 13:44:37 +02:00
let newElemText = document.createTextNode(biomePoints[b].name);
2023-10-20 13:31:23 +02:00
newElem.append(newElemText);
biomeSelector.append(newElem);
}
}
function updateWidgetStates() {
if (biomePoints.length === 0 || biomeSelector.selectedIndex === -1) {
removeBiomeButton.disabled = "disabled";
inputHeat.disabled = "disabled";
inputHumidity.disabled = "disabled";
2023-10-20 16:12:20 +02:00
inputMinY.disabled = "disabled";
inputMaxY.disabled = "disabled";
} else {
removeBiomeButton.disabled = "";
inputHeat.disabled = "";
inputHumidity.disabled = "";
2023-10-20 16:12:20 +02:00
inputMinY.disabled = "";
inputMaxY.disabled = "";
if (biomeSelector.selectedIndex !== -1) {
let selected = biomeSelector.options[biomeSelector.selectedIndex];
let point = biomePoints[biomeSelector.selectedIndex];
inputHeat.value = point.heat;
inputHumidity.value = point.humidity;
inputMinY.value = point.min_y;
inputMaxY.value = point.max_y;
}
}
}
2023-10-20 13:31:23 +02:00
biomeSelector.onchange = function() {
2023-10-20 17:22:48 +02:00
draw(getViewY(), false);
if (biomeSelector.selectedIndex !== -1) {
let selected = biomeSelector.options[biomeSelector.selectedIndex];
let point = biomePoints[biomeSelector.selectedIndex];
inputHeat.value = point.heat;
inputHumidity.value = point.humidity;
inputMinY.value = point.min_y;
inputMaxY.value = point.max_y;
}
updateWidgetStates();
2023-10-20 13:31:23 +02:00
}
function onChangeBiomeValueWidget(pointField, value) {
if (value === null) {
return;
}
if (biomeSelector.selectedIndex === -1) {
return;
}
let selected = biomeSelector.options[biomeSelector.selectedIndex];
if (selected === null) {
return;
}
let point = biomePoints[biomeSelector.selectedIndex];
point[pointField] = +value;
point.name = generateBiomeName(point.heat, point.humidity);
selected.innerText = point.name;
2023-10-20 17:22:48 +02:00
draw(getViewY(), true);
}
inputHeat.onchange = function() {
onChangeBiomeValueWidget("heat", this.value);
}
inputHumidity.onchange = function() {
onChangeBiomeValueWidget("humidity", this.value);
}
2023-10-20 16:12:20 +02:00
inputMinY.onchange = function() {
onChangeBiomeValueWidget("min_y", this.value);
}
inputMaxY.onchange = function() {
onChangeBiomeValueWidget("max_y", this.value);
}
2023-10-20 17:22:48 +02:00
inputViewY.onchange = function() {
draw(getViewY(), true);
}
2023-10-20 13:31:23 +02:00
addBiomeButton.onclick = function() {
let he = Math.round(Math.random()*100);
let hu = Math.round(Math.random()*100);
2023-10-20 13:44:37 +02:00
let newPoint = {
2023-10-20 16:31:11 +02:00
id: lastBiomeID,
2023-10-20 13:44:37 +02:00
name: generateBiomeName(he, hu),
heat: he,
min_y: MIN_Y_DEFAULT,
max_y: MAX_Y_DEFAULT,
2023-10-20 13:44:37 +02:00
humidity: hu,
};
biomePoints.push(newPoint);
2023-10-20 13:31:23 +02:00
let num = biomePoints.length
let newElem = document.createElement("option");
2023-10-20 17:22:48 +02:00
newElem.id = "biome_list_element_" + lastBiomeID;
2023-10-20 13:31:23 +02:00
newElem.value = "" + num;
2023-10-20 16:31:11 +02:00
2023-10-20 13:44:37 +02:00
let newElemText = document.createTextNode(newPoint.name);
2023-10-20 13:31:23 +02:00
newElem.append(newElemText);
biomeSelector.append(newElem);
newElem.selected = "selected";
2023-10-20 17:22:48 +02:00
draw(getViewY(), true);
updateWidgetStates();
2023-10-20 16:31:11 +02:00
lastBiomeID++;
2023-10-20 13:31:23 +02:00
}
removeBiomeButton.onclick = function() {
if (biomeSelector.selectedOptions.length === 0) {
return;
}
let firstIndex = null;
for (let o=0; o<biomeSelector.selectedOptions.length; o++) {
let opt = biomeSelector.selectedOptions[o]
let index = opt.index
if (firstIndex === null) {
firstIndex = index;
}
biomePoints.splice(index, 1);
opt.remove();
}
if (firstIndex !== null && biomePoints.length > 0) {
let newIndex = firstIndex-1;
if (newIndex < 0) {
newIndex = 0;
}
biomeSelector.options[newIndex].selected = "selected";
}
2023-10-20 17:22:48 +02:00
draw(getViewY(), true);
updateWidgetStates();
2023-10-20 13:31:23 +02:00
}
2023-10-20 16:31:11 +02:00
function selectPoint(point) {
for (let elem of biomeSelector.options) {
let strID = elem.id;
let elemID = null;
if (strID.startsWith("biome_list_element_")) {
let slice = strID.slice(19);
if (slice) {
elemID = +slice;
}
}
if (elemID !== null) {
if (point.id === elemID) {
if (elem.selected) {
console.log("Already selected!");
return [true, true];
}
elem.selected = "selected";
draw(getViewY(), true);
updateWidgetStates();
return [true, false];
}
}
}
return [false, false];
}
function getDistance(x1, y1, x2, y2) {
return Math.sqrt((x2 - x1)**2 + (y2 - y1)**2);
}
2023-10-21 01:55:49 +02:00
function canvasPixelCoordsToBiomeCoords(x, y) {
let w = (voronoiCanvas.width/(LIMIT_MAX-LIMIT_MIN));
let h = (voronoiCanvas.height/(LIMIT_MAX-LIMIT_MIN));
let heat = Math.round((x + LIMIT_MIN * w) / w);
let humidity = Math.round((y + LIMIT_MIN * w) / w);
return [heat, humidity];
}
function biomeCoordsToCanvasPixelCoords(heat, humidity) {
let w = (voronoiCanvas.width/(LIMIT_MAX-LIMIT_MIN));
let h = (voronoiCanvas.height/(LIMIT_MAX-LIMIT_MIN));
let pixelX = heat * w - LIMIT_MIN * w;
let pixelY = humidity * h - LIMIT_MIN * h;
return [pixelX, pixelY];
}
function getNearestPointFromCanvasPos(x, y, maxDist) {
let nearestPoint = null;
let nearestDist = null;
let points = getRenderedPoints(getViewY());
for (let i=0; i<points.length; i++) {
let point = points[i];
2023-10-21 01:55:49 +02:00
let [pixelX, pixelY] = biomeCoordsToCanvasPixelCoords(point.heat, point.humidity);
let dist = getDistance(x, y, pixelX, pixelY);
if (nearestPoint === null) {
nearestPoint = point;
nearestDist = dist;
} else if (dist < nearestDist) {
nearestPoint = point;
nearestDist = dist;
}
}
if (nearestDist < maxDist) {
return nearestPoint;
} else {
return null;
}
}
let mouseIsDown = false;
let dragDropPointID = null;
let dragDropPos = null;
2023-10-21 01:08:35 +02:00
function updatePointWhenDragged(pointID) {
if (pointID !== null) {
let selectedPoint = null;
let points = getRenderedPoints(getViewY());
for (let i=0; i<points.length; i++) {
if (points[i].id === dragDropPointID) {
selectedPoint = points[i];
2023-10-21 01:55:49 +02:00
let [newHeat, newHumidity] = canvasPixelCoordsToBiomeCoords(event.offsetX, event.offsetY);
selectedPoint.heat = newHeat;
selectedPoint.humidity = newHumidity;
2023-10-21 01:08:35 +02:00
selectedPoint.name = generateBiomeName(newHeat, newHumidity);
draw(getViewY(), true);
updateWidgetStates();
2023-10-21 01:08:35 +02:00
for (let elem of biomeSelector.options) {
let strID = elem.id;
let elemID = null;
if (strID.startsWith("biome_list_element_")) {
let slice = strID.slice(19);
if (slice) {
elemID = +slice;
}
}
if (elemID !== null && points[i].id === elemID) {
elem.innerText = selectedPoint.name;
break;
}
}
return;
}
}
}
2023-10-21 01:08:35 +02:00
}
voronoiCanvas.onmousemove = function(event) {
2023-10-21 01:48:45 +02:00
// drag-n-drop
2023-10-21 01:08:35 +02:00
if (mouseIsDown) {
updatePointWhenDragged(dragDropPointID);
}
2023-10-21 01:48:45 +02:00
// show coordinates
2023-10-21 01:55:49 +02:00
let [heat, humidity] = canvasPixelCoordsToBiomeCoords(event.offsetX, event.offsetY);
let html = "heat="+heat+"; humidity="+humidity;
coordinateDisplay.innerHTML = html;
}
voronoiCanvas.onmouseenter = function(event) {
// show coordinates
let [heat, humidity] = canvasPixelCoordsToBiomeCoords(event.offsetX, event.offsetY);
2023-10-21 01:48:45 +02:00
let html = "heat="+heat+"; humidity="+humidity;
coordinateDisplay.innerHTML = html;
2023-10-21 01:08:35 +02:00
}
2023-10-21 01:55:49 +02:00
2023-10-21 01:08:35 +02:00
voronoiCanvas.onmousedown = function(event) {
2023-10-21 01:48:45 +02:00
// select point by clicking.
// initiate drag-n-drop if already selected.
2023-10-21 01:08:35 +02:00
mouseIsDown = true;
let nearest = getNearestPointFromCanvasPos(event.offsetX, event.offsetY, 25);
if (nearest !== null) {
let success, alreadySelected
[success, alreadySelected] = selectPoint(nearest);
if (alreadySelected) {
dragDropPointID = nearest.id;
}
}
}
voronoiCanvas.onmouseup = function(event) {
2023-10-21 01:48:45 +02:00
// end drag-n-drop
2023-10-21 01:08:35 +02:00
updatePointWhenDragged(dragDropPointID);
mouseIsDown = false;
dragDropPos = null;
dragDropPointID = null;
}
voronoiCanvas.onmouseleave = function() {
2023-10-21 01:48:45 +02:00
// end drag-n-drop
mouseIsDown = false;
dragDropPointID = null;
dragDropPos = null;
2023-10-21 01:48:45 +02:00
coordinateDisplay.innerHTML = "&nbsp;";
}
2023-10-20 16:31:11 +02:00
window.addEventListener("load", drawInit);
2023-10-20 17:22:48 +02:00
window.addEventListener("load", function() {
draw(getViewY(), true);
})
2023-10-20 16:31:11 +02:00
window.addEventListener("load", rewriteBiomeSelector);
window.addEventListener("load", updateWidgetStates);