diff --git a/macrovision.css b/macrovision.css index 95357089..4d7be3a8 100644 --- a/macrovision.css +++ b/macrovision.css @@ -290,6 +290,14 @@ select.menu-item { color: #000; } +#rulers { + position: absolute; + display: block; + width: 100%; + height: 100%; + background: #00000000; +} + #display { display: block; width: 100%; @@ -720,6 +728,11 @@ option.filtered { top: 60%; } +#ruler { + left: 0%; + top: 80%; +} + #fit { right: 0%; top: 80%; diff --git a/macrovision.html b/macrovision.html index d50db2ca..b6310792 100644 --- a/macrovision.html +++ b/macrovision.html @@ -298,9 +298,14 @@ + + +
diff --git a/macrovision.js b/macrovision.js index 75d16d3d..af56f946 100644 --- a/macrovision.js +++ b/macrovision.js @@ -41,6 +41,12 @@ let sizeHandle = null; let worldSizeDirty = false; +let rulerMode = false; + +let rulers = []; + +let currentRuler = undefined; + const tagDefs = { "anthro": "Anthro", "feral": "Feral", @@ -532,6 +538,50 @@ function updateSizes(dirtyOnly = false) { document.querySelector("#ground").style.top = pos2pix({x: 0, y: 0}).y + "px"; + drawRulers(); +} + +function drawRulers() { + const canvas = document.querySelector("#rulers"); + + /** @type {CanvasRenderingContext2D} */ + + const ctx = canvas.getContext("2d"); + ctx.canvas.width = canvas.clientWidth; + ctx.canvas.height = canvas.clientHeight; + + + rulers.concat(currentRuler ? [currentRuler] : []).forEach(rulerDef => { + ctx.save(); + ctx.beginPath(); + const start = pos2pix({x: rulerDef.x0, y: rulerDef.y0}); + const end = pos2pix({x: rulerDef.x1, y: rulerDef.y1}); + ctx.moveTo(start.x, start.y); + ctx.lineTo(end.x, end.y); + ctx.lineWidth = 5; + ctx.stroke(); + const center = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 }; + ctx.fillStyle = "#eeeeee"; + ctx.font = 'normal 24pt coda'; + ctx.translate(center.x, center.y); + let angle = Math.atan2(end.y - start.y, end.x - start.x); + + if (angle < -Math.PI/2) { + angle += Math.PI; + } + if (angle > Math.PI/2) { + angle -= Math.PI; + } + ctx.rotate(angle); + const offsetX = Math.cos(angle + Math.PI/2); + const offsetY = Math.sin(angle + Math.PI/2); + + const distance = Math.sqrt(Math.pow(rulerDef.y1 - rulerDef.y0, 2) + Math.pow(rulerDef.x1 - rulerDef.x0, 2)); + const distanceInUnits = math.unit(distance, "meters").to(document.querySelector("#options-height-unit").value); + const textSize = ctx.measureText(distanceInUnits.format({ precision: 3})); + ctx.fillText(distanceInUnits.format({ precision: 3}), -offsetX * 10 - textSize.width / 2, -offsetY*10); + ctx.restore(); + }); } function drawScales(ifDirty = false) { @@ -1855,6 +1905,11 @@ function prepareSidebar() { name: "Add Image", id: "menu-add-image", icon: "fas fa-camera" + }, + { + name: "Clear Rulers", + id: "menu-clear-rulers", + icon: "fas fa-ruler" } ].forEach(entry => { const buttonHolder = document.createElement("div"); @@ -2582,13 +2637,15 @@ document.addEventListener("DOMContentLoaded", () => { }); document.querySelector("#world").addEventListener("touchstart", e => { - panning = true; - panOffsetX = e.touches[0].clientX; - panOffsetY = e.touches[0].clientY; - e.preventDefault(); - Object.keys(entities).forEach(key => { - document.querySelector("#entity-" + key).classList.add("no-transition"); - }); + if (!rulerMode) { + panning = true; + panOffsetX = e.touches[0].clientX; + panOffsetY = e.touches[0].clientY; + e.preventDefault(); + Object.keys(entities).forEach(key => { + document.querySelector("#entity-" + key).classList.add("no-transition"); + }); + } }); document.querySelector("#world").addEventListener("touchend", e => { @@ -2597,6 +2654,39 @@ document.addEventListener("DOMContentLoaded", () => { document.querySelector("#entity-" + key).classList.remove("no-transition"); }); }); + document.querySelector("#world").addEventListener("mousedown", e => { + // only left mouse clicks + if (e.which == 1 && rulerMode) { + let entX = document.querySelector("#entities").getBoundingClientRect().x; + let entY = document.querySelector("#entities").getBoundingClientRect().y; + const pos = pix2pos({ x: e.clientX - entX, y: e.clientY - entY }); + currentRuler = { x0: pos.x, y0: pos.y, x1: pos.y, y1: pos.y }; + } + }); + document.querySelector("#world").addEventListener("mouseup", e => { + // only left mouse clicks + if (e.which == 1 && currentRuler) { + rulers.push(currentRuler); + currentRuler = null; + rulerMode = false; + } + }); + + document.querySelector("#world").addEventListener("touchstart", e => { + if (rulerMode) { + let entX = document.querySelector("#entities").getBoundingClientRect().x; + let entY = document.querySelector("#entities").getBoundingClientRect().y; + const pos = pix2pos({ x: e.touches[0].clientX - entX, y: e.touches[0].clientY - entY }); + currentRuler = { x0: pos.x, y0: pos.y, x1: pos.y, y1: pos.y }; + } + }); + document.querySelector("#world").addEventListener("touchend", e => { + if (currentRuler) { + rulers.push(currentRuler); + currentRuler = null; + rulerMode = false; + } + }); document.querySelector("body").appendChild(testCtx.canvas); @@ -2821,6 +2911,23 @@ document.addEventListener("DOMContentLoaded", () => { sizeHandle = null; }); + document.querySelector("#ruler").addEventListener("click", e => { + rulerMode = !rulerMode; + if (rulerMode) { + toast("Ready to draw a ruler mark"); + } else { + toast("Cancelled ruler mode"); + } + }); + + document.querySelector("#ruler").addEventListener("mousedown", e => { + e.stopPropagation(); + }); + + document.querySelector("#ruler").addEventListener("touchstart", e => { + e.stopPropagation(); + }); + document.querySelector("#fit").addEventListener("click", e => { if (selected) { let targets = {}; @@ -2937,6 +3044,11 @@ document.addEventListener("DOMContentLoaded", () => { }) + document.querySelector("#menu-clear-rulers").addEventListener("click", e => { + rulers = []; + drawRulers(); + }); + document.addEventListener("paste", e => { let index = 0; @@ -3532,6 +3644,28 @@ function clearFilter() { }); } +document.addEventListener("mousemove", (e) => { + if (currentRuler) { + let entX = document.querySelector("#entities").getBoundingClientRect().x; + let entY = document.querySelector("#entities").getBoundingClientRect().y; + let position = pix2pos({ x: e.clientX - entX, y: e.clientY - entY }); + currentRuler.x1 = position.x; + currentRuler.y1 = position.y; + } + drawRulers(); +}); + +document.addEventListener("touchmove", (e) => { + if (currentRuler) { + let entX = document.querySelector("#entities").getBoundingClientRect().x; + let entY = document.querySelector("#entities").getBoundingClientRect().y; + let position = pix2pos({ x: e.touches[0].clientX - entX, y: e.touches[0].clientY - entY }); + currentRuler.x1 = position.x; + currentRuler.y1 = position.y; + } + drawRulers(); +}); + document.addEventListener("mousemove", (e) => { if (clicked) { let position = pix2pos({ x: e.clientX - dragOffsetX, y: e.clientY - dragOffsetY });