diff --git a/manual.html b/manual.html index b69d10c..bbe6686 100644 --- a/manual.html +++ b/manual.html @@ -45,6 +45,8 @@
Hover with the mouse cursor over the diagram to see the heat and humidity coordinates at this position. You can click on a dot to select it, click on it again and hold down the mouse button to drag it.
+When you hover the diagram, a small grabbing widget will apper in the bottom left corner. You can use this to resize the whole diagram. Hold down Shift while resizing to preserve the aspect ratio.
+Here you can see a list of all currently active biomes. Each biome has a name of your choice. The program always starts with a default biome at (50, 50). Select a biome in the list so you can edit it.
diff --git a/mibpov.js b/mibpov.js index 730c4ee..af9f4cc 100644 --- a/mibpov.js +++ b/mibpov.js @@ -7,6 +7,12 @@ const MAX_Y_DEFAULT = 31000 // Draw a grid line every GRID_STEP units const GRID_STEP = 10 +// Size of the resizing corner +const RESIZE_CORNER = 18; + +// Minimum canvas side length (px) +const MIN_CANVAS_SIZE = 100; + // Grid widths. We use lower grid widths // as the grid becomes more crammed. // There are 4 levels from 0 to 3. @@ -221,6 +227,10 @@ function addBiome(biomeDef) { lastBiomeID++; } +// Current cursor position on the canvas +let canvas_cursor_x = null; +let canvas_cursor_y = null; + // Add a default biome at the midpoint addBiome({name: "default", heat:midpoint_heat, humidity:midpoint_humidity, min_y: MIN_Y_DEFAULT, max_y: MAX_Y_DEFAULT}) @@ -919,6 +929,27 @@ function draw(recalculate) { putPointName(context, point); } } + + if (canvas_cursor_x !== null) { + const RESIZE_CORNER = 14; + context.beginPath(); + context.moveTo(voronoiCanvas.width, voronoiCanvas.height - RESIZE_CORNER); + context.lineTo(voronoiCanvas.width - RESIZE_CORNER, voronoiCanvas.height); + context.lineTo(voronoiCanvas.width, voronoiCanvas.height); + context.fillStyle = "#80808080"; + context.closePath(); + context.fill(); + + context.beginPath(); + context.lineWidth = 1; + for (let c = RESIZE_CORNER; c>0; c-=4) { + context.moveTo(voronoiCanvas.width, voronoiCanvas.height - c); + context.lineTo(voronoiCanvas.width - c, voronoiCanvas.height); + } + context.strokeStyle = "#00000080"; + context.closePath(); + context.stroke(); + } return true; } @@ -1096,6 +1127,15 @@ function getNearestPointFromCanvasPos(x, y, maxDist) { // Whether the mouse is currently pressed let mouseIsDown = false; +// Whether the canvas is being resized +let resizing = false; +// Start coordinates of canvas resize or null if not resizing +let resizing_start_pos_x = null; +let resizing_start_pos_y = null; +// Start size of canvas when resizing or null if not resizing +let resizing_start_size_x = null; +let resizing_start_size_y = null; + // Coordinates of where the drag-n-drop started // or is about to start let dragDropStartPos = null; @@ -1149,6 +1189,10 @@ function updateAltitudeText() { /* Update the text that shows the biome coordinates of the cursor when it's on the diagram */ function updateCoordinateDisplay(pixelX, pixelY) { + if (pixelX === null || pixelY === null) { + coordinateDisplay.innerHtml = " "; + return; + } // show coordinates let [heat, humidity] = canvasPixelCoordsToBiomeCoords(pixelX, pixelY); if (!drawError) { @@ -1162,21 +1206,27 @@ function updateCoordinateDisplay(pixelX, pixelY) { /* Updates and changes the cursor type on the diagram canvas depending on whether we can select, drag or do nothing at the pointed position */ -function updateDragDropCursorStatus() { +function updateCanvasCursorStatus(x, y) { + // Show resize cursor at the bottom right corner + if (resizing || (x > voronoiCanvas.width - RESIZE_CORNER && y > voronoiCanvas.height - RESIZE_CORNER)) { + voronoiCanvas.style.cursor = "nwse-resize"; + return + } + if (drawError || !showPoints) { // a special message is shown; use auto cursor voronoiCanvas.style.cursor = "auto"; return } - let nearest = getNearestPointFromCanvasPos(event.offsetX, event.offsetY, POINT_SELECT_DISTANCE); + let nearest = getNearestPointFromCanvasPos(x, y, POINT_SELECT_DISTANCE); if (nearest !== null) { let [id, elem] = getSelectedBiomeIDAndElement(); if (id !== null && nearest.id === id) { // This cursor indicates we can grab the point voronoiCanvas.style.cursor = "grab"; } else { - // This cursor indicates we can grab the point + // This cursor indicates we can select the point voronoiCanvas.style.cursor = "crosshair"; } } else { @@ -1237,9 +1287,58 @@ function initBiomeColorSelectors() { /***** EVENTS *****/ -/* Canvas events */ +/* Body events */ +document.body.onmousemove = function(event) { + if (resizing) { + // Get x,y position of canvas + let bodyRect = document.body.getBoundingClientRect(); + let canvasRect = voronoiCanvas.getBoundingClientRect(); + let cx = canvasRect.left - bodyRect.left; + let cy = canvasRect.top - bodyRect.top; + // Calculate new size + let rx = event.pageX - resizing_start_pos_x - cx; + let ry = event.pageY - resizing_start_pos_y - cy; + + // Limit the width + let maxX = (bodyRect.width - cx) - 20; + + // Resize + voronoiCanvas.width = Math.min(maxX, Math.max(MIN_CANVAS_SIZE, resizing_start_size_x + rx)); + + // Holding down Shift preserves aspect ratio + if (event.shiftKey) { + voronoiCanvas.height = voronoiCanvas.width; + } else { + voronoiCanvas.height = Math.max(MIN_CANVAS_SIZE, resizing_start_size_y + ry); + } + draw(false); + return; + } +} +document.body.onmouseup = function(event) { + if (resizing) { + resizing = false; + updateCanvasCursorStatus(event.offsetX, event.offsetY); + } +} +document.body.onmouseleave = function(event) { + if (resizing) { + resizing = false; + updateCanvasCursorStatus(event.offsetX, event.offsetY); + } +} + +/* Canvas events */ voronoiCanvas.onmousemove = function(event) { + if (resizing) { + updateCoordinateDisplay(event.offsetX, event.offsetY); + updateCanvasCursorStatus(event.offsetX, event.offsetY); + canvas_cursor_x = event.offsetX; + canvas_cursor_y = event.offsetY; + draw(false); + return + } // update drag-n-drop state if (dragDropState !== 2 && dragDropPointID !== null && mouseIsDown && dragDropStartPos !== null) { let dist = getDistance(dragDropStartPos.x, dragDropStartPos.y, event.offsetX, event.offsetY) @@ -1254,17 +1353,34 @@ voronoiCanvas.onmousemove = function(event) { updatePointWhenDragged(dragDropPointID); } updateCoordinateDisplay(event.offsetX, event.offsetY); - updateDragDropCursorStatus(); + updateCanvasCursorStatus(event.offsetX, event.offsetY); + canvas_cursor_x = event.offsetX; + canvas_cursor_y = event.offsetY; + draw(false); } voronoiCanvas.onmouseenter = function(event) { updateCoordinateDisplay(event.offsetX, event.offsetY); - updateDragDropCursorStatus(); + updateCanvasCursorStatus(event.offsetX, event.offsetY); + canvas_cursor_x = event.offsetX; + canvas_cursor_y = event.offsetY; + draw(false); } voronoiCanvas.onmousedown = function(event) { // select point by clicking. // initiate drag-n-drop if already selected. mouseIsDown = true; + + // Resizing the canvas + if (event.offsetX > voronoiCanvas.width - RESIZE_CORNER && event.offsetY > voronoiCanvas.height - RESIZE_CORNER) { + resizing_start_pos_x = event.offsetX; + resizing_start_pos_y = event.offsetY; + resizing_start_size_x = +this.width; + resizing_start_size_y = +this.height; + resizing = true; + return; + } + if (drawError || !showPoints) { // Points need to be shown for drag-n-drop to work return; @@ -1281,7 +1397,7 @@ voronoiCanvas.onmousedown = function(event) { if (alreadySelected) { dragDropState = 1; } - updateDragDropCursorStatus(); + updateCanvasCursorStatus(event.offsetX, event.offsetY); } } voronoiCanvas.onmouseup = function(event) { @@ -1300,7 +1416,10 @@ voronoiCanvas.onmouseleave = function() { dragDropStartPos = null; dragDropPointID = null; dragDropState = 0; + canvas_cursor_x = null; + canvas_cursor_y = null; coordinateDisplay.innerHTML = " "; + draw(false); } /* Biome list events */