diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..5a938ce1 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "tabWidth": 4, + "useTabs": false +} diff --git a/macrovision.js b/macrovision.js index dba601c6..1b8a9a53 100644 --- a/macrovision.js +++ b/macrovision.js @@ -54,303 +54,300 @@ let currentRuler = undefined; let webkitCanvasBug = false; const tagDefs = { - "anthro": "Anthro", - "feral": "Feral", - "taur": "Taur", - "naga": "Naga", - "goo": "Goo" -} + anthro: "Anthro", + feral: "Feral", + taur: "Taur", + naga: "Naga", + goo: "Goo", +}; math.createUnit({ - "ShoeSizeMensUS": { + ShoeSizeMensUS: { prefixes: "long", definition: "0.3333333333333333333 inches", - offset: 22 - } -}) + offset: 22, + }, +}); math.createUnit("humans", { - definition: "5.75 feet" + definition: "5.75 feet", }); math.createUnit("story", { definition: "12 feet", - prefixes: "long" + prefixes: "long", }); math.createUnit("stories", { definition: "12 feet", - prefixes: "long" + prefixes: "long", }); math.createUnit("buses", { definition: "11.95 meters", - prefixes: "long" + prefixes: "long", }); math.createUnit("marathons", { definition: "26.2 miles", - prefixes: "long" + prefixes: "long", }); math.createUnit("timezones", { definition: "1037.54167 miles", prefixes: "long", - aliases: ["timezone", "timezones"] + aliases: ["timezone", "timezones"], }); math.createUnit("nauticalMiles", { definition: "6080 feet", prefixes: "long", - aliases: ["nauticalMile", "nauticalMiles"] + aliases: ["nauticalMile", "nauticalMiles"], }); math.createUnit("fathoms", { definition: "6 feet", prefixes: "long", - aliases: ["fathom", "fathoms"] + aliases: ["fathom", "fathoms"], }); math.createUnit("U", { definition: "1.75 inches", - prefixes: "short" + prefixes: "short", }); math.createUnit("earths", { definition: "12756km", prefixes: "long", - aliases: ["earth", "earths", "Earth", "Earths"] + aliases: ["earth", "earths", "Earth", "Earths"], }); math.createUnit("lightsecond", { definition: "299792458 meters", - prefixes: "long" + prefixes: "long", }); math.createUnit("lightseconds", { definition: "299792458 meters", - prefixes: "long" + prefixes: "long", }); math.createUnit("parsec", { definition: "3.086e16 meters", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("parsecs", { definition: "3.086e16 meters", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("lightyears", { definition: "9.461e15 meters", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("AU", { - definition: "149597870700 meters" -}) + definition: "149597870700 meters", +}); math.createUnit("AUs", { - definition: "149597870700 meters" -}) + definition: "149597870700 meters", +}); math.createUnit("dalton", { definition: "1.66e-27 kg", - prefixes: "long" + prefixes: "long", }); math.createUnit("daltons", { definition: "1.66e-27 kg", - prefixes: "long" + prefixes: "long", }); math.createUnit("solarradii", { definition: "695990 km", - prefixes: "long" + prefixes: "long", }); math.createUnit("solarmasses", { definition: "2e30 kg", - prefixes: "long" + prefixes: "long", }); math.createUnit("galaxy", { definition: "105700 lightyears", - prefixes: "long" + prefixes: "long", }); math.createUnit("galaxies", { definition: "105700 lightyears", - prefixes: "long" + prefixes: "long", }); math.createUnit("universe", { definition: "93.016e9 lightyears", - prefixes: "long" + prefixes: "long", }); math.createUnit("universes", { definition: "93.016e9 lightyears", - prefixes: "long" + prefixes: "long", }); math.createUnit("multiverse", { definition: "1e30 lightyears", - prefixes: "long" + prefixes: "long", }); math.createUnit("multiverses", { definition: "1e30 lightyears", - prefixes: "long" + prefixes: "long", }); math.createUnit("pinHeads", { definition: "3.14159 mm^2", - prefixes: "long" + prefixes: "long", }); math.createUnit("dinnerPlates", { definition: "95 inches^2", - prefixes: "long" + prefixes: "long", }); math.createUnit("suburbanHouses", { definition: "2000 feet^2", - prefixes: "long" + prefixes: "long", }); math.createUnit("footballFields", { definition: "57600 feet^2", - prefixes: "long" + prefixes: "long", }); math.createUnit("blocks", { definition: "20000 m^2", prefixes: "long", - aliases: ["block", "blocks"] + aliases: ["block", "blocks"], }); math.createUnit("peopleInRural", { - definition: "0.02 miles^2" -}) + definition: "0.02 miles^2", +}); math.createUnit("peopleInManhattan", { - definition: "15 m^2" -}) + definition: "15 m^2", +}); math.createUnit("peopleInLooseCrowd", { - definition: "1 m^2" -}) + definition: "1 m^2", +}); math.createUnit("peopleInCrowd", { - definition: "0.3333333333333333 m^2" -}) + definition: "0.3333333333333333 m^2", +}); math.createUnit("peopleInDenseCrowd", { - definition: "0.2 m^2" -}) + definition: "0.2 m^2", +}); math.createUnit("people", { definition: "75 liters", - prefixes: "long" + prefixes: "long", }); math.createUnit("shippingContainers", { definition: "1169 ft^3", - prefixes: "long" + prefixes: "long", }); math.createUnit("olympicPools", { definition: "2500 m^3", - prefixes: "long" + prefixes: "long", }); math.createUnit("oceans", { definition: "700000000 km^3", - prefixes: "long" + prefixes: "long", }); math.createUnit("earthVolumes", { definition: "1.0867813e12 km^3", - prefixes: "long" + prefixes: "long", }); math.createUnit("universeVolumes", { definition: "4.2137775e+32 lightyears^3", - prefixes: "long" + prefixes: "long", }); math.createUnit("multiverseVolumes", { definition: "5.2359878e+89 lightyears^3", - prefixes: "long" + prefixes: "long", }); math.createUnit("peopleMass", { definition: "80 kg", - prefixes: "long" + prefixes: "long", }); math.createUnit("cars", { definition: "1250kg", - prefixes: "long" + prefixes: "long", }); math.createUnit("busMasses", { definition: "15000kg", - prefixes: "long" + prefixes: "long", }); math.createUnit("earthMass", { definition: "5.97e24 kg", - prefixes: "long" + prefixes: "long", }); math.createUnit("kcal", { definition: "4184 joules", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("foodPounds", { definition: "867 kcal", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("foodKilograms", { definition: "1909 kcal", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("chickenNuggets", { definition: "42 kcal", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("peopleEaten", { definition: "125000 kcal", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("villagesEaten", { definition: "1000 peopleEaten", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("townsEaten", { definition: "10000 peopleEaten", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("citiesEaten", { definition: "100000 peopleEaten", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("metrosEaten", { definition: "1000000 peopleEaten", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("barn", { definition: "10e-28 m^2", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("barns", { definition: "10e-28 m^2", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("points", { definition: "0.013888888888888888888888888 inches", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("beardSeconds", { definition: "10 nanometers", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("smoots", { definition: "5.5833333 feet", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("furlongs", { definition: "660 feet", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("nanoacres", { definition: "1e-9 acres", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("barnMegaparsecs", { definition: "1 barn megaparsec", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("firkins", { definition: "90 lb", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("donkeySeconds", { definition: "250 joules", - prefixes: "long" -}) + prefixes: "long", +}); math.createUnit("HU", { definition: "0.75 inches", - aliases: [ - "HUs", - "hammerUnits" - ] + aliases: ["HUs", "hammerUnits"], }); const defaultUnits = { @@ -359,55 +356,49 @@ const defaultUnits = { customary: "feet", relative: "stories", quirky: "smoots", - human: "humans" + human: "humans", }, area: { metric: "meters^2", customary: "feet^2", relative: "footballFields", quirky: "nanoacres", - human: "peopleInCrowd" + human: "peopleInCrowd", }, volume: { metric: "liters", customary: "gallons", relative: "olympicPools", volume: "barnMegaparsecs", - human: "people" + human: "people", }, mass: { metric: "kilograms", customary: "lbs", relative: "peopleMass", quirky: "firkins", - human: "peopleMass" + human: "peopleMass", }, energy: { metric: "kJ", customary: "kcal", relative: "chickenNuggets", quirky: "donkeySeconds", - human: "peopleEaten" - } -} + human: "peopleEaten", + }, +}; const unitChoices = { length: { - "metric": [ + metric: [ "angstroms", "millimeters", "centimeters", "meters", "kilometers", ], - "customary": [ - "inches", - "feet", - "yards", - "miles", - "nauticalMiles", - ], - "relative": [ + customary: ["inches", "feet", "yards", "miles", "nauticalMiles"], + relative: [ "ShoeSizeMensUS", "stories", "buses", @@ -421,9 +412,9 @@ const unitChoices = { "parsecs", "galaxies", "universes", - "multiverses" + "multiverses", ], - "quirky": [ + quirky: [ "beardSeconds", "points", "smoots", @@ -432,56 +423,31 @@ const unitChoices = { "U", "fathoms", ], - "human": [ - "humans", - ] + human: ["humans"], }, area: { - "metric": [ - "cm^2", - "meters^2", - "kilometers^2", - ], - "customary": [ - "inches^2", - "feet^2", - "acres", - "miles^2" - ], - "relative": [ + metric: ["cm^2", "meters^2", "kilometers^2"], + customary: ["inches^2", "feet^2", "acres", "miles^2"], + relative: [ "pinHeads", "dinnerPlates", "suburbanHouses", "footballFields", - "blocks" + "blocks", ], - "quirky": [ - "barns", - "nanoacres" - ], - "human": [ + quirky: ["barns", "nanoacres"], + human: [ "peopleInRural", "peopleInManhattan", "peopleInLooseCrowd", "peopleInCrowd", "peopleInDenseCrowd", - ] + ], }, volume: { - "metric": [ - "milliliters", - "liters", - "m^3", - ], - "customary": [ - "in^3", - "floz", - "cups", - "pints", - "quarts", - "gallons", - ], - "relative": [ + metric: ["milliliters", "liters", "m^3"], + customary: ["in^3", "floz", "cups", "pints", "quarts", "gallons"], + relative: [ "shippingContainers", "olympicPools", "oceans", @@ -489,62 +455,30 @@ const unitChoices = { "universeVolumes", "multiverseVolumes", ], - "quirky": [ - "barnMegaparsecs" - ], - "human": [ - "people", - ] + quirky: ["barnMegaparsecs"], + human: ["people"], }, mass: { - "metric": [ - "kilograms", - "milligrams", - "grams", - "tonnes", - ], - "customary": [ - "lbs", - "ounces", - "tons" - ], - "relative": [ - "cars", - "busMasses", - "earthMass", - "solarmasses" - ], - "quirky": [ - "firkins" - ], - "human": [ - "peopleMass", - ] + metric: ["kilograms", "milligrams", "grams", "tonnes"], + customary: ["lbs", "ounces", "tons"], + relative: ["cars", "busMasses", "earthMass", "solarmasses"], + quirky: ["firkins"], + human: ["peopleMass"], }, energy: { - "metric": [ - "kJ", - "foodKilograms" - ], - "customary": [ - "kcal", - "foodPounds" - ], - "relative": [ - "chickenNuggets", - ], - "quirky": [ - "donkeySeconds" - ], - "human": [ + metric: ["kJ", "foodKilograms"], + customary: ["kcal", "foodPounds"], + relative: ["chickenNuggets"], + quirky: ["donkeySeconds"], + human: [ "peopleEaten", "villagesEaten", "townsEaten", "citiesEaten", "metrosEaten", - ] - } -} + ], + }, +}; const config = { height: math.unit(1500, "meters"), x: 0, @@ -556,39 +490,42 @@ const config = { drawXAxis: false, autoMass: "off", autoFoodIntake: false, - autoPreyCapacity: "off" -} - -const availableEntities = { - -} - -const availableEntitiesByName = { + autoPreyCapacity: "off", +}; -} +const availableEntities = {}; -const entities = { +const availableEntitiesByName = {}; -} +const entities = {}; function constrainRel(coords) { - const worldWidth = config.height.toNumber("meters") / canvasHeight * canvasWidth; + const worldWidth = + (config.height.toNumber("meters") / canvasHeight) * canvasWidth; const worldHeight = config.height.toNumber("meters"); if (altHeld) { return coords; } return { - x: Math.min(Math.max(coords.x, -worldWidth / 2 + config.x), worldWidth / 2 + config.x), - y: Math.min(Math.max(coords.y, config.y), worldHeight + config.y) - } + x: Math.min( + Math.max(coords.x, -worldWidth / 2 + config.x), + worldWidth / 2 + config.x + ), + y: Math.min(Math.max(coords.y, config.y), worldHeight + config.y), + }; } // not using constrainRel anymore function snapPos(coords) { return { x: coords.x, - y: (!config.groundSnap || altHeld) ? coords.y : (Math.abs(coords.y) < config.height.toNumber("meters")/20 ? 0 : coords.y) + y: + !config.groundSnap || altHeld + ? coords.y + : Math.abs(coords.y) < config.height.toNumber("meters") / 20 + ? 0 + : coords.y, }; } @@ -598,38 +535,53 @@ function adjustAbs(coords, oldHeight, newHeight) { const x = (coords.x - config.x) * ratio + config.x; const y = (coords.y - config.y) * ratio + config.y; - return { x: x, y: y}; + return { x: x, y: y }; } function pos2pix(coords) { - const worldWidth = config.height.toNumber("meters") / canvasHeight * canvasWidth; + const worldWidth = + (config.height.toNumber("meters") / canvasHeight) * canvasWidth; const worldHeight = config.height.toNumber("meters"); - const x = ((coords.x - config.x) / worldWidth + 0.5) * (canvasWidth - 50) + 50; - const y = (1 - (coords.y - config.y) / worldHeight) * (canvasHeight - 50) + 50; + const x = + ((coords.x - config.x) / worldWidth + 0.5) * (canvasWidth - 50) + 50; + const y = + (1 - (coords.y - config.y) / worldHeight) * (canvasHeight - 50) + 50; return { x: x, y: y }; } function pix2pos(coords) { - const worldWidth = config.height.toNumber("meters") / canvasHeight * canvasWidth; + const worldWidth = + (config.height.toNumber("meters") / canvasHeight) * canvasWidth; const worldHeight = config.height.toNumber("meters"); - const x = (((coords.x - 50) / (canvasWidth - 50)) - 0.5) * worldWidth + config.x; - const y = (1 - ((coords.y - 50) / (canvasHeight - 50))) * worldHeight + config.y; + const x = + ((coords.x - 50) / (canvasWidth - 50) - 0.5) * worldWidth + config.x; + const y = + (1 - (coords.y - 50) / (canvasHeight - 50)) * worldHeight + config.y; return { x: x, y: y }; } function updateEntityElement(entity, element) { const position = pos2pix({ x: element.dataset.x, y: element.dataset.y }); const view = entity.view; - const form = entity.form + const form = entity.form; element.style.left = position.x + "px"; element.style.top = position.y + "px"; element.style.setProperty("--xpos", position.x + "px"); - element.style.setProperty("--entity-height", "'" + entity.views[view].height.to(config.height.units[0].unit.name).format({ precision: 2 }) + "'"); - const pixels = math.divide(entity.views[view].height, config.height) * (canvasHeight - 50); + element.style.setProperty( + "--entity-height", + "'" + + entity.views[view].height + .to(config.height.units[0].unit.name) + .format({ precision: 2 }) + + "'" + ); + const pixels = + math.divide(entity.views[view].height, config.height) * + (canvasHeight - 50); const extra = entity.views[view].image.extra; const bottom = entity.views[view].image.bottom; const bonus = (extra ? extra : 1) * (1 / (1 - (bottom ? bottom : 0))); @@ -647,13 +599,20 @@ function updateEntityElement(entity, element) { element.style.setProperty("--brightness", entity.brightness); if (entity.views[view].rename) - element.querySelector(".entity-name").innerText = entity.name == "" ? "" : entity.views[view].name; - else if (entity.forms !== undefined && Object.keys(entity.forms).length > 0 && entity.forms[form].rename) - element.querySelector(".entity-name").innerText = entity.name == "" ? "" : entity.forms[form].name; - else - element.querySelector(".entity-name").innerText = entity.name; - - const bottomName = document.querySelector("#bottom-name-" + element.dataset.key); + element.querySelector(".entity-name").innerText = + entity.name == "" ? "" : entity.views[view].name; + else if ( + entity.forms !== undefined && + Object.keys(entity.forms).length > 0 && + entity.forms[form].rename + ) + element.querySelector(".entity-name").innerText = + entity.name == "" ? "" : entity.forms[form].name; + else element.querySelector(".entity-name").innerText = entity.name; + + const bottomName = document.querySelector( + "#bottom-name-" + element.dataset.key + ); bottomName.style.left = position.x + entityX + "px"; bottomName.style.bottom = "0vh"; @@ -665,7 +624,10 @@ function updateEntityElement(entity, element) { topName.style.top = "20vh"; topName.innerText = entity.name; - if (entity.views[view].height.toNumber("meters") / 10 > config.height.toNumber("meters")) { + if ( + entity.views[view].height.toNumber("meters") / 10 > + config.height.toNumber("meters") + ) { topName.classList.add("top-name-needed"); } else { topName.classList.remove("top-name-needed"); @@ -673,113 +635,231 @@ function updateEntityElement(entity, element) { updateInfo(); } -let ratioInfo +let ratioInfo; function updateInfo() { - let text = "" + let text = ""; if (config.showRatios) { - if (selectedEntity !== null && prevSelectedEntity !== null && selectedEntity !== prevSelectedEntity) { + if ( + selectedEntity !== null && + prevSelectedEntity !== null && + selectedEntity !== prevSelectedEntity + ) { let first = selectedEntity.currentView.height; let second = prevSelectedEntity.currentView.height; - + if (first.toNumber("meters") < second.toNumber("meters")) { - text += selectedEntity.name + " is " + math.format(math.divide(second, first), { precision: 5 }) + " times smaller than " + prevSelectedEntity.name; + text += + selectedEntity.name + + " is " + + math.format(math.divide(second, first), { precision: 5 }) + + " times smaller than " + + prevSelectedEntity.name; } else { - text += selectedEntity.name + " is " + math.format(math.divide(first, second), { precision: 5 })+ " times taller than " + prevSelectedEntity.name; + text += + selectedEntity.name + + " is " + + math.format(math.divide(first, second), { precision: 5 }) + + " times taller than " + + prevSelectedEntity.name; } text += "\n"; - let apparentHeight = math.multiply(math.divide(second, first), math.unit(6, "feet")); + let apparentHeight = math.multiply( + math.divide(second, first), + math.unit(6, "feet") + ); if (config.units === "metric") { apparentHeight = apparentHeight.to("meters"); } - text += prevSelectedEntity.name + " looks " + math.format(apparentHeight, { precision: 3}) + " tall to " + selectedEntity.name + "\n"; - - if (selectedEntity.currentView.weight && prevSelectedEntity.currentView.weight) { - const ratio = math.divide(selectedEntity.currentView.weight, prevSelectedEntity.currentView.weight) + text += + prevSelectedEntity.name + + " looks " + + math.format(apparentHeight, { precision: 3 }) + + " tall to " + + selectedEntity.name + + "\n"; + + if ( + selectedEntity.currentView.weight && + prevSelectedEntity.currentView.weight + ) { + const ratio = math.divide( + selectedEntity.currentView.weight, + prevSelectedEntity.currentView.weight + ); if (ratio > 1) { - text += selectedEntity.name + " is " + math.format(ratio, {precision: 2}) + " times heavier than " + prevSelectedEntity.name + "\n"; + text += + selectedEntity.name + + " is " + + math.format(ratio, { precision: 2 }) + + " times heavier than " + + prevSelectedEntity.name + + "\n"; } else { - text += selectedEntity.name + " is " + math.format(1/ratio, {precision: 2}) + " times lighter than " + prevSelectedEntity.name + "\n"; + text += + selectedEntity.name + + " is " + + math.format(1 / ratio, { precision: 2 }) + + " times lighter than " + + prevSelectedEntity.name + + "\n"; } } - const capacity = selectedEntity.currentView.preyCapacity ?? selectedEntity.currentView.capacity ?? selectedEntity.currentView.volume + const capacity = + selectedEntity.currentView.preyCapacity ?? + selectedEntity.currentView.capacity ?? + selectedEntity.currentView.volume; if (capacity && prevSelectedEntity.currentView.weight) { - const containCount = math.divide(capacity, math.divide(prevSelectedEntity.currentView.weight, math.unit("80kg/people"))); + const containCount = math.divide( + capacity, + math.divide( + prevSelectedEntity.currentView.weight, + math.unit("80kg/people") + ) + ); if (containCount > 0.1) { - text += selectedEntity.name + " can fit " + math.format(containCount, { precision: 1 }) + " of " + prevSelectedEntity.name + " inside them" + "\n" + text += + selectedEntity.name + + " can fit " + + math.format(containCount, { precision: 1 }) + + " of " + + prevSelectedEntity.name + + " inside them" + + "\n"; } - } - - if (selectedEntity.currentView.energyIntake && prevSelectedEntity.currentView.energyValue) { - const consumeCount = math.divide(selectedEntity.currentView.energyIntake, prevSelectedEntity.currentView.energyValue); + + if ( + selectedEntity.currentView.energyIntake && + prevSelectedEntity.currentView.energyValue + ) { + const consumeCount = math.divide( + selectedEntity.currentView.energyIntake, + prevSelectedEntity.currentView.energyValue + ); if (consumeCount > 0.1) { - text += selectedEntity.name + " needs to eat " + math.format(consumeCount, { precision: 1 }) + " of " + prevSelectedEntity.name + " per day" + "\n" + text += + selectedEntity.name + + " needs to eat " + + math.format(consumeCount, { precision: 1 }) + + " of " + + prevSelectedEntity.name + + " per day" + + "\n"; } - } // todo needs a nice system for formatting this - Object.entries(selectedEntity.currentView.attributes).forEach(([key, attr]) => { - if (key !== "height") { - if (attr.type === "length") { - const ratio = math.divide(selectedEntity.currentView[key], prevSelectedEntity.currentView.height) - - if (ratio > 1) { - text += selectedEntity.name + "'s " + attr.name + " is " + math.format(ratio, {precision: 2}) + " times longer than " + prevSelectedEntity.name + " is tall\n"; - } else { - text += selectedEntity.name + "'s " + attr.name + " is " + math.format(1/ratio, {precision: 2}) + " times shorter than " + prevSelectedEntity.name + " is tall\n"; + Object.entries(selectedEntity.currentView.attributes).forEach( + ([key, attr]) => { + if (key !== "height") { + if (attr.type === "length") { + const ratio = math.divide( + selectedEntity.currentView[key], + prevSelectedEntity.currentView.height + ); + + if (ratio > 1) { + text += + selectedEntity.name + + "'s " + + attr.name + + " is " + + math.format(ratio, { precision: 2 }) + + " times longer than " + + prevSelectedEntity.name + + " is tall\n"; + } else { + text += + selectedEntity.name + + "'s " + + attr.name + + " is " + + math.format(1 / ratio, { precision: 2 }) + + " times shorter than " + + prevSelectedEntity.name + + " is tall\n"; + } } } } - }); - + ); } } if (config.showHorizon) { if (selectedEntity !== null) { - const y = document.querySelector("#entity-" + selectedEntity.index).dataset.y; - const R = math.unit(1.27560e+7, "meters") - const h = math.add(selectedEntity.currentView.height, math.unit(y, "meters")) - - const first = math.multiply(2, math.multiply(R, h)) - const second = math.multiply(h, h) - - const sightline = math.sqrt(math.add(first, second)).to(config.height.units[0].unit.name) - - sightline.fixPrefix = false - text += selectedEntity.name + " could see for " + math.format(sightline, { precision: 3 }) + "\n" + const y = document.querySelector("#entity-" + selectedEntity.index) + .dataset.y; + const R = math.unit(1.2756e7, "meters"); + const h = math.add( + selectedEntity.currentView.height, + math.unit(y, "meters") + ); + + const first = math.multiply(2, math.multiply(R, h)); + const second = math.multiply(h, h); + + const sightline = math + .sqrt(math.add(first, second)) + .to(config.height.units[0].unit.name); + + sightline.fixPrefix = false; + text += + selectedEntity.name + + " could see for " + + math.format(sightline, { precision: 3 }) + + "\n"; } } if (config.showRatios && config.showHorizon) { - if (selectedEntity !== null && prevSelectedEntity !== null && selectedEntity !== prevSelectedEntity) { - const y1 = document.querySelector("#entity-" + selectedEntity.index).dataset.y; - const y2 = document.querySelector("#entity-" + prevSelectedEntity.index).dataset.y; - const R = math.unit(1.27560e+7, "meters") - const R2 = math.subtract(math.subtract(R, prevSelectedEntity.currentView.height), math.unit(y2, "meters")) - const h = math.add(selectedEntity.currentView.height, math.unit(y1, "meters")) - - const first = math.pow(math.add(R, h), 2) - const second = math.pow(R2, 2) - - const sightline = math.sqrt(math.subtract(first, second)).to(config.height.units[0].unit.name) - - sightline.fixPrefix = false - text += selectedEntity.name + " could see " + prevSelectedEntity.name + " from " + math.format(sightline, { precision: 3 }) + " away\n" + if ( + selectedEntity !== null && + prevSelectedEntity !== null && + selectedEntity !== prevSelectedEntity + ) { + const y1 = document.querySelector("#entity-" + selectedEntity.index) + .dataset.y; + const y2 = document.querySelector( + "#entity-" + prevSelectedEntity.index + ).dataset.y; + const R = math.unit(1.2756e7, "meters"); + const R2 = math.subtract( + math.subtract(R, prevSelectedEntity.currentView.height), + math.unit(y2, "meters") + ); + const h = math.add( + selectedEntity.currentView.height, + math.unit(y1, "meters") + ); + + const first = math.pow(math.add(R, h), 2); + const second = math.pow(R2, 2); + + const sightline = math + .sqrt(math.subtract(first, second)) + .to(config.height.units[0].unit.name); + + sightline.fixPrefix = false; + text += + selectedEntity.name + + " could see " + + prevSelectedEntity.name + + " from " + + math.format(sightline, { precision: 3 }) + + " away\n"; } } ratioInfo.innerText = text; - } function pickUnit() { @@ -789,37 +869,37 @@ function pickUnit() { let type = null; let category = null; - + const heightSelect = document.querySelector("#options-height-unit"); currentUnit = heightSelect.value; - Object.keys(unitChoices).forEach(unitType => { - Object.keys(unitChoices[unitType]).forEach(unitCategory => { + Object.keys(unitChoices).forEach((unitType) => { + Object.keys(unitChoices[unitType]).forEach((unitCategory) => { if (unitChoices[unitType][unitCategory].includes(currentUnit)) { type = unitType; category = unitCategory; } - }) - }) + }); + }); // This should only happen if the unit selector isn't set up yet. // It doesn't really matter what goes into it. if (type === null || category === null) { - return "meters" + return "meters"; } - const choices = unitChoices[type][category].map(unit => { + const choices = unitChoices[type][category].map((unit) => { let value = config.height.toNumber(unit); if (value < 1) { value = 1 / value / value; } - return [unit, value] - }) + return [unit, value]; + }); heightSelect.value = choices.sort((a, b) => { - return a[1] - b[1] - })[0][0] + return a[1] - b[1]; + })[0][0]; selectNewUnit(); } @@ -839,14 +919,16 @@ function updateSizes(dirtyOnly = false) { if (e1[1].priority != e2[1].priority) { return e2[1].priority - e1[1].priority; } else { - return e1[1].views[e1[1].view].height.value - e2[1].views[e2[1].view].height.value + return ( + e1[1].views[e1[1].view].height.value - + e2[1].views[e2[1].view].height.value + ); } - }); let zIndex = ordered.length; - ordered.forEach(entity => { + ordered.forEach((entity) => { const element = document.querySelector("#entity-" + entity[0]); element.style.zIndex = zIndex; if (!dirtyOnly || entity[1].dirty) { @@ -856,8 +938,8 @@ function updateSizes(dirtyOnly = false) { zIndex -= 1; }); - document.querySelector("#ground").style.top = pos2pix({x: 0, y: 0}).y + "px"; - + document.querySelector("#ground").style.top = + pos2pix({ x: 0, y: 0 }).y + "px"; drawRulers(); } @@ -875,28 +957,30 @@ function drawRulers() { ctx.canvas.height = Math.floor(canvas.clientHeight * deviceScale); ctx.scale(deviceScale, deviceScale); - - rulers.concat(currentRuler ? [currentRuler] : []).forEach(rulerDef => { + + rulers.concat(currentRuler ? [currentRuler] : []).forEach((rulerDef) => { let x0 = rulerDef.x0; let y0 = rulerDef.y0; let x1 = rulerDef.x1; let y1 = rulerDef.y1; if (rulerDef.entityKey !== null) { - const entity = entities[rulerDef.entityKey] - const entityElement = document.querySelector("#entity-" + rulerDef.entityKey) + const entity = entities[rulerDef.entityKey]; + const entityElement = document.querySelector( + "#entity-" + rulerDef.entityKey + ); x0 *= entity.scale; y0 *= entity.scale; x1 *= entity.scale; y1 *= entity.scale; - x0 += parseFloat(entityElement.dataset.x) - x1 += parseFloat(entityElement.dataset.x) - y0 += parseFloat(entityElement.dataset.y) - y1 += parseFloat(entityElement.dataset.y) + x0 += parseFloat(entityElement.dataset.x); + x1 += parseFloat(entityElement.dataset.x); + y0 += parseFloat(entityElement.dataset.y); + y1 += parseFloat(entityElement.dataset.y); } ctx.save(); ctx.beginPath(); - const start = pos2pix({x: x0, y: y0}); - const end = pos2pix({x: x1, y: y1}); + const start = pos2pix({ x: x0, y: y0 }); + const end = pos2pix({ x: x1, y: y1 }); ctx.moveTo(start.x, start.y); ctx.lineTo(end.x, end.y); ctx.lineWidth = 5; @@ -904,24 +988,32 @@ function drawRulers() { 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.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) { + if (angle < -Math.PI / 2) { angle += Math.PI; } - if (angle > Math.PI/2) { + 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 offsetX = Math.cos(angle + Math.PI / 2); + const offsetY = Math.sin(angle + Math.PI / 2); + const distance = Math.sqrt(Math.pow(y1 - y0, 2) + Math.pow(x1 - 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); + 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(); }); } @@ -941,9 +1033,14 @@ function drawScales(ifDirty = false) { ctx.scale(deviceScale, deviceScale); ctx.beginPath(); - ctx.rect(0, 0, ctx.canvas.width / deviceScale, ctx.canvas.height / deviceScale); + ctx.rect( + 0, + 0, + ctx.canvas.width / deviceScale, + ctx.canvas.height / deviceScale + ); - switch(config.background){ + switch (config.background) { case "black": ctx.fillStyle = "#000"; break; @@ -959,7 +1056,6 @@ function drawScales(ifDirty = false) { } ctx.fill(); - if (config.drawYAxis || config.drawAltitudes !== "none") { drawVerticalScale(ifDirty); } @@ -969,16 +1065,19 @@ function drawScales(ifDirty = false) { } function drawVerticalScale(ifDirty = false) { - if (ifDirty && !worldSizeDirty) - return; - function drawTicks(/** @type {CanvasRenderingContext2D} */ ctx, pixelsPer, heightPer) { + if (ifDirty && !worldSizeDirty) return; + function drawTicks( + /** @type {CanvasRenderingContext2D} */ ctx, + pixelsPer, + heightPer + ) { let total = heightPer.clone(); total.value = config.y; let y = ctx.canvas.clientHeight - 50; let offset = total.toNumber("meters") % heightPer.toNumber("meters"); - y += offset / heightPer.toNumber("meters") * pixelsPer; + y += (offset / heightPer.toNumber("meters")) * pixelsPer; total = math.subtract(total, math.unit(offset, "meters")); for (; y >= 50; y -= pixelsPer) { @@ -987,7 +1086,13 @@ function drawVerticalScale(ifDirty = false) { } } - function drawTick(/** @type {CanvasRenderingContext2D} */ ctx, x, y, label, flipped=false) { + function drawTick( + /** @type {CanvasRenderingContext2D} */ ctx, + x, + y, + label, + flipped = false + ) { const oldStroke = ctx.strokeStyle; const oldFill = ctx.fillStyle; @@ -1008,7 +1113,7 @@ function drawVerticalScale(ifDirty = false) { } else { ctx.strokeStyle = "#aaaaaa"; } - + ctx.stroke(); ctx.beginPath(); @@ -1018,18 +1123,17 @@ function drawVerticalScale(ifDirty = false) { ctx.stroke(); const oldFont = ctx.font; - ctx.font = 'normal 24pt coda'; + ctx.font = "normal 24pt coda"; ctx.fillStyle = "#dddddd"; ctx.beginPath(); if (flipped) { ctx.textAlign = "end"; - ctx.fillText(label, ctx.canvas.clientWidth - 70, y + 35) + ctx.fillText(label, ctx.canvas.clientWidth - 70, y + 35); } else { ctx.fillText(label, x + 20, y + 35); } ctx.textAlign = "start"; - ctx.font = oldFont; ctx.strokeStyle = oldStroke; @@ -1037,8 +1141,12 @@ function drawVerticalScale(ifDirty = false) { } function drawAltitudeLine(ctx, height, label) { - const pixelScale = (ctx.canvas.clientHeight - 100) / config.height.toNumber("meters"); - const y = ctx.canvas.clientHeight - 50 - (height.toNumber("meters") - config.y) * pixelScale; + const pixelScale = + (ctx.canvas.clientHeight - 100) / config.height.toNumber("meters"); + const y = + ctx.canvas.clientHeight - + 50 - + (height.toNumber("meters") - config.y) * pixelScale; const offsetY = y + getVerticalOffset() * pixelScale; if (offsetY < ctx.canvas.clientHeight - 100) { drawTick(ctx, 50, y, label, true); @@ -1051,12 +1159,12 @@ function drawVerticalScale(ifDirty = false) { const ctx = canvas.getContext("2d"); - const pixelScale = (ctx.canvas.clientHeight - 100) / config.height.toNumber(); + const pixelScale = + (ctx.canvas.clientHeight - 100) / config.height.toNumber(); let pixelsPer = pixelScale; heightPer = 1; - if (pixelsPer < config.minLineSize) { const factor = math.ceil(config.minLineSize / pixelsPer); heightPer *= factor; @@ -1070,12 +1178,15 @@ function drawVerticalScale(ifDirty = false) { } if (heightPer == 0) { - console.error("The world size is invalid! Refusing to draw the scale..."); + console.error( + "The world size is invalid! Refusing to draw the scale..." + ); return; } - heightPer = math.unit(heightPer, document.querySelector("#options-height-unit").value); - - + heightPer = math.unit( + heightPer, + document.querySelector("#options-height-unit").value + ); ctx.beginPath(); ctx.moveTo(50, 50); @@ -1098,10 +1209,14 @@ function drawVerticalScale(ifDirty = false) { drawAltitudeLine(ctx, math.unit(675, "km"), "Thermosphere"); drawAltitudeLine(ctx, math.unit(10000, "km"), "Exosphere"); } - + if (config.drawAltitudes == "orbits" || config.drawAltitudes == "all") { drawAltitudeLine(ctx, math.unit(7, "miles"), "Cruising Altitude"); - drawAltitudeLine(ctx, math.unit(100, "km"), "Edge of Space (Kármán line)"); + drawAltitudeLine( + ctx, + math.unit(100, "km"), + "Edge of Space (Kármán line)" + ); drawAltitudeLine(ctx, math.unit(211.3, "miles"), "Space Station"); drawAltitudeLine(ctx, math.unit(369.7, "miles"), "Hubble Telescope"); drawAltitudeLine(ctx, math.unit(1500, "km"), "Low Earth Orbit"); @@ -1121,16 +1236,20 @@ function drawVerticalScale(ifDirty = false) { drawAltitudeLine(ctx, math.unit(123, "AU"), "Heliopause"); drawAltitudeLine(ctx, math.unit(26e3, "lightyears"), "Orbit of Sol"); } - + if (config.drawAltitudes == "weather" || config.drawAltitudes == "all") { drawAltitudeLine(ctx, math.unit(1000, "meters"), "Low-level Clouds"); drawAltitudeLine(ctx, math.unit(3000, "meters"), "Mid-level Clouds"); drawAltitudeLine(ctx, math.unit(10000, "meters"), "High-level Clouds"); - drawAltitudeLine(ctx, math.unit(20, "km"), "Polar Stratospheric Clouds"); + drawAltitudeLine( + ctx, + math.unit(20, "km"), + "Polar Stratospheric Clouds" + ); drawAltitudeLine(ctx, math.unit(80, "km"), "Noctilucent Clouds"); drawAltitudeLine(ctx, math.unit(100, "km"), "Aurora"); } - + if (config.drawAltitudes == "water" || config.drawAltitudes == "all") { drawAltitudeLine(ctx, math.unit(12100, "feet"), "Average Ocean Depth"); drawAltitudeLine(ctx, math.unit(8376, "meters"), "Milkwaukee Deep"); @@ -1141,7 +1260,7 @@ function drawVerticalScale(ifDirty = false) { drawAltitudeLine(ctx, math.unit(7.5, "meters"), "Littoral Zone"); drawAltitudeLine(ctx, math.unit(140, "meters"), "Continental Shelf"); } - + if (config.drawAltitudes == "geology" || config.drawAltitudes == "all") { drawAltitudeLine(ctx, math.unit(35, "km"), "Crust"); drawAltitudeLine(ctx, math.unit(670, "km"), "Upper Mantle"); @@ -1149,8 +1268,11 @@ function drawVerticalScale(ifDirty = false) { drawAltitudeLine(ctx, math.unit(5150, "km"), "Outer Core"); drawAltitudeLine(ctx, math.unit(6370, "km"), "Inner Core"); } - - if (config.drawAltitudes == "thicknesses" || config.drawAltitudes == "all") { + + if ( + config.drawAltitudes == "thicknesses" || + config.drawAltitudes == "all" + ) { drawAltitudeLine(ctx, math.unit(0.335, "nm"), "Monolayer Graphene"); drawAltitudeLine(ctx, math.unit(3, "um"), "Spider Silk"); drawAltitudeLine(ctx, math.unit(0.07, "mm"), "Human Hair"); @@ -1160,7 +1282,7 @@ function drawVerticalScale(ifDirty = false) { drawAltitudeLine(ctx, math.unit(0.1, "um"), "Gold Leaf"); drawAltitudeLine(ctx, math.unit(35, "um"), "PCB Trace"); } - + if (config.drawAltitudes == "airspaces" || config.drawAltitudes == "all") { drawAltitudeLine(ctx, math.unit(18000, "feet"), "Class A"); drawAltitudeLine(ctx, math.unit(14500, "feet"), "Class E"); @@ -1171,23 +1293,26 @@ function drawVerticalScale(ifDirty = false) { if (config.drawAltitudes == "races" || config.drawAltitudes == "all") { drawAltitudeLine(ctx, math.unit(100, "meters"), "100m Dash"); - drawAltitudeLine(ctx, math.unit(26.2188/2, "miles"), "Half Marathon"); + drawAltitudeLine(ctx, math.unit(26.2188 / 2, "miles"), "Half Marathon"); drawAltitudeLine(ctx, math.unit(26.2188, "miles"), "Marathon"); drawAltitudeLine(ctx, math.unit(161.734, "miles"), "Monaco Grand Prix"); drawAltitudeLine(ctx, math.unit(500, "miles"), "Daytona 500"); drawAltitudeLine(ctx, math.unit(2121.6, "miles"), "Tour de France"); } - if (config.drawAltitudes == "olympic-records" || config.drawAltitudes == "all") { + if ( + config.drawAltitudes == "olympic-records" || + config.drawAltitudes == "all" + ) { drawAltitudeLine(ctx, math.unit(2.39, "meters"), "High Jump"); drawAltitudeLine(ctx, math.unit(6.03, "meters"), "Pole Vault"); - drawAltitudeLine(ctx, math.unit(8.90, "meters"), "Long Jump"); + drawAltitudeLine(ctx, math.unit(8.9, "meters"), "Long Jump"); drawAltitudeLine(ctx, math.unit(18.09, "meters"), "Triple Jump"); - drawAltitudeLine(ctx, math.unit(23.30, "meters"), "Shot Put"); - drawAltitudeLine(ctx, math.unit(72.30, "meters"), "Discus Throw"); - drawAltitudeLine(ctx, math.unit(84.80, "meters"), "Hammer Throw"); + drawAltitudeLine(ctx, math.unit(23.3, "meters"), "Shot Put"); + drawAltitudeLine(ctx, math.unit(72.3, "meters"), "Discus Throw"); + drawAltitudeLine(ctx, math.unit(84.8, "meters"), "Hammer Throw"); drawAltitudeLine(ctx, math.unit(90.57, "meters"), "Javelin Throw"); - } + } if (config.drawAltitudes == "d&d-sizes" || config.drawAltitudes == "all") { drawAltitudeLine(ctx, math.unit(0.375, "feet"), "Fine"); @@ -1199,38 +1324,52 @@ function drawVerticalScale(ifDirty = false) { drawAltitudeLine(ctx, math.unit(24, "feet"), "Huge"); drawAltitudeLine(ctx, math.unit(48, "feet"), "Gargantuan"); drawAltitudeLine(ctx, math.unit(96, "feet"), "Colossal"); - } + } } // this is a lot of copypizza... function drawHorizontalScale(ifDirty = false) { - if (ifDirty && !worldSizeDirty) - return; - function drawTicks(/** @type {CanvasRenderingContext2D} */ ctx, pixelsPer, heightPer) { + if (ifDirty && !worldSizeDirty) return; + function drawTicks( + /** @type {CanvasRenderingContext2D} */ ctx, + pixelsPer, + heightPer + ) { let total = heightPer.clone(); total.value = math.unit(-config.x, "meters").toNumber(config.unit); // further adjust it to put the current position in the center - total.value -= heightPer.toNumber("meters") / pixelsPer * (canvasWidth + 50) / 2; + total.value -= + ((heightPer.toNumber("meters") / pixelsPer) * (canvasWidth + 50)) / + 2; let x = ctx.canvas.clientWidth - 50; - let offset = total.toNumber("meters") % heightPer.toNumber("meters"); - x += offset / heightPer.toNumber("meters") * pixelsPer; + x += (offset / heightPer.toNumber("meters")) * pixelsPer; total = math.subtract(total, math.unit(offset, "meters")); for (; x >= 50 - pixelsPer; x -= pixelsPer) { // negate it so that the left side is negative - drawTick(ctx, x, 50, math.multiply(-1, total).format({ precision: 3 })); + drawTick( + ctx, + x, + 50, + math.multiply(-1, total).format({ precision: 3 }) + ); total = math.add(total, heightPer); } } - function drawTick(/** @type {CanvasRenderingContext2D} */ ctx, x, y, label) { - ctx.save() + function drawTick( + /** @type {CanvasRenderingContext2D} */ ctx, + x, + y, + label + ) { + ctx.save(); x = Math.round(x); y = Math.round(y); @@ -1254,13 +1393,13 @@ function drawHorizontalScale(ifDirty = false) { ctx.stroke(); const oldFont = ctx.font; - ctx.font = 'normal 24pt coda'; + ctx.font = "normal 24pt coda"; ctx.fillStyle = "#dddddd"; ctx.beginPath(); ctx.fillText(label, x + 35, y + 20); - ctx.restore() + ctx.restore(); } const canvas = document.querySelector("#display"); @@ -1273,31 +1412,35 @@ function drawHorizontalScale(ifDirty = false) { heightPer = 1; if (pixelsPer < config.minLineSize * 2) { - const factor = math.ceil(config.minLineSize * 2/ pixelsPer); + const factor = math.ceil((config.minLineSize * 2) / pixelsPer); heightPer *= factor; pixelsPer *= factor; } if (pixelsPer > config.maxLineSize * 2) { - const factor = math.ceil(pixelsPer / 2/ config.maxLineSize); + const factor = math.ceil(pixelsPer / 2 / config.maxLineSize); heightPer /= factor; pixelsPer /= factor; } if (heightPer == 0) { - console.error("The world size is invalid! Refusing to draw the scale..."); + console.error( + "The world size is invalid! Refusing to draw the scale..." + ); return; } - heightPer = math.unit(heightPer, document.querySelector("#options-height-unit").value); - + heightPer = math.unit( + heightPer, + document.querySelector("#options-height-unit").value + ); ctx.beginPath(); ctx.moveTo(0, 50); ctx.lineTo(ctx.canvas.clientWidth, 50); ctx.stroke(); ctx.beginPath(); - ctx.moveTo(0, ctx.canvas.clientHeight - 50); - ctx.lineTo(ctx.canvas.clientWidth , ctx.canvas.clientHeight - 50); + ctx.moveTo(0, ctx.canvas.clientHeight - 50); + ctx.lineTo(ctx.canvas.clientWidth, ctx.canvas.clientHeight - 50); ctx.stroke(); drawTicks(ctx, pixelsPer, heightPer); @@ -1305,7 +1448,7 @@ function drawHorizontalScale(ifDirty = false) { // Entities are generated as needed, and we make a copy // every time - the resulting objects get mutated, after all. -// But we also want to be able to read some information without +// But we also want to be able to read some information without // calling the constructor -- e.g. making a list of authors and // owners. So, this function is used to generate that information. // It is invoked like makeEntity so that it can be dropped in easily, @@ -1323,10 +1466,10 @@ function createEntityMaker(info, views, sizes, forms) { maker.owners = []; maker.nsfw = false; - Object.values(views).forEach(view => { + Object.values(views).forEach((view) => { const authors = authorsOf(view.image.source); if (authors) { - authors.forEach(author => { + authors.forEach((author) => { if (maker.authors.indexOf(author) == -1) { maker.authors.push(author); } @@ -1334,7 +1477,7 @@ function createEntityMaker(info, views, sizes, forms) { } const owners = ownersOf(view.image.source); if (owners) { - owners.forEach(owner => { + owners.forEach((owner) => { if (maker.owners.indexOf(owner) == -1) { maker.owners.push(owner); } @@ -1350,7 +1493,7 @@ function createEntityMaker(info, views, sizes, forms) { } // This function serializes and parses its arguments to avoid sharing -// references to a common object. This allows for the objects to be +// references to a common object. This allows for the objects to be // safely mutated. function makeEntity(info, views, sizes, forms = {}) { @@ -1361,7 +1504,10 @@ function makeEntity(info, views, sizes, forms = {}) { rotation: 0, info: JSON.parse(JSON.stringify(info)), views: JSON.parse(JSON.stringify(views), math.reviver), - sizes: sizes === undefined ? [] : JSON.parse(JSON.stringify(sizes), math.reviver), + sizes: + sizes === undefined + ? [] + : JSON.parse(JSON.stringify(sizes), math.reviver), forms: forms, init: function () { const entity = this; @@ -1381,8 +1527,7 @@ function makeEntity(info, views, sizes, forms = {}) { } if (view.default) { - if (forms === {} || this.defaultForm === view.form) - { + if (forms === {} || this.defaultForm === view.form) { this.defaultView = viewKey; this.view = viewKey; this.form = view.form; @@ -1393,73 +1538,115 @@ function makeEntity(info, views, sizes, forms = {}) { view.units = {}; - if (config.autoMass !== "off" && view.attributes.weight === undefined) { + if ( + config.autoMass !== "off" && + view.attributes.weight === undefined + ) { let base = undefined; - switch(config.autoMass) { + switch (config.autoMass) { case "human": - baseMass = math.unit(150, "lbs") - baseHeight = math.unit(5.917, "feet") - break + baseMass = math.unit(150, "lbs"); + baseHeight = math.unit(5.917, "feet"); + break; case "quadruped at shoulder": - baseMass = math.unit(80, "lbs") - baseHeight = math.unit(30, "inches") - break + baseMass = math.unit(80, "lbs"); + baseHeight = math.unit(30, "inches"); + break; } - const ratio = math.divide(view.attributes.height.base, baseHeight) + const ratio = math.divide( + view.attributes.height.base, + baseHeight + ); view.attributes.weight = { name: "Mass", power: 3, type: "mass", - base: math.multiply(baseMass, Math.pow(ratio, 3)) - } + base: math.multiply(baseMass, Math.pow(ratio, 3)), + }; } - if (config.autoFoodIntake && view.attributes.weight !== undefined && view.attributes.energyIntake === undefined) { + if ( + config.autoFoodIntake && + view.attributes.weight !== undefined && + view.attributes.energyIntake === undefined + ) { view.attributes.energyIntake = { name: "Food Intake", - power: 3 * 3/4, + power: (3 * 3) / 4, type: "energy", - base: math.unit(2000 * Math.pow(view.attributes.weight.base.toNumber("lbs") / 150, 3/4), "kcal") - } + base: math.unit( + 2000 * + Math.pow( + view.attributes.weight.base.toNumber( + "lbs" + ) / 150, + 3 / 4 + ), + "kcal" + ), + }; } - if (config.autoCaloricValue && view.attributes.weight !== undefined && view.attributes.energyWorth === undefined) { + if ( + config.autoCaloricValue && + view.attributes.weight !== undefined && + view.attributes.energyWorth === undefined + ) { view.attributes.energyValue = { name: "Caloric Value", power: 3, type: "energy", - base: math.unit(860 * view.attributes.weight.base.toNumber("lbs"), "kcal") - } + base: math.unit( + 860 * view.attributes.weight.base.toNumber("lbs"), + "kcal" + ), + }; } - if (config.autoPreyCapacity !== "off" && view.attributes.weight !== undefined && view.attributes.preyCapacity === undefined) { + if ( + config.autoPreyCapacity !== "off" && + view.attributes.weight !== undefined && + view.attributes.preyCapacity === undefined + ) { view.attributes.preyCapacity = { name: "Prey Capacity", power: 3, type: "volume", - base: math.unit((config.autoPreyCapacity == "same-size" ? 1 : 0.05) * view.attributes.weight.base.toNumber("lbs") / 150, "people") - } + base: math.unit( + ((config.autoPreyCapacity == "same-size" + ? 1 + : 0.05) * + view.attributes.weight.base.toNumber("lbs")) / + 150, + "people" + ), + }; } Object.entries(view.attributes).forEach(([key, val]) => { - Object.defineProperty( - view, - key, - { - get: function () { - return math.multiply(Math.pow(this.parent.scale, this.attributes[key].power), this.attributes[key].base); - }, - set: function (value) { - const newScale = Math.pow(math.divide(value, this.attributes[key].base), 1 / this.attributes[key].power); - this.parent.scale = newScale; - } - } - ); + Object.defineProperty(view, key, { + get: function () { + return math.multiply( + Math.pow( + this.parent.scale, + this.attributes[key].power + ), + this.attributes[key].base + ); + }, + set: function (value) { + const newScale = Math.pow( + math.divide(value, this.attributes[key].base), + 1 / this.attributes[key].power + ); + this.parent.scale = newScale; + }, + }); }); }); - this.sizes.forEach(size => { + this.sizes.forEach((size) => { if (size.default === true) { if (Object.keys(forms).length > 0) { if (this.defaultForm !== size.form) { @@ -1479,8 +1666,8 @@ function makeEntity(info, views, sizes, forms = {}) { this.sizes = [ { name: "Normal", - height: this.views[this.defaultView].height - } + height: this.views[this.defaultView].height, + }, ]; this.size = this.sizes[0]; } @@ -1488,43 +1675,38 @@ function makeEntity(info, views, sizes, forms = {}) { this.desc = {}; Object.entries(this.info).forEach(([key, value]) => { - Object.defineProperty( - this.desc, - key, - { - get: function () { - let text = value.text; - - if (entity.views[entity.view].info) { - if (entity.views[entity.view].info[key]) { - text = combineInfo(text, entity.views[entity.view].info[key]); - } + Object.defineProperty(this.desc, key, { + get: function () { + let text = value.text; + + if (entity.views[entity.view].info) { + if (entity.views[entity.view].info[key]) { + text = combineInfo( + text, + entity.views[entity.view].info[key] + ); } + } - if (entity.size.info) { - if (entity.size.info[key]) { - text = combineInfo(text, entity.size.info[key]); - } + if (entity.size.info) { + if (entity.size.info[key]) { + text = combineInfo(text, entity.size.info[key]); } - - return { title: value.title, text: text }; } - } - ) + + return { title: value.title, text: text }; + }, + }); }); - Object.defineProperty( - this, - "currentView", - { - get: function() { - return entity.views[entity.view]; - } - } - ) + Object.defineProperty(this, "currentView", { + get: function () { + return entity.views[entity.view]; + }, + }); this.formViews = {}; - this.formSizes = {} + this.formSizes = {}; Object.entries(views).forEach(([key, value]) => { if (value.default) { @@ -1538,28 +1720,32 @@ function makeEntity(info, views, sizes, forms = {}) { } }); - this.sizes.forEach(size => { + this.sizes.forEach((size) => { if (size.default) { this.formSizes[size.form] = size; } - }) + }); - this.sizes.forEach(size => { + this.sizes.forEach((size) => { if (this.formSizes[size.form] === undefined) { this.formSizes[size.form] = size; } }); - Object.values(views).forEach(view => { + Object.values(views).forEach((view) => { if (this.formSizes[view.form] === undefined) { - this.formSizes[view.form] = { name: "Normal", height: view.attributes.height.base, default: true, form: view.form }; + this.formSizes[view.form] = { + name: "Normal", + height: view.attributes.height.base, + default: true, + form: view.form, + }; } }); - delete this.init; return this; - } + }, }.init(); return entityTemplate; @@ -1593,8 +1779,9 @@ function clickDown(target, x, y) { movingInBounds = true; } - - clickTimeout = setTimeout(() => { dragging = true }, 200) + clickTimeout = setTimeout(() => { + dragging = true; + }, 200); target.classList.add("no-transition"); } @@ -1605,7 +1792,6 @@ function hoveringInDeleteArea(e) { } function clickUp(e) { - if (e.which != 1) { return; } @@ -1619,14 +1805,15 @@ function clickUp(e) { if (hoveringInDeleteArea(e)) { removeEntity(clicked); - document.querySelector("#menubar").classList.remove("hover-delete"); + document + .querySelector("#menubar") + .classList.remove("hover-delete"); } } else { select(clicked); } clicked = null; } - } function deselect(e) { @@ -1644,7 +1831,8 @@ function deselect(e) { prevSelected.classList.remove("prevSelected"); } - document.getElementById("options-selected-entity-none").selected = "selected"; + document.getElementById("options-selected-entity-none").selected = + "selected"; document.getElementById("delete-entity").style.display = "none"; clearAttribution(); @@ -1661,12 +1849,10 @@ function deselect(e) { } function select(target) { - if (prevSelected !== null) { prevSelected.classList.remove("prevSelected"); } - prevSelected = selected; prevSelectedEntity = selectedEntity; deselect(); @@ -1675,10 +1861,16 @@ function select(target) { updateInfo(); - document.getElementById("options-selected-entity-" + target.dataset.key).selected = "selected"; + document.getElementById( + "options-selected-entity-" + target.dataset.key + ).selected = "selected"; document.getElementById("delete-entity").style.display = ""; - if (prevSelected !== null && config.showRatios && selected !== prevSelected) { + if ( + prevSelected !== null && + config.showRatios && + selected !== prevSelected + ) { prevSelected.classList.add("prevSelected"); } selected.classList.add("selected"); @@ -1703,7 +1895,6 @@ function configFormList(entity, selectedForm) { list.innerHTML = ""; - if (selectedForm === undefined) { label.style.display = "none"; list.style.display = "none"; @@ -1713,7 +1904,7 @@ function configFormList(entity, selectedForm) { label.style.display = "block"; list.style.display = "block"; - Object.keys(entity.forms).forEach(form => { + Object.keys(entity.forms).forEach((form) => { const option = document.createElement("option"); option.innerText = entity.forms[form].name; option.value = form; @@ -1721,7 +1912,7 @@ function configFormList(entity, selectedForm) { if (form === selectedForm) { option.selected = true; } - + list.appendChild(option); }); } @@ -1733,7 +1924,7 @@ function configViewList(entity, selectedView) { list.style.display = "block"; - Object.keys(entity.views).forEach(view => { + Object.keys(entity.views).forEach((view) => { if (Object.keys(entity.forms).length > 0) { if (entity.views[view].form !== entity.form) { return; @@ -1744,7 +1935,7 @@ function configViewList(entity, selectedView) { option.value = view; if (isNsfw(entity.views[view].image.source)) { - option.classList.add("nsfw") + option.classList.add("nsfw"); } if (view === selectedView) { @@ -1756,7 +1947,7 @@ function configViewList(entity, selectedView) { list.classList.remove("nsfw"); } } - + list.appendChild(option); }); } @@ -1796,19 +1987,19 @@ function configEntityOptions(entity, view) { scaleInput.classList.add("options-field-numeric"); scaleInput.id = "options-entity-scale"; - scaleInput.addEventListener("change", e => { + scaleInput.addEventListener("change", (e) => { try { - const newScale = e.target.value == 0 ? 1 : math.evaluate(e.target.value); - if (typeof(newScale) !== "number") { - toast("Invalid input: scale can't have any units!") + const newScale = + e.target.value == 0 ? 1 : math.evaluate(e.target.value); + if (typeof newScale !== "number") { + toast("Invalid input: scale can't have any units!"); return; } entity.scale = newScale; } catch { - toast("Invalid input: could not parse " + e.target.value) + toast("Invalid input: could not parse " + e.target.value); } - entity.dirty = true; if (config.autoFit) { fitWorld(); @@ -1819,9 +2010,9 @@ function configEntityOptions(entity, view) { updateViewOptions(entity, entity.view); }); - scaleInput.addEventListener("keydown", e => { + scaleInput.addEventListener("keydown", (e) => { e.stopPropagation(); - }) + }); setNumericInput(scaleInput, entity.scale); @@ -1840,15 +2031,15 @@ function configEntityOptions(entity, view) { nameInput.classList.add("options-field-text"); nameInput.value = entity.name; - nameInput.addEventListener("input", e => { + nameInput.addEventListener("input", (e) => { entity.name = e.target.value; entity.dirty = true; updateSizes(true); - }) + }); - nameInput.addEventListener("keydown", e => { + nameInput.addEventListener("keydown", (e) => { e.stopPropagation(); - }) + }); nameRow.appendChild(nameInput); @@ -1857,8 +2048,10 @@ function configEntityOptions(entity, view) { configSizeList(entity); - document.querySelector("#options-order-display").innerText = entity.priority; - document.querySelector("#options-brightness-display").innerText = entity.brightness; + document.querySelector("#options-order-display").innerText = + entity.priority; + document.querySelector("#options-brightness-display").innerText = + entity.brightness; document.querySelector("#options-ordering").style.display = "flex"; } @@ -1867,7 +2060,7 @@ function configSizeList(entity) { defaultHolder.innerHTML = ""; - entity.sizes.forEach(defaultInfo => { + entity.sizes.forEach((defaultInfo) => { if (Object.keys(entity.forms).length > 0) { if (defaultInfo.form !== entity.form) { return; @@ -1878,9 +2071,10 @@ function configSizeList(entity) { button.innerText = defaultInfo.name; - button.addEventListener("click", e => { + button.addEventListener("click", (e) => { if (Object.keys(entity.forms).length > 0) { - entity.views[entity.formViews[entity.form]].height = defaultInfo.height; + entity.views[entity.formViews[entity.form]].height = + defaultInfo.height; } else { entity.views[entity.defaultView].height = defaultInfo.height; } @@ -1896,7 +2090,6 @@ function configSizeList(entity) { targets[selected.dataset.key] = entities[selected.dataset.key]; fitEntities(targets); } - }); defaultHolder.appendChild(button); @@ -1907,8 +2100,10 @@ function updateEntityOptions(entity, view) { const scaleInput = document.querySelector("#options-entity-scale"); setNumericInput(scaleInput, entity.scale); - document.querySelector("#options-order-display").innerText = entity.priority; - document.querySelector("#options-brightness-display").innerText = entity.brightness; + document.querySelector("#options-order-display").innerText = + entity.priority; + document.querySelector("#options-brightness-display").innerText = + entity.brightness; } function clearEntityOptions() { @@ -1950,44 +2145,48 @@ function configViewOptions(entity, view) { const select = document.createElement("select"); select.classList.add("options-field-unit"); - select.id = "options-view-" + key + "-select" + select.id = "options-view-" + key + "-select"; Object.entries(unitChoices[val.type]).forEach(([group, entries]) => { const optGroup = document.createElement("optgroup"); optGroup.label = group; select.appendChild(optGroup); - entries.forEach(entry => { + entries.forEach((entry) => { const option = document.createElement("option"); option.innerText = entry; if (entry == defaultUnits[val.type][config.units]) { option.selected = true; } select.appendChild(option); - }) + }); }); - - input.addEventListener("change", e => { + input.addEventListener("change", (e) => { const raw_value = input.value == 0 ? 1 : input.value; - let value + let value; try { value = math.evaluate(raw_value).toNumber(select.value); } catch { try { - value = math.evaluate(input.value) - if (typeof(value) !== "number") { - toast("Invalid input: " + value.format() + " can't convert to " + select.value) - value = undefined + value = math.evaluate(input.value); + if (typeof value !== "number") { + toast( + "Invalid input: " + + value.format() + + " can't convert to " + + select.value + ); + value = undefined; } } catch { - toast("Invalid input: could not parse: " + input.value) - value = undefined + toast("Invalid input: could not parse: " + input.value); + value = undefined; } } if (value === undefined) { return; } - input.value = value + input.value = value; entity.views[view][key] = math.unit(value, select.value); entity.dirty = true; if (config.autoFit) { @@ -1999,9 +2198,9 @@ function configViewOptions(entity, view) { updateViewOptions(entity, view, key); }); - input.addEventListener("keydown", e => { + input.addEventListener("keydown", (e) => { e.stopPropagation(); - }) + }); if (entity.currentView.units[key]) { select.value = entity.currentView.units[key]; @@ -2014,12 +2213,17 @@ function configViewOptions(entity, view) { setNumericInput(input, entity.views[view][key].toNumber(select.value)); // TODO does this ever cause a change in the world? - select.addEventListener("input", e => { + select.addEventListener("input", (e) => { const value = input.value == 0 ? 1 : input.value; const oldUnit = select.dataset.oldUnit; - entity.views[entity.view][key] = math.unit(value, oldUnit).to(select.value); + entity.views[entity.view][key] = math + .unit(value, oldUnit) + .to(select.value); entity.dirty = true; - setNumericInput(input, entity.views[entity.view][key].toNumber(select.value)); + setNumericInput( + input, + entity.views[entity.view][key].toNumber(select.value) + ); select.dataset.oldUnit = select.value; entity.views[view].units[key] = select.value; @@ -2037,26 +2241,29 @@ function configViewOptions(entity, view) { row.appendChild(input); row.appendChild(select); }); - } function updateViewOptions(entity, view, changed) { Object.entries(entity.views[view].attributes).forEach(([key, val]) => { if (key != changed) { - const input = document.querySelector("#options-view-" + key + "-input"); - const select = document.querySelector("#options-view-" + key + "-select"); + const input = document.querySelector( + "#options-view-" + key + "-input" + ); + const select = document.querySelector( + "#options-view-" + key + "-select" + ); const currentUnit = select.value; - const convertedAmount = entity.views[view][key].toNumber(currentUnit); + const convertedAmount = + entity.views[view][key].toNumber(currentUnit); setNumericInput(input, convertedAmount); } - }); } function setNumericInput(input, value, round = 6) { if (typeof value == "string") { - value = parseFloat(value) + value = parseFloat(value); } input.value = value.toPrecision(round); } @@ -2088,7 +2295,7 @@ testCanvas.id = "test-canvas"; function rotate(point, angle) { return [ point[0] * Math.cos(angle) - point[1] * Math.sin(angle), - point[0] * Math.sin(angle) + point[1] * Math.cos(angle) + point[0] * Math.sin(angle) + point[1] * Math.cos(angle), ]; } @@ -2096,8 +2303,7 @@ const testCtx = testCanvas.getContext("2d"); function testClick(event) { const target = event.target; - if (webkitCanvasBug) - { + if (webkitCanvasBug) { return clickDown(target.parentElement, event.clientX, event.clientY); } testCtx.save(); @@ -2110,7 +2316,8 @@ function testClick(event) { let w = target.width; let h = target.height; - let ratioW = 1, ratioH = 1; + let ratioW = 1, + ratioH = 1; // Limit the size of the canvas so that very large images don't cause problems) if (w > 1000) { @@ -2135,9 +2342,12 @@ function testClick(event) { y = event.clientY - target.getBoundingClientRect().y, alpha; - [xTarget,yTarget] = [x,y]; + [xTarget, yTarget] = [x, y]; - [actualW, actualH] = [target.getBoundingClientRect().width, target.getBoundingClientRect().height]; + [actualW, actualH] = [ + target.getBoundingClientRect().width, + target.getBoundingClientRect().height, + ]; xTarget /= ratio; yTarget /= ratio; @@ -2154,14 +2364,12 @@ function testClick(event) { testCtx.resetTransform(); - - - testCtx.translate(actualW/2, actualH/2); + testCtx.translate(actualW / 2, actualH / 2); testCtx.rotate(angle); - testCtx.translate(-actualW/2, -actualH/2); - testCtx.drawImage(target, (actualW/2 - w/2), (actualH/2 - h/2), w, h); + testCtx.translate(-actualW / 2, -actualH / 2); + testCtx.drawImage(target, actualW / 2 - w / 2, actualH / 2 - h / 2, w, h); testCtx.fillStyle = "red"; - testCtx.fillRect(actualW/2,actualH/2,10,10); + testCtx.fillRect(actualW / 2, actualH / 2, 10, 10); testCtx.restore(); @@ -2174,11 +2382,16 @@ function testClick(event) { if (alpha === 0) { const oldDisplay = target.style.display; target.style.display = "none"; - const newTarget = document.elementFromPoint(event.clientX, event.clientY); - newTarget.dispatchEvent(new MouseEvent(event.type, { - "clientX": event.clientX, - "clientY": event.clientY - })); + const newTarget = document.elementFromPoint( + event.clientX, + event.clientY + ); + newTarget.dispatchEvent( + new MouseEvent(event.type, { + clientX: event.clientX, + clientY: event.clientY, + }) + ); target.style.display = oldDisplay; } else { clickDown(target.parentElement, event.clientX, event.clientY); @@ -2187,11 +2400,15 @@ function testClick(event) { } function arrangeEntities(order) { - const worldWidth = config.height.toNumber("meters") / canvasHeight * canvasWidth; + const worldWidth = + (config.height.toNumber("meters") / canvasHeight) * canvasWidth; let sum = 0; - order.forEach(key => { - const image = document.querySelector("#entity-" + key + " > .entity-image"); - const meters = entities[key].views[entities[key].view].height.toNumber("meters"); + order.forEach((key) => { + const image = document.querySelector( + "#entity-" + key + " > .entity-image" + ); + const meters = + entities[key].views[entities[key].view].height.toNumber("meters"); let height = image.height; let width = image.width; @@ -2203,15 +2420,17 @@ function arrangeEntities(order) { width = height; } - sum += meters * width / height; + sum += (meters * width) / height; }); let x = config.x - sum / 2; - order.forEach(key => { - - const image = document.querySelector("#entity-" + key + " > .entity-image"); - const meters = entities[key].views[entities[key].view].height.toNumber("meters"); + order.forEach((key) => { + const image = document.querySelector( + "#entity-" + key + " > .entity-image" + ); + const meters = + entities[key].views[entities[key].view].height.toNumber("meters"); let height = image.height; let width = image.width; @@ -2223,30 +2442,31 @@ function arrangeEntities(order) { width = height; } - x += meters * width / height / 2; + x += (meters * width) / height / 2; document.querySelector("#entity-" + key).dataset.x = x; document.querySelector("#entity-" + key).dataset.y = config.y; - x += meters * width / height / 2; - - }) + x += (meters * width) / height / 2; + }); fitWorld(); updateSizes(); } function removeAllEntities() { - Object.keys(entities).forEach(key => { + Object.keys(entities).forEach((key) => { removeEntity(document.querySelector("#entity-" + key)); }); } function clearAttribution() { - document.querySelector("#attribution-category-header").style.display = "none"; + document.querySelector("#attribution-category-header").style.display = + "none"; document.querySelector("#options-attribution").style.display = "none"; } function displayAttribution(file) { - document.querySelector("#attribution-category-header").style.display = "block"; + document.querySelector("#attribution-category-header").style.display = + "block"; document.querySelector("#options-attribution").style.display = "inline"; const authors = authorsOfFull(file); const owners = ownersOfFull(file); @@ -2255,7 +2475,9 @@ function displayAttribution(file) { const authorHolder = document.querySelector("#options-attribution-authors"); const ownerHolder = document.querySelector("#options-attribution-owners"); - const citationHolder = document.querySelector("#options-attribution-citations"); + const citationHolder = document.querySelector( + "#options-attribution-citations" + ); const sourceHolder = document.querySelector("#options-attribution-source"); if (authors === []) { @@ -2273,7 +2495,7 @@ function displayAttribution(file) { const list = document.createElement("ul"); authorHolder.appendChild(list); - authors.forEach(author => { + authors.forEach((author) => { const authorEntry = document.createElement("li"); if (author.url) { const link = document.createElement("a"); @@ -2289,7 +2511,6 @@ function displayAttribution(file) { } list.appendChild(authorEntry); }); - } if (owners === []) { @@ -2307,7 +2528,7 @@ function displayAttribution(file) { const list = document.createElement("ul"); ownerHolder.appendChild(list); - owners.forEach(owner => { + owners.forEach((owner) => { const ownerEntry = document.createElement("li"); if (owner.url) { const link = document.createElement("a"); @@ -2323,19 +2544,17 @@ function displayAttribution(file) { } list.appendChild(ownerEntry); }); - } citationHolder.innerHTML = ""; - - if (citations === [] || citations === undefined) { + if (citations === [] || citations === undefined) { } else { citationHolder.innerHTML = ""; const list = document.createElement("ul"); citationHolder.appendChild(list); - citations.forEach(citation => { + citations.forEach((citation) => { const citationEntry = document.createElement("li"); const link = document.createElement("a"); link.style.display = "block"; @@ -2345,7 +2564,7 @@ function displayAttribution(file) { link.target = "_blank"; citationEntry.appendChild(link); list.appendChild(citationEntry); - }) + }); } if (source === null) { @@ -2378,11 +2597,15 @@ function removeEntity(element) { clicked = null; } - const option = document.querySelector("#options-selected-entity-" + element.dataset.key); + const option = document.querySelector( + "#options-selected-entity-" + element.dataset.key + ); option.parentElement.removeChild(option); delete entities[element.dataset.key]; - const bottomName = document.querySelector("#bottom-name-" + element.dataset.key); + const bottomName = document.querySelector( + "#bottom-name-" + element.dataset.key + ); const topName = document.querySelector("#top-name-" + element.dataset.key); bottomName.parentElement.removeChild(bottomName); topName.parentElement.removeChild(topName); @@ -2394,7 +2617,7 @@ function removeEntity(element) { } function checkEntity(entity) { - Object.values(entity.views).forEach(view => { + Object.values(entity.views).forEach((view) => { if (authorsOf(view.image.source) === undefined) { console.warn("No authors: " + view.image.source); } @@ -2402,9 +2625,9 @@ function checkEntity(entity) { } function preloadViews(entity) { - Object.values(entity.views).forEach(view => { + Object.values(entity.views).forEach((view) => { if (Object.keys(entity.forms).length > 0) { - if (entity.form !== view.form){ + if (entity.form !== view.form) { return; } } @@ -2416,7 +2639,14 @@ function preloadViews(entity) { }); } -function displayEntity(entity, view, x, y, selectEntity = false, refresh = false) { +function displayEntity( + entity, + view, + x, + y, + selectEntity = false, + refresh = false +) { checkEntity(entity); // preload all of the entity's views @@ -2428,7 +2658,7 @@ function displayEntity(entity, view, x, y, selectEntity = false, refresh = false const img = document.createElement("img"); img.classList.add("entity-image"); - img.addEventListener("dragstart", e => { + img.addEventListener("dragstart", (e) => { e.preventDefault(); }); @@ -2442,26 +2672,38 @@ function displayEntity(entity, view, x, y, selectEntity = false, refresh = false img.src = image.source; if (image.bottom !== undefined) { - img.style.setProperty("--offset", ((-1 + image.bottom) * 100) + "%") + img.style.setProperty("--offset", (-1 + image.bottom) * 100 + "%"); } else { - img.style.setProperty("--offset", ((-1) * 100) + "%") + img.style.setProperty("--offset", -1 * 100 + "%"); } - img.style.setProperty("--rotation", (entity.rotation * 180 / Math.PI) + "deg") + img.style.setProperty( + "--rotation", + (entity.rotation * 180) / Math.PI + "deg" + ); box.dataset.x = x; box.dataset.y = y; - img.addEventListener("mousedown", e => { if (e.which == 1) { testClick(e); if (clicked) { e.stopPropagation() } } }); - img.addEventListener("touchstart", e => { + img.addEventListener("mousedown", (e) => { + if (e.which == 1) { + testClick(e); + if (clicked) { + e.stopPropagation(); + } + } + }); + img.addEventListener("touchstart", (e) => { const fakeEvent = { target: e.target, clientX: e.touches[0].clientX, clientY: e.touches[0].clientY, - which: 1 + which: 1, }; testClick(fakeEvent); - if (clicked) { e.stopPropagation() } + if (clicked) { + e.stopPropagation(); + } }); const heightBar = document.createElement("div"); @@ -2473,10 +2715,8 @@ function displayEntity(entity, view, x, y, selectEntity = false, refresh = false box.dataset.key = entityIndex; entity.view = view; - if (entity.priority === undefined) - entity.priority = 0; - if (entity.brightness === undefined) - entity.brightness = 1; + if (entity.priority === undefined) entity.priority = 0; + if (entity.brightness === undefined) entity.brightness = 1; entities[entityIndex] = entity; entity.index = entityIndex; @@ -2507,14 +2747,15 @@ function displayEntity(entity, view, x, y, selectEntity = false, refresh = false entityOption.value = entityIndex; entityOption.innerText = entity.name; - document.getElementById("options-selected-entity").appendChild(entityOption); + document + .getElementById("options-selected-entity") + .appendChild(entityOption); entityIndex += 1; if (config.autoFit) { fitWorld(); } - if (selectEntity) - select(box); + if (selectEntity) select(box); entity.dirty = true; @@ -2523,19 +2764,17 @@ function displayEntity(entity, view, x, y, selectEntity = false, refresh = false targets[entityIndex - 1] = entity; fitEntities(targets); } - if (refresh) - updateSizes(true); + if (refresh) updateSizes(true); } - window.onblur = function () { altHeld = false; shiftHeld = false; -} +}; window.onfocus = function () { window.dispatchEvent(new Event("keydown")); -} +}; // thanks to https://developers.google.com/web/fundamentals/native-hardware/fullscreen @@ -2543,13 +2782,25 @@ function toggleFullScreen() { var doc = window.document; var docEl = doc.documentElement; - var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen; - var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen; - - if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) { + var requestFullScreen = + docEl.requestFullscreen || + docEl.mozRequestFullScreen || + docEl.webkitRequestFullScreen || + docEl.msRequestFullscreen; + var cancelFullScreen = + doc.exitFullscreen || + doc.mozCancelFullScreen || + doc.webkitExitFullscreen || + doc.msExitFullscreen; + + if ( + !doc.fullscreenElement && + !doc.mozFullScreenElement && + !doc.webkitFullscreenElement && + !doc.msFullscreenElement + ) { requestFullScreen.call(docEl); - } - else { + } else { cancelFullScreen.call(doc); } } @@ -2573,79 +2824,79 @@ function prepareSidebar() { name: "Show/hide sidebar", id: "menu-toggle-sidebar", icon: "fas fa-chevron-circle-down", - rotates: true + rotates: true, }, { name: "Fullscreen", id: "menu-fullscreen", - icon: "fas fa-compress" + icon: "fas fa-compress", }, { name: "Clear", id: "menu-clear", - icon: "fas fa-file" + icon: "fas fa-file", }, { name: "Sort by height", id: "menu-order-height", - icon: "fas fa-sort-numeric-up" + icon: "fas fa-sort-numeric-up", }, { name: "Permalink", id: "menu-permalink", - icon: "fas fa-link" + icon: "fas fa-link", }, { name: "Export to clipboard", id: "menu-export", - icon: "fas fa-share" + icon: "fas fa-share", }, { name: "Import from clipboard", id: "menu-import", icon: "fas fa-share", - classes: ["flipped"] + classes: ["flipped"], }, { name: "Save Scene", id: "menu-save", icon: "fas fa-download", - input: true + input: true, }, { name: "Load Scene", id: "menu-load", icon: "fas fa-upload", - select: true + select: true, }, { name: "Delete Scene", id: "menu-delete", icon: "fas fa-trash", - select: true + select: true, }, { name: "Load Autosave", id: "menu-load-autosave", - icon: "fas fa-redo" + icon: "fas fa-redo", }, { name: "Load Preset", id: "menu-preset", icon: "fas fa-play", - select: true + select: true, }, { name: "Add Image", id: "menu-add-image", - icon: "fas fa-camera" + icon: "fas fa-camera", }, { name: "Clear Rulers", id: "menu-clear-rulers", - icon: "fas fa-ruler" - } - ].forEach(entry => { + icon: "fas fa-ruler", + }, + ].forEach((entry) => { const buttonHolder = document.createElement("div"); buttonHolder.classList.add("menu-button-holder"); const button = document.createElement("button"); @@ -2659,7 +2910,7 @@ function prepareSidebar() { } if (entry.classes) { - entry.classes.forEach(cls => icon.classList.add(cls)); + entry.classes.forEach((cls) => icon.classList.add(cls)); } const actionText = document.createElement("span"); @@ -2680,16 +2931,17 @@ function prepareSidebar() { const input = document.createElement("input"); buttonHolder.appendChild(input); input.placeholder = "default"; - input.addEventListener("keyup", e => { + input.addEventListener("keyup", (e) => { if (e.key === "Enter") { - const name = document.querySelector("#menu-save ~ input").value; + const name = + document.querySelector("#menu-save ~ input").value; if (/\S/.test(name)) { saveScene(name); } updateSaveInfo(); e.preventDefault(); } - }) + }); } if (entry.select) { @@ -2714,19 +2966,19 @@ function toggleBodyClass(cls, setting) { } const backgroundColors = { - "none": "#00000000", - "black": "#000", - "dark": "#111", - "medium": "#333", - "light": "#555" -} + none: "#00000000", + black: "#000", + dark: "#111", + medium: "#333", + light: "#555", +}; const settingsCategories = { - "background": "Background", - "controls": "Controls", - "info": "Info", - "visuals": "Visuals" -} + background: "Background", + controls: "Controls", + info: "Info", + visuals: "Visuals", +}; const groundPosChoices = [ "very-high", @@ -2749,7 +3001,7 @@ const settingsData = { set value(param) { config.drawYAxis = param; drawScales(false); - } + }, }, "show-horizontal-scale": { name: "Horiziontal Scale", @@ -2762,7 +3014,7 @@ const settingsData = { set value(param) { config.drawXAxis = param; drawScales(false); - } + }, }, "show-altitudes": { name: "Altitudes", @@ -2782,7 +3034,7 @@ const settingsData = { "airspaces", "races", "olympic-records", - "d&d-sizes" + "d&d-sizes", ], get value() { return config.drawAltitudes; @@ -2790,7 +3042,7 @@ const settingsData = { set value(param) { config.drawAltitudes = param; drawScales(false); - } + }, }, "lock-y-axis": { name: "Lock Y-Axis", @@ -2806,7 +3058,7 @@ const settingsData = { if (param) { updateSizes(); } - } + }, }, "ground-snap": { name: "Snap to Ground", @@ -2818,32 +3070,28 @@ const settingsData = { }, set value(param) { config.groundSnap = param; - } + }, }, "axis-spacing": { name: "Axis Spacing", desc: "How frequent the axis lines are", type: "select", default: "standard", - options: [ - "dense", - "standard", - "sparse" - ], + options: ["dense", "standard", "sparse"], get value() { return config.axisSpacing; }, set value(param) { config.axisSpacing = param; const factor = { - "dense": 0.5, - "standard": 1, - "sparse": 2 + dense: 0.5, + standard: 1, + sparse: 2, }[param]; config.minLineSize = factor * 100; config.maxLineSize = factor * 150; updateSizes(); - } + }, }, "ground-type": { name: "Ground", @@ -2851,20 +3099,16 @@ const settingsData = { type: "select", default: "black", disabled: "none", - options: [ - "none", - "black", - "dark", - "medium", - "light", - ], + options: ["none", "black", "dark", "medium", "light"], get value() { return config.groundKind; }, set value(param) { config.groundKind = param; - document.querySelector("#ground").style.setProperty("--ground-color", backgroundColors[param]) - } + document + .querySelector("#ground") + .style.setProperty("--ground-color", backgroundColors[param]); + }, }, "ground-pos": { name: "Ground Position", @@ -2879,26 +3123,21 @@ const settingsData = { config.groundPos = param; updateScrollButtons(); updateSizes(); - } + }, }, "background-brightness": { name: "Background Color", desc: "How bright the background is", type: "select", default: "medium", - options: [ - "black", - "dark", - "medium", - "light", - ], + options: ["black", "dark", "medium", "light"], get value() { return config.background; }, set value(param) { config.background = param; drawScales(); - } + }, }, "auto-scale": { name: "Auto-Size World", @@ -2911,7 +3150,7 @@ const settingsData = { set value(param) { config.autoFit = param; checkFitWorld(); - } + }, }, "auto-units": { name: "Auto-Select Units", @@ -2923,7 +3162,7 @@ const settingsData = { }, set value(param) { config.autoUnits = param; - } + }, }, "zoom-when-adding": { name: "Zoom On Add", @@ -2935,7 +3174,7 @@ const settingsData = { }, set value(param) { config.autoFitAdd = param; - } + }, }, "zoom-when-sizing": { name: "Zoom On Size", @@ -2947,7 +3186,7 @@ const settingsData = { }, set value(param) { config.autoFitSize = param; - } + }, }, "show-ratios": { name: "Show Ratios", @@ -2960,7 +3199,7 @@ const settingsData = { set value(param) { config.showRatios = param; updateInfo(); - } + }, }, "show-horizon": { name: "Show Horizon", @@ -2973,7 +3212,7 @@ const settingsData = { set value(param) { config.showHorizon = param; updateInfo(); - } + }, }, "attach-rulers": { name: "Attach Rulers", @@ -2985,29 +3224,23 @@ const settingsData = { }, set value(param) { config.rulersStick = param; - } + }, }, - "units": { + units: { name: "Default Units", desc: "Which kind of unit to use by default", type: "select", default: "metric", - options: [ - "metric", - "customary", - "relative", - "quirky", - "human" - ], + options: ["metric", "customary", "relative", "quirky", "human"], get value() { return config.units; }, set value(param) { config.units = param; updateSizes(); - } + }, }, - "names": { + names: { name: "Show Names", desc: "Display names over entities", type: "toggle", @@ -3017,7 +3250,7 @@ const settingsData = { }, set value(param) { toggleBodyClass("toggle-entity-name", param); - } + }, }, "bottom-names": { name: "Bottom Names", @@ -3029,7 +3262,7 @@ const settingsData = { }, set value(param) { toggleBodyClass("toggle-bottom-name", param); - } + }, }, "top-names": { name: "Show Arrows", @@ -3041,7 +3274,7 @@ const settingsData = { }, set value(param) { toggleBodyClass("toggle-top-name", param); - } + }, }, "height-bars": { name: "Height Bars", @@ -3053,7 +3286,7 @@ const settingsData = { }, set value(param) { toggleBodyClass("toggle-height-bars", param); - } + }, }, "flag-nsfw": { name: "Flag NSFW", @@ -3065,7 +3298,7 @@ const settingsData = { }, set value(param) { toggleBodyClass("flag-nsfw", param); - } + }, }, "glowing-entities": { name: "Glowing Edges", @@ -3077,31 +3310,27 @@ const settingsData = { }, set value(param) { toggleBodyClass("toggle-entity-glow", param); - } + }, }, "select-style": { name: "Selection Style", desc: "How to highlight selected entities (outlines are laggier", type: "select", default: "color", - options: [ - "color", - "outline" - ], + options: ["color", "outline"], get value() { if (checkBodyClass("highlight-color")) { - return "color" - } - else { - return "outline" + return "color"; + } else { + return "outline"; } }, set value(param) { - toggleBodyClass("highlight-color", param === "color") - toggleBodyClass("highlight-outline", param === "outline") - } + toggleBodyClass("highlight-color", param === "color"); + toggleBodyClass("highlight-outline", param === "outline"); + }, }, - "smoothing": { + smoothing: { name: "Smoothing", desc: "Smooth out movements and size changes. Disable for better performance.", type: "toggle", @@ -3111,7 +3340,7 @@ const settingsData = { }, set value(param) { toggleBodyClass("smoothing", param); - } + }, }, "auto-mass": { name: "Estimate Mass", @@ -3119,17 +3348,13 @@ const settingsData = { type: "select", default: "off", disabled: "off", - options: [ - "off", - "human", - "quadruped at shoulder", - ], + options: ["off", "human", "quadruped at shoulder"], get value() { - return config.autoMass + return config.autoMass; }, set value(param) { - config.autoMass = param - } + config.autoMass = param; + }, }, "auto-food-intake": { name: "Estimate Food Intake", @@ -3137,11 +3362,11 @@ const settingsData = { type: "toggle", default: false, get value() { - return config.autoFoodIntake + return config.autoFoodIntake; }, set value(param) { - config.autoFoodIntake = param - } + config.autoFoodIntake = param; + }, }, "auto-caloric-value": { name: "Estimate Caloric Value", @@ -3149,11 +3374,11 @@ const settingsData = { type: "toggle", default: false, get value() { - return config.autoCaloricValue + return config.autoCaloricValue; }, set value(param) { - config.autoCaloricValue = param - } + config.autoCaloricValue = param; + }, }, "auto-prey-capacity": { name: "Estimate Prey Capacity", @@ -3161,19 +3386,15 @@ const settingsData = { type: "select", default: "off", disabled: "off", - options: [ - "off", - "realistic", - "same-size" - ], + options: ["off", "realistic", "same-size"], get value() { return config.autoPreyCapacity; }, set value(param) { config.autoPreyCapacity = param; - } + }, }, -} +}; function prepareSettings(userSettings) { const menubar = document.querySelector("#settings-menu"); @@ -3181,13 +3402,13 @@ function prepareSettings(userSettings) { Object.entries(settingsData).forEach(([id, entry]) => { const holder = document.createElement("label"); holder.classList.add("settings-holder"); - + const input = document.createElement("input"); input.id = "setting-" + id; - + const vertical = document.createElement("div"); vertical.classList.add("settings-vertical"); - + const name = document.createElement("label"); name.innerText = entry.name; name.classList.add("settings-name"); @@ -3201,8 +3422,11 @@ function prepareSettings(userSettings) { if (entry.type == "toggle") { input.type = "checkbox"; - input.checked = userSettings[id] === undefined ? entry.default : userSettings[id]; - + input.checked = + userSettings[id] === undefined + ? entry.default + : userSettings[id]; + holder.setAttribute("for", input.id); vertical.appendChild(name); @@ -3219,10 +3443,10 @@ function prepareSettings(userSettings) { holder.classList.remove("enabled"); holder.classList.add("disabled"); } - + entry.value = input.checked; - } - + }; + setTimeout(update); input.addEventListener("change", update); @@ -3232,13 +3456,16 @@ function prepareSettings(userSettings) { const select = document.createElement("select"); select.id = "setting-" + id; - entry.options.forEach(choice => { + entry.options.forEach((choice) => { const option = document.createElement("option"); option.innerText = choice; select.appendChild(option); - }) + }); - select.value = userSettings[id] === undefined ? entry.default : userSettings[id]; + select.value = + userSettings[id] === undefined + ? entry.default + : userSettings[id]; vertical.appendChild(name); vertical.appendChild(desc); @@ -3249,20 +3476,23 @@ function prepareSettings(userSettings) { const update = () => { entry.value = select.value; - if (entry.disabled !== undefined && entry.value !== entry.disabled) { + if ( + entry.disabled !== undefined && + entry.value !== entry.disabled + ) { holder.classList.add("enabled"); holder.classList.remove("disabled"); } else { holder.classList.remove("enabled"); holder.classList.add("disabled"); } - } + }; update(); select.addEventListener("change", update); } - }) + }); } function prepareMenu() { @@ -3278,16 +3508,16 @@ function updateSaveInfo() { const saves = getSaves(); const load = document.querySelector("#menu-load ~ select"); load.innerHTML = ""; - saves.forEach(save => { + saves.forEach((save) => { const option = document.createElement("option"); option.innerText = save; option.value = save; load.appendChild(option); }); - + const del = document.querySelector("#menu-delete ~ select"); del.innerHTML = ""; - saves.forEach(save => { + saves.forEach((save) => { const option = document.createElement("option"); option.innerText = save; option.value = save; @@ -3298,14 +3528,16 @@ function updateSaveInfo() { function getSaves() { try { const results = []; - Object.keys(localStorage).forEach(key => { + Object.keys(localStorage).forEach((key) => { if (key.startsWith("macrovision-save-")) { results.push(key.replace("macrovision-save-", "")); } - }) + }); return results; } catch (err) { - alert("Something went wrong while loading (maybe you didn't have anything saved. Check the F12 console for the error.") + alert( + "Something went wrong while loading (maybe you didn't have anything saved. Check the F12 console for the error." + ); console.error(err); return false; } @@ -3364,13 +3596,14 @@ function setHelpDate() { function doYScroll() { const worldHeight = config.height.toNumber("meters"); - config.y += scrollDirection * worldHeight / 180; + config.y += (scrollDirection * worldHeight) / 180; updateSizes(); scrollDirection *= 1.05; } function doXScroll() { - const worldWidth = config.height.toNumber("meters") / canvasHeight * canvasWidth; - config.x += scrollDirection * worldWidth / 180 ; + const worldWidth = + (config.height.toNumber("meters") / canvasHeight) * canvasWidth; + config.x += (scrollDirection * worldWidth) / 180; updateSizes(); scrollDirection *= 1.05; } @@ -3386,7 +3619,10 @@ function doSize() { if (selected) { const entity = entities[selected.dataset.key]; const oldHeight = entity.views[entity.view].height; - entity.views[entity.view].height = math.multiply(oldHeight, sizeDirection < 0 ? -1/sizeDirection : sizeDirection); + entity.views[entity.view].height = math.multiply( + oldHeight, + sizeDirection < 0 ? -1 / sizeDirection : sizeDirection + ); entity.dirty = true; updateEntityOptions(entity, entity.view); updateViewOptions(entity, entity.view); @@ -3399,9 +3635,15 @@ function doSize() { const worldHeight = config.height.toNumber("meters"); if (ownHeight * extra > worldHeight) { - setWorldHeight(config.height, math.multiply(entity.views[entity.view].height, extra)); + setWorldHeight( + config.height, + math.multiply(entity.views[entity.view].height, extra) + ); } else if (ownHeight * extra * 10 < worldHeight) { - setWorldHeight(config.height, math.multiply(entity.views[entity.view].height, extra * 10)); + setWorldHeight( + config.height, + math.multiply(entity.views[entity.view].height, extra * 10) + ); } } } @@ -3410,7 +3652,9 @@ function selectNewUnit() { const unitSelector = document.querySelector("#options-height-unit"); checkFitWorld(); const scaleInput = document.querySelector("#options-height-value"); - const newVal = math.unit(scaleInput.value, unitSelector.dataset.oldUnit).toNumber(unitSelector.value); + const newVal = math + .unit(scaleInput.value, unitSelector.dataset.oldUnit) + .toNumber(unitSelector.value); setNumericInput(scaleInput, newVal); updateWorldHeight(); unitSelector.dataset.oldUnit = unitSelector.value; @@ -3418,47 +3662,58 @@ function selectNewUnit() { // given a world position, return the position relative to the entity at normal scale function entityRelativePosition(pos, entityElement) { - const entity = entities[entityElement.dataset.key] - const x = parseFloat(entityElement.dataset.x) - const y = parseFloat(entityElement.dataset.y) + const entity = entities[entityElement.dataset.key]; + const x = parseFloat(entityElement.dataset.x); + const y = parseFloat(entityElement.dataset.y); - pos.x -= x - pos.y -= y + pos.x -= x; + pos.y -= y; - pos.x /= entity.scale - pos.y /= entity.scale + pos.x /= entity.scale; + pos.y /= entity.scale; - return pos + return pos; } document.addEventListener("DOMContentLoaded", () => { prepareMenu(); prepareEntities(); - document.querySelector("#open-help").addEventListener("click", e => { + document.querySelector("#open-help").addEventListener("click", (e) => { setHelpDate(); document.querySelector("#open-help").classList.remove("highlighted"); - window.open("https://www.notion.so/Macrovision-5c7f9377424743358ddf6db5671f439e", "_blank"); + window.open( + "https://www.notion.so/Macrovision-5c7f9377424743358ddf6db5671f439e", + "_blank" + ); }); - document.querySelector("#copy-screenshot").addEventListener("click", e => { - copyScreenshot(); - }); + document + .querySelector("#copy-screenshot") + .addEventListener("click", (e) => { + copyScreenshot(); + }); - document.querySelector("#save-screenshot").addEventListener("click", e => { - saveScreenshot(); - }); + document + .querySelector("#save-screenshot") + .addEventListener("click", (e) => { + saveScreenshot(); + }); - document.querySelector("#open-screenshot").addEventListener("click", e => { - openScreenshot(); - }); - - document.querySelector("#toggle-menu").addEventListener("click", e => { + document + .querySelector("#open-screenshot") + .addEventListener("click", (e) => { + openScreenshot(); + }); + + document.querySelector("#toggle-menu").addEventListener("click", (e) => { const popoutMenu = document.querySelector("#sidebar-menu"); if (popoutMenu.classList.contains("visible")) { popoutMenu.classList.remove("visible"); } else { - document.querySelectorAll(".popout-menu").forEach(menu => menu.classList.remove("visible")); + document + .querySelectorAll(".popout-menu") + .forEach((menu) => menu.classList.remove("visible")); const rect = e.target.getBoundingClientRect(); popoutMenu.classList.add("visible"); @@ -3475,129 +3730,164 @@ document.addEventListener("DOMContentLoaded", () => { e.stopPropagation(); }); - document.querySelector("#sidebar-menu").addEventListener("click", e => { + document.querySelector("#sidebar-menu").addEventListener("click", (e) => { e.stopPropagation(); }); - document.addEventListener("click", e => { + document.addEventListener("click", (e) => { document.querySelector("#sidebar-menu").classList.remove("visible"); }); - - document.querySelector("#toggle-settings").addEventListener("click", e => { - const popoutMenu = document.querySelector("#settings-menu"); - if (popoutMenu.classList.contains("visible")) { - popoutMenu.classList.remove("visible"); - } else { - document.querySelectorAll(".popout-menu").forEach(menu => menu.classList.remove("visible")); - const rect = e.target.getBoundingClientRect(); - popoutMenu.classList.add("visible"); - popoutMenu.style.left = rect.x + rect.width + 10 + "px"; - popoutMenu.style.top = rect.y + rect.height + 10 + "px"; - - let menuWidth = popoutMenu.getBoundingClientRect().width; - let screenWidth = window.innerWidth; - - if (menuWidth * 1.5 > screenWidth) { - popoutMenu.style.left = 25 + "px"; + document + .querySelector("#toggle-settings") + .addEventListener("click", (e) => { + const popoutMenu = document.querySelector("#settings-menu"); + if (popoutMenu.classList.contains("visible")) { + popoutMenu.classList.remove("visible"); + } else { + document + .querySelectorAll(".popout-menu") + .forEach((menu) => menu.classList.remove("visible")); + const rect = e.target.getBoundingClientRect(); + popoutMenu.classList.add("visible"); + popoutMenu.style.left = rect.x + rect.width + 10 + "px"; + popoutMenu.style.top = rect.y + rect.height + 10 + "px"; + + let menuWidth = popoutMenu.getBoundingClientRect().width; + let screenWidth = window.innerWidth; + + if (menuWidth * 1.5 > screenWidth) { + popoutMenu.style.left = 25 + "px"; + } } - } - e.stopPropagation(); - }); + e.stopPropagation(); + }); - document.querySelector("#settings-menu").addEventListener("click", e => { + document.querySelector("#settings-menu").addEventListener("click", (e) => { e.stopPropagation(); }); - document.addEventListener("click", e => { + document.addEventListener("click", (e) => { document.querySelector("#settings-menu").classList.remove("visible"); }); - - window.addEventListener("unload", () => { saveScene("autosave"); setUserSettings(exportUserSettings()); }); - document.querySelector("#options-selected-entity").addEventListener("input", e => { - if (e.target.value == "None") { - deselect() - } else { - select(document.querySelector("#entity-" + e.target.value)); - } - - }); + document + .querySelector("#options-selected-entity") + .addEventListener("input", (e) => { + if (e.target.value == "None") { + deselect(); + } else { + select(document.querySelector("#entity-" + e.target.value)); + } + }); - document.querySelector("#menu-toggle-sidebar").addEventListener("click", e => { - const sidebar = document.querySelector("#options"); - if (sidebar.classList.contains("hidden")) { - sidebar.classList.remove("hidden"); - e.target.classList.remove("rotate-forward"); - e.target.classList.add("rotate-backward"); - } else { - sidebar.classList.add("hidden"); - e.target.classList.add("rotate-forward"); - e.target.classList.remove("rotate-backward"); - } - handleResize(); - }); + document + .querySelector("#menu-toggle-sidebar") + .addEventListener("click", (e) => { + const sidebar = document.querySelector("#options"); + if (sidebar.classList.contains("hidden")) { + sidebar.classList.remove("hidden"); + e.target.classList.remove("rotate-forward"); + e.target.classList.add("rotate-backward"); + } else { + sidebar.classList.add("hidden"); + e.target.classList.add("rotate-forward"); + e.target.classList.remove("rotate-backward"); + } + handleResize(); + }); - document.querySelector("#menu-fullscreen").addEventListener("click", toggleFullScreen); + document + .querySelector("#menu-fullscreen") + .addEventListener("click", toggleFullScreen); - document.querySelector("#options-order-forward").addEventListener("click", e => { - if (selected) { - entities[selected.dataset.key].priority += 1; - } - document.querySelector("#options-order-display").innerText = entities[selected.dataset.key].priority; - updateSizes(); - }); + document + .querySelector("#options-order-forward") + .addEventListener("click", (e) => { + if (selected) { + entities[selected.dataset.key].priority += 1; + } + document.querySelector("#options-order-display").innerText = + entities[selected.dataset.key].priority; + updateSizes(); + }); - document.querySelector("#options-order-back").addEventListener("click", e => { - if (selected) { - entities[selected.dataset.key].priority -= 1; - } - document.querySelector("#options-order-display").innerText = entities[selected.dataset.key].priority; - updateSizes(); - }); + document + .querySelector("#options-order-back") + .addEventListener("click", (e) => { + if (selected) { + entities[selected.dataset.key].priority -= 1; + } + document.querySelector("#options-order-display").innerText = + entities[selected.dataset.key].priority; + updateSizes(); + }); - document.querySelector("#options-brightness-up").addEventListener("click", e => { - if (selected) { - entities[selected.dataset.key].brightness += 1; - } - document.querySelector("#options-brightness-display").innerText = entities[selected.dataset.key].brightness; - updateSizes(); - }); + document + .querySelector("#options-brightness-up") + .addEventListener("click", (e) => { + if (selected) { + entities[selected.dataset.key].brightness += 1; + } + document.querySelector("#options-brightness-display").innerText = + entities[selected.dataset.key].brightness; + updateSizes(); + }); - document.querySelector("#options-brightness-down").addEventListener("click", e => { - if (selected) { - entities[selected.dataset.key].brightness -= 1; - } - document.querySelector("#options-brightness-display").innerText = entities[selected.dataset.key].brightness; - updateSizes(); - }); + document + .querySelector("#options-brightness-down") + .addEventListener("click", (e) => { + if (selected) { + entities[selected.dataset.key].brightness -= 1; + } + document.querySelector("#options-brightness-display").innerText = + entities[selected.dataset.key].brightness; + updateSizes(); + }); - document.querySelector("#options-rotate-left").addEventListener("click", e => { - if (selected) { - entities[selected.dataset.key].rotation -= Math.PI/4; - } - selected.querySelector("img").style.setProperty("--rotation", (entities[selected.dataset.key].rotation * 180 / Math.PI) + "deg") - updateSizes(); - }); + document + .querySelector("#options-rotate-left") + .addEventListener("click", (e) => { + if (selected) { + entities[selected.dataset.key].rotation -= Math.PI / 4; + } + selected + .querySelector("img") + .style.setProperty( + "--rotation", + (entities[selected.dataset.key].rotation * 180) / Math.PI + + "deg" + ); + updateSizes(); + }); - document.querySelector("#options-rotate-right").addEventListener("click", e => { - if (selected) { - entities[selected.dataset.key].rotation += Math.PI/4; - } - selected.querySelector("img").style.setProperty("--rotation", (entities[selected.dataset.key].rotation * 180 / Math.PI) + "deg") - updateSizes(); - }); + document + .querySelector("#options-rotate-right") + .addEventListener("click", (e) => { + if (selected) { + entities[selected.dataset.key].rotation += Math.PI / 4; + } + selected + .querySelector("img") + .style.setProperty( + "--rotation", + (entities[selected.dataset.key].rotation * 180) / Math.PI + + "deg" + ); + updateSizes(); + }); - document.querySelector("#options-flip").addEventListener("click", e => { + document.querySelector("#options-flip").addEventListener("click", (e) => { if (selected) { selected.querySelector(".entity-image").classList.toggle("flipped"); } - document.querySelector("#options-brightness-display").innerText = entities[selected.dataset.key].brightness; + document.querySelector("#options-brightness-display").innerText = + entities[selected.dataset.key].brightness; updateSizes(); }); @@ -3610,7 +3900,7 @@ document.addEventListener("DOMContentLoaded", () => { sceneChoices.appendChild(option); }); - document.querySelector("#menu-preset").addEventListener("click", e => { + document.querySelector("#menu-preset").addEventListener("click", (e) => { const chosen = sceneChoices.value; removeAllEntities(); scenes[chosen](); @@ -3620,13 +3910,17 @@ document.addEventListener("DOMContentLoaded", () => { canvasWidth = document.querySelector("#display").clientWidth - 100; canvasHeight = document.querySelector("#display").clientHeight - 50; - document.querySelector("#options-height-value").addEventListener("change", e => { - updateWorldHeight(); - }) + document + .querySelector("#options-height-value") + .addEventListener("change", (e) => { + updateWorldHeight(); + }); - document.querySelector("#options-height-value").addEventListener("keydown", e => { - e.stopPropagation(); - }) + document + .querySelector("#options-height-value") + .addEventListener("keydown", (e) => { + e.stopPropagation(); + }); const unitSelector = document.querySelector("#options-height-unit"); @@ -3635,15 +3929,14 @@ document.addEventListener("DOMContentLoaded", () => { optGroup.label = group; unitSelector.appendChild(optGroup); - entries.forEach(entry => { + entries.forEach((entry) => { const option = document.createElement("option"); option.innerText = entry; // we haven't loaded user settings yet, so we can't choose the unit just yet unitSelector.appendChild(option); - }) - + }); }); unitSelector.addEventListener("input", selectNewUnit); @@ -3658,64 +3951,82 @@ document.addEventListener("DOMContentLoaded", () => { param = new URL(window.location.href).searchParams.get("scene"); } - document.querySelector("#world").addEventListener("mousedown", e => { + document.querySelector("#world").addEventListener("mousedown", (e) => { // only middle mouse clicks if (e.which == 2) { panning = true; panOffsetX = e.clientX; panOffsetY = e.clientY; - Object.keys(entities).forEach(key => { - document.querySelector("#entity-" + key).classList.add("no-transition"); + Object.keys(entities).forEach((key) => { + document + .querySelector("#entity-" + key) + .classList.add("no-transition"); }); } }); - document.addEventListener("mouseup", e => { + document.addEventListener("mouseup", (e) => { if (e.which == 2) { panning = false; - Object.keys(entities).forEach(key => { - document.querySelector("#entity-" + key).classList.remove("no-transition"); + Object.keys(entities).forEach((key) => { + document + .querySelector("#entity-" + key) + .classList.remove("no-transition"); }); } }); - document.querySelector("#world").addEventListener("touchstart", e => { + document.querySelector("#world").addEventListener("touchstart", (e) => { 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"); + Object.keys(entities).forEach((key) => { + document + .querySelector("#entity-" + key) + .classList.add("no-transition"); }); } }); - document.querySelector("#world").addEventListener("touchend", e => { + document.querySelector("#world").addEventListener("touchend", (e) => { panning = false; - Object.keys(entities).forEach(key => { - document.querySelector("#entity-" + key).classList.remove("no-transition"); + Object.keys(entities).forEach((key) => { + document + .querySelector("#entity-" + key) + .classList.remove("no-transition"); }); }); - document.querySelector("#world").addEventListener("mousedown", e => { + 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; + let entX = document + .querySelector("#entities") + .getBoundingClientRect().x; + let entY = document + .querySelector("#entities") + .getBoundingClientRect().y; let pos = pix2pos({ x: e.clientX - entX, y: e.clientY - entY }); if (config.rulersStick && selected) { - pos = entityRelativePosition(pos, selected) + pos = entityRelativePosition(pos, selected); } - currentRuler = { x0: pos.x, y0: pos.y, x1: pos.y, y1: pos.y, entityKey: null }; + currentRuler = { + x0: pos.x, + y0: pos.y, + x1: pos.y, + y1: pos.y, + entityKey: null, + }; if (config.rulersStick && selected) { - currentRuler.entityKey = selected.dataset.key + currentRuler.entityKey = selected.dataset.key; } } }); - document.querySelector("#world").addEventListener("mouseup", e => { + document.querySelector("#world").addEventListener("mouseup", (e) => { // only left mouse clicks if (e.which == 1 && currentRuler) { rulers.push(currentRuler); @@ -3724,22 +4035,35 @@ document.addEventListener("DOMContentLoaded", () => { } }); - document.querySelector("#world").addEventListener("touchstart", e => { + document.querySelector("#world").addEventListener("touchstart", (e) => { if (rulerMode) { - let entX = document.querySelector("#entities").getBoundingClientRect().x; - let entY = document.querySelector("#entities").getBoundingClientRect().y; - let pos = pix2pos({ x: e.touches[0].clientX - entX, y: e.touches[0].clientY - entY }); + let entX = document + .querySelector("#entities") + .getBoundingClientRect().x; + let entY = document + .querySelector("#entities") + .getBoundingClientRect().y; + let pos = pix2pos({ + x: e.touches[0].clientX - entX, + y: e.touches[0].clientY - entY, + }); if (config.rulersStick && selected) { - pos = entityRelativePosition(pos, selected) + pos = entityRelativePosition(pos, selected); } - currentRuler = { x0: pos.x, y0: pos.y, x1: pos.y, y1: pos.y, entityKey: null }; + currentRuler = { + x0: pos.x, + y0: pos.y, + x1: pos.y, + y1: pos.y, + entityKey: null, + }; if (config.rulersStick && selected) { - currentRuler.entityKey = selected.dataset.key + currentRuler.entityKey = selected.dataset.key; } } }); - document.querySelector("#world").addEventListener("touchend", e => { + document.querySelector("#world").addEventListener("touchend", (e) => { if (currentRuler) { rulers.push(currentRuler); currentRuler = null; @@ -3749,42 +4073,45 @@ document.addEventListener("DOMContentLoaded", () => { document.querySelector("body").appendChild(testCtx.canvas); - world.addEventListener("mousedown", e => deselect(e)); - world.addEventListener("touchstart", e => deselect({ - which: 1, - })); + world.addEventListener("mousedown", (e) => deselect(e)); + world.addEventListener("touchstart", (e) => + deselect({ + which: 1, + }) + ); document.querySelector("#entities").addEventListener("mousedown", deselect); document.querySelector("#display").addEventListener("mousedown", deselect); - document.addEventListener("mouseup", e => clickUp(e)); - document.addEventListener("touchend", e => { + document.addEventListener("mouseup", (e) => clickUp(e)); + document.addEventListener("touchend", (e) => { const fakeEvent = { target: e.target, clientX: e.changedTouches[0].clientX, clientY: e.changedTouches[0].clientY, - which: 1 + which: 1, }; clickUp(fakeEvent); }); const formList = document.querySelector("#entity-form"); - formList.addEventListener("input", e => { + formList.addEventListener("input", (e) => { const entity = entities[selected.dataset.key]; entity.form = e.target.value; - const oldView = entity.currentView + const oldView = entity.currentView; - entity.view = entity.formViews[entity.form] + entity.view = entity.formViews[entity.form]; // to set the size properly, even if we use a non-default view if (Object.keys(entity.forms).length > 0) - entity.views[entity.view].height = entity.formSizes[entity.form].height; + entity.views[entity.view].height = + entity.formSizes[entity.form].height; let found = Object.entries(entity.views).find(([key, view]) => { - return view.form === entity.form && view.name === oldView.name - }) + return view.form === entity.form && view.name === oldView.name; + }); - const newView = found ? found[0] : entity.formViews[entity.form] + const newView = found ? found[0] : entity.formViews[entity.form]; entity.view = newView; preloadViews(entity); @@ -3799,9 +4126,13 @@ document.addEventListener("DOMContentLoaded", () => { displayAttribution(image.source); if (image.bottom !== undefined) { - selected.querySelector(".entity-image").style.setProperty("--offset", ((-1 + image.bottom) * 100) + "%") + selected + .querySelector(".entity-image") + .style.setProperty("--offset", (-1 + image.bottom) * 100 + "%"); } else { - selected.querySelector(".entity-image").style.setProperty("--offset", ((-1) * 100) + "%") + selected + .querySelector(".entity-image") + .style.setProperty("--offset", -1 * 100 + "%"); } if (config.autoFitSize) { @@ -3818,12 +4149,13 @@ document.addEventListener("DOMContentLoaded", () => { const viewList = document.querySelector("#entity-view"); - document.querySelector("#entity-view").addEventListener("input", e => { + document.querySelector("#entity-view").addEventListener("input", (e) => { const entity = entities[selected.dataset.key]; entity.view = e.target.value; preloadViews(entity); - const image = entities[selected.dataset.key].views[e.target.value].image; + const image = + entities[selected.dataset.key].views[e.target.value].image; selected.querySelector(".entity-image").src = image.source; configViewOptions(entity, entity.view); @@ -3831,105 +4163,98 @@ document.addEventListener("DOMContentLoaded", () => { displayAttribution(image.source); if (image.bottom !== undefined) { - selected.querySelector(".entity-image").style.setProperty("--offset", ((-1 + image.bottom) * 100) + "%") + selected + .querySelector(".entity-image") + .style.setProperty("--offset", (-1 + image.bottom) * 100 + "%"); } else { - selected.querySelector(".entity-image").style.setProperty("--offset", ((-1) * 100) + "%") + selected + .querySelector(".entity-image") + .style.setProperty("--offset", -1 * 100 + "%"); } updateSizes(); updateEntityOptions(entities[selected.dataset.key], e.target.value); updateViewOptions(entities[selected.dataset.key], e.target.value); }); - document.querySelector("#entity-view").addEventListener("input", e => { - if (viewList.options[viewList.selectedIndex].classList.contains("nsfw")) { + document.querySelector("#entity-view").addEventListener("input", (e) => { + if ( + viewList.options[viewList.selectedIndex].classList.contains("nsfw") + ) { viewList.classList.add("nsfw"); } else { viewList.classList.remove("nsfw"); } - }) + }); clearViewList(); - document.querySelector("#menu-clear").addEventListener("click", e => { + document.querySelector("#menu-clear").addEventListener("click", (e) => { removeAllEntities(); }); document.querySelector("#delete-entity").disabled = true; - document.querySelector("#delete-entity").addEventListener("click", e => { + document.querySelector("#delete-entity").addEventListener("click", (e) => { if (selected) { removeEntity(selected); selected = null; } }); - document.querySelector("#menu-order-height").addEventListener("click", e => { - const order = Object.keys(entities).sort((a, b) => { - const entA = entities[a]; - const entB = entities[b]; - const viewA = entA.view; - const viewB = entB.view; - const heightA = entA.views[viewA].height.to("meter").value; - const heightB = entB.views[viewB].height.to("meter").value; - return heightA - heightB; - }); + document + .querySelector("#menu-order-height") + .addEventListener("click", (e) => { + const order = Object.keys(entities).sort((a, b) => { + const entA = entities[a]; + const entB = entities[b]; + const viewA = entA.view; + const viewB = entB.view; + const heightA = entA.views[viewA].height.to("meter").value; + const heightB = entB.views[viewB].height.to("meter").value; + return heightA - heightB; + }); - arrangeEntities(order); - }); + arrangeEntities(order); + }); // TODO: write some generic logic for this lol - document.querySelector("#scroll-left").addEventListener("mousedown", e => { - scrollDirection = -1; - clearInterval(scrollHandle); - scrollHandle = setInterval(doXScroll, 1000 / 20); - e.stopPropagation(); - }); - - document.querySelector("#scroll-right").addEventListener("mousedown", e => { - scrollDirection = 1; - clearInterval(scrollHandle); - scrollHandle = setInterval(doXScroll, 1000 / 20); - e.stopPropagation(); - }); - - document.querySelector("#scroll-left").addEventListener("touchstart", e => { - scrollDirection = -1; - clearInterval(scrollHandle); - scrollHandle = setInterval(doXScroll, 1000 / 20); - e.stopPropagation(); - }); - - document.querySelector("#scroll-right").addEventListener("touchstart", e => { - scrollDirection = 1; - clearInterval(scrollHandle); - scrollHandle = setInterval(doXScroll, 1000 / 20); - e.stopPropagation(); - }); - + document + .querySelector("#scroll-left") + .addEventListener("mousedown", (e) => { + scrollDirection = -1; + clearInterval(scrollHandle); + scrollHandle = setInterval(doXScroll, 1000 / 20); + e.stopPropagation(); + }); - document.querySelector("#scroll-up").addEventListener("mousedown", e => { - if (config.lockYAxis) { - moveGround(true); - } else { + document + .querySelector("#scroll-right") + .addEventListener("mousedown", (e) => { scrollDirection = 1; clearInterval(scrollHandle); - scrollHandle = setInterval(doYScroll, 1000 / 20); + scrollHandle = setInterval(doXScroll, 1000 / 20); e.stopPropagation(); - } - }); + }); - document.querySelector("#scroll-down").addEventListener("mousedown", e => { - if (config.lockYAxis) { - moveGround(false); - } else { + document + .querySelector("#scroll-left") + .addEventListener("touchstart", (e) => { scrollDirection = -1; clearInterval(scrollHandle); - scrollHandle = setInterval(doYScroll, 1000 / 20); + scrollHandle = setInterval(doXScroll, 1000 / 20); e.stopPropagation(); - } - }); + }); + + document + .querySelector("#scroll-right") + .addEventListener("touchstart", (e) => { + scrollDirection = 1; + clearInterval(scrollHandle); + scrollHandle = setInterval(doXScroll, 1000 / 20); + e.stopPropagation(); + }); - document.querySelector("#scroll-up").addEventListener("touchstart", e => { + document.querySelector("#scroll-up").addEventListener("mousedown", (e) => { if (config.lockYAxis) { moveGround(true); } else { @@ -3940,104 +4265,130 @@ document.addEventListener("DOMContentLoaded", () => { } }); - document.querySelector("#scroll-down").addEventListener("touchstart", e => { + document + .querySelector("#scroll-down") + .addEventListener("mousedown", (e) => { + if (config.lockYAxis) { + moveGround(false); + } else { + scrollDirection = -1; + clearInterval(scrollHandle); + scrollHandle = setInterval(doYScroll, 1000 / 20); + e.stopPropagation(); + } + }); + + document.querySelector("#scroll-up").addEventListener("touchstart", (e) => { if (config.lockYAxis) { - moveGround(false); + moveGround(true); } else { - scrollDirection = -1; + scrollDirection = 1; clearInterval(scrollHandle); scrollHandle = setInterval(doYScroll, 1000 / 20); e.stopPropagation(); } }); - document.addEventListener("mouseup", e => { + document + .querySelector("#scroll-down") + .addEventListener("touchstart", (e) => { + if (config.lockYAxis) { + moveGround(false); + } else { + scrollDirection = -1; + clearInterval(scrollHandle); + scrollHandle = setInterval(doYScroll, 1000 / 20); + e.stopPropagation(); + } + }); + + document.addEventListener("mouseup", (e) => { clearInterval(scrollHandle); scrollHandle = null; }); - document.addEventListener("touchend", e => { + document.addEventListener("touchend", (e) => { clearInterval(scrollHandle); scrollHandle = null; }); - document.querySelector("#zoom-in").addEventListener("mousedown", e => { + document.querySelector("#zoom-in").addEventListener("mousedown", (e) => { zoomDirection = -1; clearInterval(zoomHandle); zoomHandle = setInterval(doZoom, 1000 / 20); e.stopPropagation(); }); - document.querySelector("#zoom-out").addEventListener("mousedown", e => { + document.querySelector("#zoom-out").addEventListener("mousedown", (e) => { zoomDirection = 1; clearInterval(zoomHandle); zoomHandle = setInterval(doZoom, 1000 / 20); e.stopPropagation(); }); - document.querySelector("#zoom-in").addEventListener("touchstart", e => { + document.querySelector("#zoom-in").addEventListener("touchstart", (e) => { zoomDirection = -1; clearInterval(zoomHandle); zoomHandle = setInterval(doZoom, 1000 / 20); e.stopPropagation(); }); - document.querySelector("#zoom-out").addEventListener("touchstart", e => { + document.querySelector("#zoom-out").addEventListener("touchstart", (e) => { zoomDirection = 1; clearInterval(zoomHandle); zoomHandle = setInterval(doZoom, 1000 / 20); e.stopPropagation(); }); - document.addEventListener("mouseup", e => { + document.addEventListener("mouseup", (e) => { clearInterval(zoomHandle); zoomHandle = null; }); - document.addEventListener("touchend", e => { + document.addEventListener("touchend", (e) => { clearInterval(zoomHandle); zoomHandle = null; }); - document.querySelector("#shrink").addEventListener("mousedown", e => { + document.querySelector("#shrink").addEventListener("mousedown", (e) => { sizeDirection = -1; clearInterval(sizeHandle); sizeHandle = setInterval(doSize, 1000 / 20); e.stopPropagation(); }); - document.querySelector("#grow").addEventListener("mousedown", e => { + document.querySelector("#grow").addEventListener("mousedown", (e) => { sizeDirection = 1; clearInterval(sizeHandle); sizeHandle = setInterval(doSize, 1000 / 20); e.stopPropagation(); }); - document.querySelector("#shrink").addEventListener("touchstart", e => { + document.querySelector("#shrink").addEventListener("touchstart", (e) => { sizeDirection = -1; clearInterval(sizeHandle); sizeHandle = setInterval(doSize, 1000 / 20); e.stopPropagation(); }); - document.querySelector("#grow").addEventListener("touchstart", e => { + document.querySelector("#grow").addEventListener("touchstart", (e) => { sizeDirection = 1; clearInterval(sizeHandle); sizeHandle = setInterval(doSize, 1000 / 20); e.stopPropagation(); }); - document.addEventListener("mouseup", e => { + document.addEventListener("mouseup", (e) => { clearInterval(sizeHandle); sizeHandle = null; }); - document.addEventListener("touchend", e => { + document.addEventListener("touchend", (e) => { clearInterval(sizeHandle); sizeHandle = null; }); - document.querySelector("#ruler").addEventListener("click", e => { + document.querySelector("#ruler").addEventListener("click", (e) => { rulerMode = !rulerMode; if (rulerMode) { toast("Ready to draw a ruler mark"); @@ -4046,15 +4397,15 @@ document.addEventListener("DOMContentLoaded", () => { } }); - document.querySelector("#ruler").addEventListener("mousedown", e => { + document.querySelector("#ruler").addEventListener("mousedown", (e) => { e.stopPropagation(); }); - document.querySelector("#ruler").addEventListener("touchstart", e => { + document.querySelector("#ruler").addEventListener("touchstart", (e) => { e.stopPropagation(); }); - document.querySelector("#fit").addEventListener("click", e => { + document.querySelector("#fit").addEventListener("click", (e) => { if (selected) { let targets = {}; targets[selected.dataset.key] = entities[selected.dataset.key]; @@ -4062,29 +4413,41 @@ document.addEventListener("DOMContentLoaded", () => { } }); - document.querySelector("#fit").addEventListener("mousedown", e => { + document.querySelector("#fit").addEventListener("mousedown", (e) => { e.stopPropagation(); }); - document.querySelector("#fit").addEventListener("touchstart", e => { + document.querySelector("#fit").addEventListener("touchstart", (e) => { e.stopPropagation(); }); - document.querySelector("#options-world-fit").addEventListener("click", () => fitWorld(true)); + document + .querySelector("#options-world-fit") + .addEventListener("click", () => fitWorld(true)); - document.querySelector("#options-reset-pos-x").addEventListener("click", () => { config.x = 0; updateSizes(); }); - document.querySelector("#options-reset-pos-y").addEventListener("click", () => { config.y = 0; updateSizes(); }); + document + .querySelector("#options-reset-pos-x") + .addEventListener("click", () => { + config.x = 0; + updateSizes(); + }); + document + .querySelector("#options-reset-pos-y") + .addEventListener("click", () => { + config.y = 0; + updateSizes(); + }); - document.addEventListener("keydown", e => { + document.addEventListener("keydown", (e) => { if (e.key == "Delete" || e.key == "Backspace") { if (selected) { removeEntity(selected); selected = null; } } - }) + }); - document.addEventListener("keydown", e => { + document.addEventListener("keydown", (e) => { if (e.key == "Shift") { shiftHeld = true; e.preventDefault(); @@ -4095,7 +4458,7 @@ document.addEventListener("DOMContentLoaded", () => { } }); - document.addEventListener("keyup", e => { + document.addEventListener("keyup", (e) => { if (e.key == "Shift") { shiftHeld = false; e.preventDefault(); @@ -4103,7 +4466,6 @@ document.addEventListener("DOMContentLoaded", () => { altHeld = false; e.preventDefault(); } - }); window.addEventListener("resize", handleResize); @@ -4117,20 +4479,19 @@ document.addEventListener("DOMContentLoaded", () => { setTimeout(handleResize, 750); setTimeout(handleResize, 1000); - - document.querySelector("#menu-permalink").addEventListener("click", e => { + document.querySelector("#menu-permalink").addEventListener("click", (e) => { linkScene(); }); - document.querySelector("#menu-export").addEventListener("click", e => { + document.querySelector("#menu-export").addEventListener("click", (e) => { copyScene(); }); - document.querySelector("#menu-import").addEventListener("click", e => { + document.querySelector("#menu-import").addEventListener("click", (e) => { pasteScene(); }); - document.querySelector("#menu-save").addEventListener("click", e => { + document.querySelector("#menu-save").addEventListener("click", (e) => { const name = document.querySelector("#menu-save ~ input").value; if (/\S/.test(name)) { saveScene(name); @@ -4138,45 +4499,48 @@ document.addEventListener("DOMContentLoaded", () => { updateSaveInfo(); }); - document.querySelector("#menu-load").addEventListener("click", e => { + document.querySelector("#menu-load").addEventListener("click", (e) => { const name = document.querySelector("#menu-load ~ select").value; if (/\S/.test(name)) { loadScene(name); } - }); - document.querySelector("#menu-delete").addEventListener("click", e => { + document.querySelector("#menu-delete").addEventListener("click", (e) => { const name = document.querySelector("#menu-delete ~ select").value; if (/\S/.test(name)) { deleteScene(name); } }); - document.querySelector("#menu-load-autosave").addEventListener("click", e => { - loadScene("autosave"); - }); + document + .querySelector("#menu-load-autosave") + .addEventListener("click", (e) => { + loadScene("autosave"); + }); - document.querySelector("#menu-add-image").addEventListener("click", e => { + document.querySelector("#menu-add-image").addEventListener("click", (e) => { document.querySelector("#file-upload-picker").click(); }); - document.querySelector("#file-upload-picker").addEventListener("change", e => { - if (e.target.files.length > 0) { - for (let i=0; i { + if (e.target.files.length > 0) { + for (let i = 0; i < e.target.files.length; i++) { + customEntityFromFile(e.target.files[i]); + } } - } - - }) - - document.querySelector("#menu-clear-rulers").addEventListener("click", e => { - rulers = []; - drawRulers(); - }); + }); - document.addEventListener("paste", e => { + document + .querySelector("#menu-clear-rulers") + .addEventListener("click", (e) => { + rulers = []; + drawRulers(); + }); + document.addEventListener("paste", (e) => { let index = 0; let item = null; let found = false; @@ -4192,7 +4556,7 @@ document.addEventListener("DOMContentLoaded", () => { if (!found) { return; } - + let url = null; const file = item.getAsFile(); @@ -4200,19 +4564,23 @@ document.addEventListener("DOMContentLoaded", () => { customEntityFromFile(file); }); - document.querySelector("#world").addEventListener("dragover", e => { + document.querySelector("#world").addEventListener("dragover", (e) => { e.preventDefault(); - }) + }); - document.querySelector("#world").addEventListener("drop", e => { + document.querySelector("#world").addEventListener("drop", (e) => { e.preventDefault(); if (e.dataTransfer.files.length > 0) { - let entX = document.querySelector("#entities").getBoundingClientRect().x; - let entY = document.querySelector("#entities").getBoundingClientRect().y; - let coords = pix2pos({x: e.clientX-entX, y: e.clientY-entY}); + let entX = document + .querySelector("#entities") + .getBoundingClientRect().x; + let entY = document + .querySelector("#entities") + .getBoundingClientRect().y; + let coords = pix2pos({ x: e.clientX - entX, y: e.clientY - entY }); customEntityFromFile(e.dataTransfer.files[0], coords.x, coords.y); } - }) + }); clearEntityOptions(); clearViewOptions(); clearAttribution(); @@ -4225,21 +4593,18 @@ document.addEventListener("DOMContentLoaded", () => { unitSelector.dataset.oldUnit = defaultUnits.length[config.units]; - document.querySelector("#options-height-unit").value = defaultUnits.length[config.units]; + document.querySelector("#options-height-unit").value = + defaultUnits.length[config.units]; // ...and then update the world height by setting off an input event - document.querySelector("#options-height-unit").dispatchEvent(new Event('input', { - - })); - - + document + .querySelector("#options-height-unit") + .dispatchEvent(new Event("input", {})); if (param === null) { scenes["Empty"](); - } - - else { + } else { try { const data = JSON.parse(b64DecodeUnicode(param)); if (data.entities === undefined) { @@ -4254,20 +4619,20 @@ document.addEventListener("DOMContentLoaded", () => { console.error(err); scenes["Empty"](); - // probably wasn't valid data + // probably wasn't valid data } } - document.querySelector("#world").addEventListener("wheel", e => { + document.querySelector("#world").addEventListener("wheel", (e) => { let magnitude = Math.abs(e.deltaY / 100); if (shiftHeld) { // macs do horizontal scrolling with shift held - - let delta = e.deltaY + + let delta = e.deltaY; if (e.deltaY == 0) { - magnitude = Math.abs(e.deltaX / 100) - delta = e.deltaX + magnitude = Math.abs(e.deltaX / 100); + delta = e.deltaX; } if (selected) { let dir = delta > 0 ? 10 / 11 : 11 / 10; @@ -4275,72 +4640,100 @@ document.addEventListener("DOMContentLoaded", () => { dir *= magnitude; dir += 1; const entity = entities[selected.dataset.key]; - entity.views[entity.view].height = math.multiply(entity.views[entity.view].height, dir); + entity.views[entity.view].height = math.multiply( + entity.views[entity.view].height, + dir + ); entity.dirty = true; updateEntityOptions(entity, entity.view); updateViewOptions(entity, entity.view); updateSizes(true); } else { - const worldWidth = config.height.toNumber("meters") / canvasHeight * canvasWidth; - config.x += (e.deltaY > 0 ? 1 : -1) * worldWidth / 20 ; + const worldWidth = + (config.height.toNumber("meters") / canvasHeight) * + canvasWidth; + config.x += ((e.deltaY > 0 ? 1 : -1) * worldWidth) / 20; updateSizes(); updateSizes(); } - } else { if (config.autoFit) { - toastRateLimit("Zoom is locked! Check Settings to disable.", "zoom-lock", 1000); + toastRateLimit( + "Zoom is locked! Check Settings to disable.", + "zoom-lock", + 1000 + ); } else { let dir = e.deltaY < 0 ? 10 / 11 : 11 / 10; dir -= 1; dir *= magnitude; dir += 1; - const change = config.height.toNumber("meters") - math.multiply(config.height, dir).toNumber("meters"); + const change = + config.height.toNumber("meters") - + math.multiply(config.height, dir).toNumber("meters"); if (!config.lockYAxis) { config.y += change / 2; } - setWorldHeight(config.height, math.multiply(config.height, dir)); + setWorldHeight( + config.height, + math.multiply(config.height, dir) + ); updateWorldOptions(); - } - } checkFitWorld(); - }) + }); 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 }); - + 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, + }); + if (config.rulersStick && selected) { - position = entityRelativePosition(position, selected) + position = entityRelativePosition(position, selected); } 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 }); + 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, + }); if (config.rulersStick && selected) { - position = entityRelativePosition(position, selected) + position = entityRelativePosition(position, selected); } 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 }); - + let position = pix2pos({ + x: e.clientX - dragOffsetX, + y: e.clientY - dragOffsetY, + }); + if (movingInBounds) { position = snapPos(position); } else { @@ -4353,66 +4746,86 @@ document.addEventListener("DOMContentLoaded", () => { clicked.dataset.x = position.x; clicked.dataset.y = position.y; updateEntityElement(entities[clicked.dataset.key], clicked); - + if (hoveringInDeleteArea(e)) { - document.querySelector("#menubar").classList.add("hover-delete"); + document + .querySelector("#menubar") + .classList.add("hover-delete"); } else { - document.querySelector("#menubar").classList.remove("hover-delete"); + document + .querySelector("#menubar") + .classList.remove("hover-delete"); } } if (panning && panReady) { - - const worldWidth = config.height.toNumber("meters") / canvasHeight * canvasWidth; + const worldWidth = + (config.height.toNumber("meters") / canvasHeight) * canvasWidth; const worldHeight = config.height.toNumber("meters"); - - config.x -= (e.clientX - panOffsetX) / canvasWidth * worldWidth; - config.y += (e.clientY - panOffsetY) / canvasHeight * worldHeight; + + config.x -= ((e.clientX - panOffsetX) / canvasWidth) * worldWidth; + config.y += ((e.clientY - panOffsetY) / canvasHeight) * worldHeight; panOffsetX = e.clientX; panOffsetY = e.clientY; updateSizes(); panReady = false; - setTimeout(() => panReady=true, 1000/120); + setTimeout(() => (panReady = true), 1000 / 120); } }); - - document.addEventListener("touchmove", (e) => { - if (clicked) { - e.preventDefault(); - let x = e.touches[0].clientX; - let y = e.touches[0].clientY; - - const position = snapPos(pix2pos({ x: x - dragOffsetX, y: y - dragOffsetY })); - clicked.dataset.x = position.x; - clicked.dataset.y = position.y; - updateEntityElement(entities[clicked.dataset.key], clicked); - - // what a hack - // I should centralize this 'fake event' creation... - if (hoveringInDeleteArea({ clientY: y })) { - document.querySelector("#menubar").classList.add("hover-delete"); - } else { - document.querySelector("#menubar").classList.remove("hover-delete"); + + document.addEventListener( + "touchmove", + (e) => { + if (clicked) { + e.preventDefault(); + let x = e.touches[0].clientX; + let y = e.touches[0].clientY; + + const position = snapPos( + pix2pos({ x: x - dragOffsetX, y: y - dragOffsetY }) + ); + clicked.dataset.x = position.x; + clicked.dataset.y = position.y; + updateEntityElement(entities[clicked.dataset.key], clicked); + + // what a hack + // I should centralize this 'fake event' creation... + if (hoveringInDeleteArea({ clientY: y })) { + document + .querySelector("#menubar") + .classList.add("hover-delete"); + } else { + document + .querySelector("#menubar") + .classList.remove("hover-delete"); + } } - } - if (panning && panReady) { - - const worldWidth = config.height.toNumber("meters") / canvasHeight * canvasWidth; - const worldHeight = config.height.toNumber("meters"); - - config.x -= (e.touches[0].clientX - panOffsetX) / canvasWidth * worldWidth; - config.y += (e.touches[0].clientY - panOffsetY) / canvasHeight * worldHeight; - panOffsetX = e.touches[0].clientX; - panOffsetY = e.touches[0].clientY; - updateSizes(); - panReady = false; - setTimeout(() => panReady=true, 1000/60); - } - }, { passive: false }); - + if (panning && panReady) { + const worldWidth = + (config.height.toNumber("meters") / canvasHeight) * + canvasWidth; + const worldHeight = config.height.toNumber("meters"); + + config.x -= + ((e.touches[0].clientX - panOffsetX) / canvasWidth) * + worldWidth; + config.y += + ((e.touches[0].clientY - panOffsetY) / canvasHeight) * + worldHeight; + panOffsetX = e.touches[0].clientX; + panOffsetY = e.touches[0].clientY; + updateSizes(); + panReady = false; + setTimeout(() => (panReady = true), 1000 / 60); + } + }, + { passive: false } + ); updateWorldHeight(); - document.querySelector("#search-box").addEventListener("change", e => doSearch(e.target.value)); + document + .querySelector("#search-box") + .addEventListener("change", (e) => doSearch(e.target.value)); // Webkit doesn't draw resized SVGs correctly. It will always draw them at their intrinsic size, I think // This checks for that. @@ -4421,14 +4834,17 @@ document.addEventListener("DOMContentLoaded", () => { testCtx.canvas.height = 500; testCtx.clearRect(0, 0, 500, 500); testCtx.drawImage(webkitBugTest, 0, 0, 500, 500); - webkitCanvasBug = testCtx.getImageData(250, 250, 1, 1).data[3] == 0 + webkitCanvasBug = testCtx.getImageData(250, 250, 1, 1).data[3] == 0; if (webkitCanvasBug) { - toast("Heads up: Safari can't select through gaps or take screenshots (check the console for info!)") - console.log("Webkit messes up the process of drawing an SVG image to a canvas. This is important for both selecting things (it lets you click through a gap and hit something else) and for taking screenshots (since it needs to render them to a canvas). Sorry :(") + toast( + "Heads up: Safari can't select through gaps or take screenshots (check the console for info!)" + ); + console.log( + "Webkit messes up the process of drawing an SVG image to a canvas. This is important for both selecting things (it lets you click through a gap and hit something else) and for taking screenshots (since it needs to render them to a canvas). Sorry :(" + ); } - } - + }; }); let searchText = ""; @@ -4438,19 +4854,19 @@ function doSearch(value) { updateFilter(); } -function customEntityFromFile(file, x=0.5, y=0.5) { - file.arrayBuffer().then(buf => { +function customEntityFromFile(file, x = 0.5, y = 0.5) { + file.arrayBuffer().then((buf) => { arr = new Uint8Array(buf); - blob = new Blob([arr], {type: file.type }); - url = window.URL.createObjectURL(blob) + blob = new Blob([arr], { type: file.type }); + url = window.URL.createObjectURL(blob); makeCustomEntity(url, x, y); }); } -function makeCustomEntity(url, x=0.5, y=0.5) { +function makeCustomEntity(url, x = 0.5, y = 0.5) { const maker = createEntityMaker( { - name: "Custom Entity" + name: "Custom Entity", }, { custom: { @@ -4459,16 +4875,16 @@ function makeCustomEntity(url, x=0.5, y=0.5) { name: "Height", power: 1, type: "length", - base: math.unit(6, "feet") - } + base: math.unit(6, "feet"), + }, }, image: { - source: url + source: url, }, name: "Image", info: {}, - rename: false - } + rename: false, + }, }, [] ); @@ -4485,116 +4901,146 @@ const filterDefs = { none: { id: "none", name: "No Filter", - extract: maker => [], - render: name => name, - sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]) + extract: (maker) => [], + render: (name) => name, + sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]), }, author: { id: "author", name: "Authors", - extract: maker => maker.authors ? maker.authors : [], - render: author => attributionData.people[author].name, - sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]) + extract: (maker) => (maker.authors ? maker.authors : []), + render: (author) => attributionData.people[author].name, + sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]), }, owner: { id: "owner", name: "Owners", - extract: maker => maker.owners ? maker.owners : [], - render: owner => attributionData.people[owner].name, - sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]) + extract: (maker) => (maker.owners ? maker.owners : []), + render: (owner) => attributionData.people[owner].name, + sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]), }, species: { id: "species", name: "Species", - extract: maker => maker.info && maker.info.species ? getSpeciesInfo(maker.info.species) : [], - render: species => speciesData[species].name, - sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]) + extract: (maker) => + maker.info && maker.info.species + ? getSpeciesInfo(maker.info.species) + : [], + render: (species) => speciesData[species].name, + sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]), }, tags: { id: "tags", name: "Tags", - extract: maker => maker.info && maker.info.tags ? maker.info.tags : [], - render: tag => tagDefs[tag], - sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]) + extract: (maker) => + maker.info && maker.info.tags ? maker.info.tags : [], + render: (tag) => tagDefs[tag], + sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]), }, size: { id: "size", name: "Normal Size", - extract: maker => maker.sizes && maker.sizes.length > 0 ? Array.from(maker.sizes.reduce((result, size) => { - if (result && !size.default) { - return result; - } - let meters = size.height.toNumber("meters"); - if (meters < 1e-1) { - return ["micro"]; - } else if (meters < 1e1) { - return ["moderate"]; - } else { - return ["macro"]; - } - }, null)) : [], - render: tag => { return { - "micro": "Micro", - "moderate": "Moderate", - "macro": "Macro" - }[tag]}, + extract: (maker) => + maker.sizes && maker.sizes.length > 0 + ? Array.from( + maker.sizes.reduce((result, size) => { + if (result && !size.default) { + return result; + } + let meters = size.height.toNumber("meters"); + if (meters < 1e-1) { + return ["micro"]; + } else if (meters < 1e1) { + return ["moderate"]; + } else { + return ["macro"]; + } + }, null) + ) + : [], + render: (tag) => { + return { + micro: "Micro", + moderate: "Moderate", + macro: "Macro", + }[tag]; + }, sort: (tag1, tag2) => { const order = { - "micro": 0, - "moderate": 1, - "macro": 2 + micro: 0, + moderate: 1, + macro: 2, }; return order[tag1[0]] - order[tag2[0]]; - } + }, }, allSizes: { id: "allSizes", name: "Possible Size", - extract: maker => maker.sizes ? Array.from(maker.sizes.reduce((set, size) => { - - const height = size.height; - - let result = Object.entries(sizeCategories).reduce((result, [name, value]) => { - if (result) { - return result; - } else { - if (math.compare(height, value) <= 0) { - return name; - } - } - }, null); - - set.add(result ? result : "infinite"); - - return set; - }, new Set())) : [], - render: tag => tag[0].toUpperCase() + tag.slice(1), + extract: (maker) => + maker.sizes + ? Array.from( + maker.sizes.reduce((set, size) => { + const height = size.height; + + let result = Object.entries(sizeCategories).reduce( + (result, [name, value]) => { + if (result) { + return result; + } else { + if (math.compare(height, value) <= 0) { + return name; + } + } + }, + null + ); + + set.add(result ? result : "infinite"); + + return set; + }, new Set()) + ) + : [], + render: (tag) => tag[0].toUpperCase() + tag.slice(1), sort: (tag1, tag2) => { const order = [ - "atomic", "microscopic", "tiny", "small", "moderate", "large", "macro", "megamacro", "planetary", "stellar", - "galactic", "universal", "omniversal", "infinite" - ] + "atomic", + "microscopic", + "tiny", + "small", + "moderate", + "large", + "macro", + "megamacro", + "planetary", + "stellar", + "galactic", + "universal", + "omniversal", + "infinite", + ]; return order.indexOf(tag1[0]) - order.indexOf(tag2[0]); - } - } -} + }, + }, +}; const sizeCategories = { - "atomic": math.unit(100, "angstroms"), - "microscopic": math.unit(100, "micrometers"), - "tiny": math.unit(100, "millimeters"), - "small": math.unit(1, "meter"), - "moderate": math.unit(3, "meters"), - "large": math.unit(10, "meters"), - "macro": math.unit(300, "meters"), - "megamacro": math.unit(1000, "kilometers"), - "planetary": math.unit(10, "earths"), - "stellar": math.unit(10, "solarradii"), - "galactic": math.unit(10, "galaxies"), - "universal": math.unit(10, "universes"), - "omniversal": math.unit(10, "multiverses") + atomic: math.unit(100, "angstroms"), + microscopic: math.unit(100, "micrometers"), + tiny: math.unit(100, "millimeters"), + small: math.unit(1, "meter"), + moderate: math.unit(3, "meters"), + large: math.unit(10, "meters"), + macro: math.unit(300, "meters"), + megamacro: math.unit(1000, "kilometers"), + planetary: math.unit(10, "earths"), + stellar: math.unit(10, "solarradii"), + galactic: math.unit(10, "galaxies"), + universal: math.unit(10, "universes"), + omniversal: math.unit(10, "multiverses"), }; function prepareEntities() { @@ -4614,23 +5060,23 @@ function prepareEntities() { availableEntities["species"] = makeSpecies(); availableEntities["vehicles"] = makeVehicles(); - availableEntities["species"].forEach(x => { + availableEntities["species"].forEach((x) => { if (x.name == "Human") { availableEntities["food"].push(x); } - }) + }); availableEntities["characters"].sort((x, y) => { - return x.name.localeCompare(y.name) + return x.name.localeCompare(y.name); }); availableEntities["species"].sort((x, y) => { - return x.name.localeCompare(y.name) + return x.name.localeCompare(y.name); }); availableEntities["objects"].sort((x, y) => { - return x.name.localeCompare(y.name) + return x.name.localeCompare(y.name); }); availableEntities["furniture"].sort((x, y) => { - return x.name.localeCompare(y.name) + return x.name.localeCompare(y.name); }); const holder = document.querySelector("#spawners"); const filterHolder = document.querySelector("#filters"); @@ -4645,9 +5091,9 @@ function prepareEntities() { const filterSets = {}; - Object.values(filterDefs).forEach(filter => { + Object.values(filterDefs).forEach((filter) => { filterSets[filter.id] = new Set(); - }) + }); Object.entries(availableEntities).forEach(([category, entityList]) => { const select = document.createElement("select"); @@ -4664,17 +5110,19 @@ function prepareEntities() { option.classList.add("nsfw"); } - Object.values(filterDefs).forEach(filter => { - filter.extract(entity).forEach(result => { + Object.values(filterDefs).forEach((filter) => { + filter.extract(entity).forEach((result) => { filterSets[filter.id].add(result); }); - }); + }); availableEntitiesByName[entity.name] = entity; - }; + } - select.addEventListener("change", e => { - if (select.options[select.selectedIndex]?.classList.contains("nsfw")) { + select.addEventListener("change", (e) => { + if ( + select.options[select.selectedIndex]?.classList.contains("nsfw") + ) { select.classList.add("nsfw"); } else { select.classList.remove("nsfw"); @@ -4684,37 +5132,43 @@ function prepareEntities() { const entity = entityList[select.selectedIndex]?.constructor(); - if (entity) - { + if (entity) { let img = new Image(); img.src = entity.currentView.image.source; } - - }) + }); const button = document.createElement("button"); button.id = "create-entity-" + category + "-button"; button.classList.add("entity-button"); - button.innerHTML = ""; + button.innerHTML = ''; - button.addEventListener("click", e => { - if (entityList[select.value] == null) - return; + button.addEventListener("click", (e) => { + if (entityList[select.value] == null) return; - const newEntity = entityList[select.value].constructor() + const newEntity = entityList[select.value].constructor(); let yOffset = 0; if (config.lockYAxis) { yOffset = getVerticalOffset(); } else { - yOffset = (config.lockYAxis ? 0 : config.height.toNumber("meters")/2); + yOffset = config.lockYAxis + ? 0 + : config.height.toNumber("meters") / 2; } - displayEntity(newEntity, newEntity.defaultView, config.x, config.y + yOffset, true, true); + displayEntity( + newEntity, + newEntity.defaultView, + config.x, + config.y + yOffset, + true, + true + ); }); const categoryOption = document.createElement("option"); - categoryOption.value = category + categoryOption.value = category; categoryOption.innerText = category; if (category == "characters") { @@ -4728,7 +5182,7 @@ function prepareEntities() { holder.appendChild(button); }); - Object.values(filterDefs).forEach(filter => { + Object.values(filterDefs).forEach((filter) => { const option = document.createElement("option"); option.innerText = filter.name; option.value = filter.id; @@ -4753,24 +5207,39 @@ function prepareEntities() { i.classList.add("fa-plus"); button.appendChild(i); - button.addEventListener("click", e => { - const makers = Array.from(document.querySelector(".entity-select.category-visible")).filter(element => !element.classList.contains("filtered")); + button.addEventListener("click", (e) => { + const makers = Array.from( + document.querySelector(".entity-select.category-visible") + ).filter((element) => !element.classList.contains("filtered")); const count = makers.length + 2; let index = 1; if (makers.length > 50) { - if (!confirm("Really spawn " + makers.length + " things at once?")) { + if ( + !confirm( + "Really spawn " + makers.length + " things at once?" + ) + ) { return; } } - const worldWidth = config.height.toNumber("meters") / canvasHeight * canvasWidth; + const worldWidth = + (config.height.toNumber("meters") / canvasHeight) * canvasWidth; - const spawned = makers.map(element => { - const category = document.querySelector("#category-picker").value; + const spawned = makers.map((element) => { + const category = + document.querySelector("#category-picker").value; const maker = availableEntities[category][element.value]; - const entity = maker.constructor() - displayEntity(entity, entity.view, -worldWidth * 0.45 + config.x + worldWidth * 0.9 * index / (count - 1), config.y); + const entity = maker.constructor(); + displayEntity( + entity, + entity.view, + -worldWidth * 0.45 + + config.x + + (worldWidth * 0.9 * index) / (count - 1), + config.y + ); index += 1; return entityIndex - 1; }); @@ -4778,36 +5247,49 @@ function prepareEntities() { if (config.autoFitAdd) { let targets = {}; - spawned.forEach(key => { + spawned.forEach((key) => { targets[key] = entities[key]; - }) + }); fitEntities(targets); } }); - Array.from(filterSets[filter.id]).map(name => [name, filter.render(name)]).sort(filterDefs[filter.id].sort).forEach(name => { - const option = document.createElement("option"); - option.innerText = name[1]; - option.value = name[0]; - filterNameSelect.appendChild(option); - }); - - filterNameSelect.addEventListener("change", e => { + Array.from(filterSets[filter.id]) + .map((name) => [name, filter.render(name)]) + .sort(filterDefs[filter.id].sort) + .forEach((name) => { + const option = document.createElement("option"); + option.innerText = name[1]; + option.value = name[0]; + filterNameSelect.appendChild(option); + }); + + filterNameSelect.addEventListener("change", (e) => { updateFilter(); }); }); - console.log("Loaded " + Object.keys(availableEntitiesByName).length + " entities"); + console.log( + "Loaded " + Object.keys(availableEntitiesByName).length + " entities" + ); - categorySelect.addEventListener("input", e => { - const oldSelect = document.querySelector(".entity-select.category-visible"); + categorySelect.addEventListener("input", (e) => { + const oldSelect = document.querySelector( + ".entity-select.category-visible" + ); oldSelect.classList.remove("category-visible"); - const oldButton = document.querySelector(".entity-button.category-visible"); + const oldButton = document.querySelector( + ".entity-button.category-visible" + ); oldButton.classList.remove("category-visible"); - const newSelect = document.querySelector("#create-entity-" + e.target.value); + const newSelect = document.querySelector( + "#create-entity-" + e.target.value + ); newSelect.classList.add("category-visible"); - const newButton = document.querySelector("#create-entity-" + e.target.value + "-button"); + const newButton = document.querySelector( + "#create-entity-" + e.target.value + "-button" + ); newButton.classList.add("category-visible"); recomputeFilters(); @@ -4816,10 +5298,11 @@ function prepareEntities() { recomputeFilters(); - filterSelect.addEventListener("input", e => { - const oldSelect = document.querySelector(".filter-select.category-visible"); - if (oldSelect) - oldSelect.classList.remove("category-visible"); + filterSelect.addEventListener("input", (e) => { + const oldSelect = document.querySelector( + ".filter-select.category-visible" + ); + if (oldSelect) oldSelect.classList.remove("category-visible"); const newSelect = document.querySelector("#filter-" + e.target.value); if (newSelect && e.target.value != "none") @@ -4828,8 +5311,7 @@ function prepareEntities() { updateFilter(); }); - ratioInfo = document.body.querySelector(".extra-info") - + ratioInfo = document.body.querySelector(".extra-info"); } // Only display authors and owners if they appear @@ -4839,37 +5321,45 @@ function recomputeFilters() { const filterSets = {}; - Object.values(filterDefs).forEach(filter => { + Object.values(filterDefs).forEach((filter) => { filterSets[filter.id] = new Set(); }); - - document.querySelectorAll(".entity-select.category-visible > option").forEach(element => { - const entity = availableEntities[category][element.value]; - - Object.values(filterDefs).forEach(filter => { - filter.extract(entity).forEach(result => { - filterSets[filter.id].add(result); + + document + .querySelectorAll(".entity-select.category-visible > option") + .forEach((element) => { + const entity = availableEntities[category][element.value]; + + Object.values(filterDefs).forEach((filter) => { + filter.extract(entity).forEach((result) => { + filterSets[filter.id].add(result); + }); }); }); - }); - Object.values(filterDefs).forEach(filter => { + Object.values(filterDefs).forEach((filter) => { // always show the "none" option let found = filter.id == "none"; - document.querySelectorAll("#filter-" + filter.id + " > option").forEach(element => { - if (filterSets[filter.id].has(element.value) || filter.id == "none") { - element.classList.remove("filtered"); - element.disabled = false; - found = true; - } else { - element.classList.add("filtered"); - element.disabled = true; - } - }); - + document + .querySelectorAll("#filter-" + filter.id + " > option") + .forEach((element) => { + if ( + filterSets[filter.id].has(element.value) || + filter.id == "none" + ) { + element.classList.remove("filtered"); + element.disabled = false; + found = true; + } else { + element.classList.add("filtered"); + element.disabled = true; + } + }); - const filterOption = document.querySelector("#filter-picker > option[value='" + filter.id + "']"); + const filterOption = document.querySelector( + "#filter-picker > option[value='" + filter.id + "']" + ); if (found) { filterOption.classList.remove("filtered"); filterOption.disabled = false; @@ -4886,55 +5376,71 @@ function recomputeFilters() { function updateFilter() { const category = document.querySelector("#category-picker").value; const type = document.querySelector("#filter-picker").value; - const filterKeySelect = document.querySelector(".filter-select.category-visible"); + const filterKeySelect = document.querySelector( + ".filter-select.category-visible" + ); clearFilter(); const noFilter = !filterKeySelect; let key; - let current = document.querySelector(".entity-select.category-visible").value; + let current = document.querySelector( + ".entity-select.category-visible" + ).value; - if (!noFilter) - { + if (!noFilter) { key = filterKeySelect.value; - current + current; } - + let replace = current == ""; let first = null; let count = 0; const lowerSearchText = searchText !== "" ? searchText.toLowerCase() : null; - document.querySelectorAll(".entity-select.category-visible > option").forEach(element => { - let keep = noFilter; - - if (!noFilter && filterDefs[type].extract(availableEntities[category][element.value]).indexOf(key) >= 0) { - keep = true; - } + document + .querySelectorAll(".entity-select.category-visible > option") + .forEach((element) => { + let keep = noFilter; + + if ( + !noFilter && + filterDefs[type] + .extract(availableEntities[category][element.value]) + .indexOf(key) >= 0 + ) { + keep = true; + } - if (searchText != "" && !availableEntities[category][element.value].name.toLowerCase().includes(lowerSearchText)) - { - keep = false; - } + if ( + searchText != "" && + !availableEntities[category][element.value].name + .toLowerCase() + .includes(lowerSearchText) + ) { + keep = false; + } - if (!keep) { - element.classList.add("filtered"); - element.disabled = true; + if (!keep) { + element.classList.add("filtered"); + element.disabled = true; - if (current == element.value) { - replace = true; - } - } else { - count += 1; - if (!first) { - first = element.value; + if (current == element.value) { + replace = true; + } + } else { + count += 1; + if (!first) { + first = element.value; + } } - } - }); + }); - const button = document.querySelector(".filter-select.category-visible + button"); + const button = document.querySelector( + ".filter-select.category-visible + button" + ); if (button) { button.querySelector(".button-counter").innerText = count; @@ -4942,15 +5448,19 @@ function updateFilter() { if (replace) { document.querySelector(".entity-select.category-visible").value = first; - document.querySelector("#create-entity-" + category).dispatchEvent(new Event("change")); + document + .querySelector("#create-entity-" + category) + .dispatchEvent(new Event("change")); } } function clearFilter() { - document.querySelectorAll(".entity-select.category-visible > option").forEach(element => { - element.classList.remove("filtered"); - element.disabled = false; - }); + document + .querySelectorAll(".entity-select.category-visible > option") + .forEach((element) => { + element.classList.remove("filtered"); + element.disabled = false; + }); } function checkFitWorld() { @@ -4965,7 +5475,6 @@ function fitWorld(manual = false, factor = 1.1) { if (Object.keys(entities).length > 0) { fitEntities(entities, factor); } - } function fitEntities(targetEntities, manual = false, factor = 1.1) { @@ -4976,18 +5485,22 @@ function fitEntities(targetEntities, manual = false, factor = 1.1) { let count = 0; - const worldWidth = config.height.toNumber("meters") / canvasHeight * canvasWidth; + const worldWidth = + (config.height.toNumber("meters") / canvasHeight) * canvasWidth; const worldHeight = config.height.toNumber("meters"); - Object.entries(targetEntities).forEach(([key, entity]) => { const view = entity.view; let extra = entity.views[view].image.extra; extra = extra === undefined ? 1 : extra; - const image = document.querySelector("#entity-" + key + " > .entity-image"); - const x = parseFloat(document.querySelector("#entity-" + key).dataset.x); + const image = document.querySelector( + "#entity-" + key + " > .entity-image" + ); + const x = parseFloat( + document.querySelector("#entity-" + key).dataset.x + ); let width = image.width; let height = image.height; @@ -5000,10 +5513,16 @@ function fitEntities(targetEntities, manual = false, factor = 1.1) { width = height; } - const xBottom = x - entity.views[view].height.toNumber("meters") * width / height / 2; - const xTop = x + entity.views[view].height.toNumber("meters") * width / height / 2; + const xBottom = + x - + (entity.views[view].height.toNumber("meters") * width) / height / 2; + const xTop = + x + + (entity.views[view].height.toNumber("meters") * width) / height / 2; - const y = parseFloat(document.querySelector("#entity-" + key).dataset.y); + const y = parseFloat( + document.querySelector("#entity-" + key).dataset.y + ); const yBottom = y; const yTop = entity.views[view].height.toNumber("meters") + yBottom; @@ -5022,13 +5541,13 @@ function fitEntities(targetEntities, manual = false, factor = 1.1) { let xSize = (maxX - minX) * factor; if (xSize / ySize > worldWidth / worldHeight) { - ySize *= ((xSize / ySize) / (worldWidth / worldHeight)); + ySize *= xSize / ySize / (worldWidth / worldHeight); } config.x = (maxX + minX) / 2; config.y = minY; - height = math.unit(ySize, "meter") + height = math.unit(ySize, "meter"); setWorldHeight(config.height, math.multiply(height, factor)); } @@ -5037,18 +5556,23 @@ function updateWorldHeight() { const unit = document.querySelector("#options-height-unit").value; const rawValue = document.querySelector("#options-height-value").value; - var value + var value; try { - value = math.evaluate(rawValue) - if (typeof(value) !== "number") { + value = math.evaluate(rawValue); + if (typeof value !== "number") { try { - value = value.toNumber(unit) + value = value.toNumber(unit); } catch { - toast("Invalid input: " + rawValue + " can't be converted to " + unit) + toast( + "Invalid input: " + + rawValue + + " can't be converted to " + + unit + ); } } } catch { - toast("Invalid input: could not parse " + rawValue) + toast("Invalid input: could not parse " + rawValue); return; } @@ -5058,18 +5582,27 @@ function updateWorldHeight() { setWorldHeight(oldHeight, math.unit(newHeight, unit), true); } -function setWorldHeight(oldHeight, newHeight, keepUnit=false) { +function setWorldHeight(oldHeight, newHeight, keepUnit = false) { worldSizeDirty = true; - config.height = newHeight.to(document.querySelector("#options-height-unit").value) + config.height = newHeight.to( + document.querySelector("#options-height-unit").value + ); const unit = document.querySelector("#options-height-unit").value; - setNumericInput(document.querySelector("#options-height-value"), config.height.toNumber(unit)); + setNumericInput( + document.querySelector("#options-height-value"), + config.height.toNumber(unit) + ); Object.entries(entities).forEach(([key, entity]) => { const element = document.querySelector("#entity-" + key); let newPosition; if (altHeld) { - newPosition = adjustAbs({ x: element.dataset.x, y: element.dataset.y }, oldHeight, config.height); + newPosition = adjustAbs( + { x: element.dataset.x, y: element.dataset.y }, + oldHeight, + config.height + ); } else { newPosition = { x: element.dataset.x, y: element.dataset.y }; } @@ -5079,7 +5612,7 @@ function setWorldHeight(oldHeight, newHeight, keepUnit=false) { }); if (!keepUnit) { - pickUnit() + pickUnit(); } updateSizes(); @@ -5087,19 +5620,23 @@ function setWorldHeight(oldHeight, newHeight, keepUnit=false) { function loadScene(name = "default") { if (name === "") { - name = "default" + name = "default"; } try { - const data = JSON.parse(localStorage.getItem("macrovision-save-" + name)); + const data = JSON.parse( + localStorage.getItem("macrovision-save-" + name) + ); if (data === null) { - console.error("Couldn't load " + name) + console.error("Couldn't load " + name); return false; } importScene(data); toast("Loaded " + name); return true; } catch (err) { - alert("Something went wrong while loading (maybe you didn't have anything saved. Check the F12 console for the error.") + alert( + "Something went wrong while loading (maybe you didn't have anything saved. Check the F12 console for the error." + ); console.error(err); return false; } @@ -5111,7 +5648,9 @@ function saveScene(name = "default") { localStorage.setItem("macrovision-save-" + name, string); toast("Saved as " + name); } catch (err) { - alert("Something went wrong while saving (maybe I don't have localStorage permissions, or exporting failed). Check the F12 console for the error.") + alert( + "Something went wrong while saving (maybe I don't have localStorage permissions, or exporting failed). Check the F12 console for the error." + ); console.error(err); } } @@ -5119,7 +5658,7 @@ function saveScene(name = "default") { function deleteScene(name = "default") { if (confirm("Really delete the " + name + " scene?")) { try { - localStorage.removeItem("macrovision-save-" + name) + localStorage.removeItem("macrovision-save-" + name); toast("Deleted " + name); } catch (err) { console.error(err); @@ -5133,29 +5672,31 @@ function exportScene() { results.entities = []; - Object.entries(entities).filter(([key, entity]) => entity.ephemeral !== true).forEach(([key, entity]) => { - const element = document.querySelector("#entity-" + key); - results.entities.push({ - name: entity.identifier, - customName: entity.name, - scale: entity.scale, - rotation: entity.rotation, - view: entity.view, - form: entity.form, - x: element.dataset.x, - y: element.dataset.y, - priority: entity.priority, - brightness: entity.brightness + Object.entries(entities) + .filter(([key, entity]) => entity.ephemeral !== true) + .forEach(([key, entity]) => { + const element = document.querySelector("#entity-" + key); + results.entities.push({ + name: entity.identifier, + customName: entity.name, + scale: entity.scale, + rotation: entity.rotation, + view: entity.view, + form: entity.form, + x: element.dataset.x, + y: element.dataset.y, + priority: entity.priority, + brightness: entity.brightness, + }); }); - }); const unit = document.querySelector("#options-height-unit").value; results.world = { height: config.height.toNumber(unit), unit: unit, x: config.x, - y: config.y - } + y: config.y, + }; results.version = migrationDefs.length; @@ -5172,22 +5713,37 @@ function b64EncodeUnicode(str) { // first we use encodeURIComponent to get percent-encoded UTF-8, // then we convert the percent encodings into raw bytes which // can be fed into btoa. - return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, - function toSolidBytes(match, p1) { - return String.fromCharCode('0x' + p1); - })); + return btoa( + encodeURIComponent(str).replace( + /%([0-9A-F]{2})/g, + function toSolidBytes(match, p1) { + return String.fromCharCode("0x" + p1); + } + ) + ); } function b64DecodeUnicode(str) { // Going backwards: from bytestream, to percent-encoding, to original string. - return decodeURIComponent(atob(str).split('').map(function (c) { - return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); - }).join('')); + return decodeURIComponent( + atob(str) + .split("") + .map(function (c) { + return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); + }) + .join("") + ); } function linkScene() { loc = new URL(window.location); - const link = loc.protocol + "//" + loc.host + loc.pathname + "#" + b64EncodeUnicode(JSON.stringify(exportScene())); + const link = + loc.protocol + + "//" + + loc.host + + loc.pathname + + "#" + + b64EncodeUnicode(JSON.stringify(exportScene())); window.history.replaceState(null, "Macrovision", link); try { navigator.clipboard.writeText(link); @@ -5195,7 +5751,6 @@ function linkScene() { } catch { toast("Couldn't copy permalink"); } - } function copyScene() { @@ -5206,24 +5761,27 @@ function copyScene() { function pasteScene() { try { - navigator.clipboard.readText().then(text => { - const data = JSON.parse(text); - if (data.entities === undefined) { - return; - } - if (data.world === undefined) { - return; - } + navigator.clipboard + .readText() + .then((text) => { + const data = JSON.parse(text); + if (data.entities === undefined) { + return; + } + if (data.world === undefined) { + return; + } - importScene(data); - }).catch(err => alert(err)); + importScene(data); + }) + .catch((err) => alert(err)); } catch (err) { console.error(err); - // probably wasn't valid data + // probably wasn't valid data } } -// TODO - don't just search through every single entity +// TODO - don't just search through every single entity // probably just have a way to do lookups directly function findEntity(name) { @@ -5237,7 +5795,7 @@ const migrationDefs = [ Adds x and y coordinates for the camera */ - data => { + (data) => { data.world.x = 0; data.world.y = 0; }, @@ -5247,8 +5805,8 @@ const migrationDefs = [ Adds priority and brightness to each entity */ - data => { - data.entities.forEach(entity => { + (data) => { + data.entities.forEach((entity) => { entity.priority = 0; entity.brightness = 1; }); @@ -5259,9 +5817,9 @@ const migrationDefs = [ Custom names are exported */ - data => { - data.entities.forEach(entity => { - entity.customName = entity.name + (data) => { + data.entities.forEach((entity) => { + entity.customName = entity.name; }); }, /* @@ -5270,18 +5828,19 @@ const migrationDefs = [ Rotation is now stored */ - data => { - data.entities.forEach(entity => { - entity.rotation = 0 + (data) => { + data.entities.forEach((entity) => { + entity.rotation = 0; }); - } -] - + }, +]; function migrateScene(data) { if (data.version === undefined) { - alert("This save was created before save versions were tracked. The scene may import incorrectly."); - console.trace() + alert( + "This save was created before save versions were tracked. The scene may import incorrectly." + ); + console.trace(); data.version = 0; } else if (data.version < migrationDefs.length) { migrationDefs[data.version](data); @@ -5295,7 +5854,7 @@ function importScene(data) { migrateScene(data); - data.entities.forEach(entityInfo => { + data.entities.forEach((entityInfo) => { const entity = findEntity(entityInfo.name).constructor(); entity.name = entityInfo.customName; entity.scale = entityInfo.scale; @@ -5310,11 +5869,15 @@ function importScene(data) { config.x = data.world.x; config.y = data.world.y; - const height = math.unit(data.world.height, data.world.unit).toNumber(defaultUnits.length[config.units]); + const height = math + .unit(data.world.height, data.world.unit) + .toNumber(defaultUnits.length[config.units]); document.querySelector("#options-height-value").value = height; - document.querySelector("#options-height-unit").dataset.oldUnit = defaultUnits.length[config.units]; - document.querySelector("#options-height-unit").value = defaultUnits.length[config.units]; + document.querySelector("#options-height-unit").dataset.oldUnit = + defaultUnits.length[config.units]; + document.querySelector("#options-height-unit").value = + defaultUnits.length[config.units]; if (data.canvasWidth) { doHorizReposition(data.canvasWidth / canvasWidth); @@ -5326,40 +5889,43 @@ function importScene(data) { function renderToCanvas() { const ctx = document.querySelector("#display").getContext("2d"); - Object.entries(entities).sort((ent1, ent2) => { - z1 = document.querySelector("#entity-" + ent1[0]).style.zIndex; - z2 = document.querySelector("#entity-" + ent2[0]).style.zIndex; - return z1 - z2; - }).forEach(([id, entity]) => { - element = document.querySelector("#entity-" + id); - img = element.querySelector("img"); - - let x = parseFloat(element.dataset.x); - let y = parseFloat(element.dataset.y); - - let coords = pos2pix({x: x, y: y}); - - let offset = img.style.getPropertyValue("--offset"); - offset = parseFloat(offset.substring(0, offset.length-1)) - - let xSize = img.width; - let ySize = img.height; - x = coords.x - y = coords.y + ySize/2 + ySize * offset / 100; - const oldFilter = ctx.filter - const brightness = getComputedStyle(element).getPropertyValue("--brightness") - ctx.filter = `brightness(${brightness})`; - - ctx.save(); - ctx.resetTransform(); - ctx.scale(window.devicePixelRatio, window.devicePixelRatio) - ctx.translate(x, y); - ctx.rotate(entity.rotation); - ctx.drawImage(img, -xSize/2, -ySize/2, xSize, ySize); - ctx.restore(); - - ctx.filter = oldFilter - }); + Object.entries(entities) + .sort((ent1, ent2) => { + z1 = document.querySelector("#entity-" + ent1[0]).style.zIndex; + z2 = document.querySelector("#entity-" + ent2[0]).style.zIndex; + return z1 - z2; + }) + .forEach(([id, entity]) => { + element = document.querySelector("#entity-" + id); + img = element.querySelector("img"); + + let x = parseFloat(element.dataset.x); + let y = parseFloat(element.dataset.y); + + let coords = pos2pix({ x: x, y: y }); + + let offset = img.style.getPropertyValue("--offset"); + offset = parseFloat(offset.substring(0, offset.length - 1)); + + let xSize = img.width; + let ySize = img.height; + x = coords.x; + y = coords.y + ySize / 2 + (ySize * offset) / 100; + const oldFilter = ctx.filter; + const brightness = + getComputedStyle(element).getPropertyValue("--brightness"); + ctx.filter = `brightness(${brightness})`; + + ctx.save(); + ctx.resetTransform(); + ctx.scale(window.devicePixelRatio, window.devicePixelRatio); + ctx.translate(x, y); + ctx.rotate(entity.rotation); + ctx.drawImage(img, -xSize / 2, -ySize / 2, xSize, ySize); + ctx.restore(); + + ctx.filter = oldFilter; + }); ctx.save(); ctx.resetTransform(); ctx.drawImage(document.querySelector("#rulers"), 0, 0); @@ -5378,9 +5944,14 @@ function generateScreenshot(callback) { if (config.groundKind !== "none") { ctx.fillStyle = backgroundColors[config.groundKind]; - ctx.fillRect(0, pos2pix({x: 0, y: 0}).y, canvasWidth + 100, canvasHeight); + ctx.fillRect( + 0, + pos2pix({ x: 0, y: 0 }).y, + canvasWidth + 100, + canvasHeight + ); } - + renderToCanvas(); ctx.resetTransform(); @@ -5389,49 +5960,53 @@ function generateScreenshot(callback) { ctx.font = "normal normal lighter 16pt coda"; ctx.fillText("macrovision.crux.sexy", 10, 25); - - exportCanvas(blob => { + exportCanvas((blob) => { callback(blob); - }); + }); } function copyScreenshot() { if (window.ClipboardItem === undefined) { - alert("Sorry, this browser doesn't yet support writing images to the clipboard."); + alert( + "Sorry, this browser doesn't yet support writing images to the clipboard." + ); return; } - generateScreenshot(blob => { - navigator.clipboard.write([ - new ClipboardItem({ - "image/png": blob - }) - ]).then(e => - toast("Copied to clipboard!")) - .catch(e => { + generateScreenshot((blob) => { + navigator.clipboard + .write([ + new ClipboardItem({ + "image/png": blob, + }), + ]) + .then((e) => toast("Copied to clipboard!")) + .catch((e) => { console.error(e); - toast("Couldn't write to the clipboard. Make sure the screenshot completes before switching tabs. Also, currently busted in Safari :(") - }) + toast( + "Couldn't write to the clipboard. Make sure the screenshot completes before switching tabs. Also, currently busted in Safari :(" + ); + }); }); drawScales(false); } function saveScreenshot() { - generateScreenshot(blob => { + generateScreenshot((blob) => { const a = document.createElement("a"); a.href = URL.createObjectURL(blob); a.setAttribute("download", "macrovision.png"); a.click(); - }); + }); drawScales(false); } function openScreenshot() { - generateScreenshot(blob => { + generateScreenshot((blob) => { const a = document.createElement("a"); a.href = URL.createObjectURL(blob); a.setAttribute("target", "_blank"); a.click(); - }); + }); drawScales(false); } @@ -5446,52 +6021,84 @@ function toast(msg) { setTimeout(() => { document.body.removeChild(div); - }, 5000) + }, 5000); } function toastRateLimit(msg, key, delay) { if (!rateLimits[key]) { toast(msg); rateLimits[key] = setTimeout(() => { - delete rateLimits[key] + delete rateLimits[key]; }, delay); } } let lastTime = undefined; function pan(fromX, fromY, fromHeight, toX, toY, toHeight, duration) { - - Object.keys(entities).forEach(key => { + Object.keys(entities).forEach((key) => { document.querySelector("#entity-" + key).classList.add("no-transition"); }); config.x = fromX; config.y = fromY; - config.height = math.unit(fromHeight, "meters") + config.height = math.unit(fromHeight, "meters"); updateSizes(); lastTime = undefined; - requestAnimationFrame((timestamp) => panTo(toX, toY, toHeight, (toX - fromX) / duration, (toY - fromY) / duration, (toHeight - fromHeight) / duration, timestamp, duration)); + requestAnimationFrame((timestamp) => + panTo( + toX, + toY, + toHeight, + (toX - fromX) / duration, + (toY - fromY) / duration, + (toHeight - fromHeight) / duration, + timestamp, + duration + ) + ); } -function panTo(x, y, height, xSpeed, ySpeed, heightSpeed, timestamp, remaining) { +function panTo( + x, + y, + height, + xSpeed, + ySpeed, + heightSpeed, + timestamp, + remaining +) { if (lastTime === undefined) { lastTime = timestamp; } dt = timestamp - lastTime; remaining -= dt; if (remaining < 0) { - dt += remaining + dt += remaining; } let newX = config.x + xSpeed * dt; let newY = config.y + ySpeed * dt; let newHeight = config.height.toNumber("meters") + heightSpeed * dt; if (remaining > 0) { - requestAnimationFrame((timestamp) => panTo(x, y, height, xSpeed, ySpeed, heightSpeed, timestamp, remaining)) + requestAnimationFrame((timestamp) => + panTo( + x, + y, + height, + xSpeed, + ySpeed, + heightSpeed, + timestamp, + remaining + ) + ); } else { - Object.keys(entities).forEach(key => { - document.querySelector("#entity-" + key).classList.remove("no-transition"); + Object.keys(entities).forEach((key) => { + document + .querySelector("#entity-" + key) + .classList.remove("no-transition"); }); } config.x = newX; @@ -5502,13 +6109,13 @@ function panTo(x, y, height, xSpeed, ySpeed, heightSpeed, timestamp, remaining) function getVerticalOffset() { if (config.groundPos === "very-high") { - return config.height.toNumber("meters") / 12 * 5; + return (config.height.toNumber("meters") / 12) * 5; } else if (config.groundPos === "high") { - return config.height.toNumber("meters") / 12 * 4; + return (config.height.toNumber("meters") / 12) * 4; } else if (config.groundPos === "medium") { - return config.height.toNumber("meters") / 12 * 3; + return (config.height.toNumber("meters") / 12) * 3; } else if (config.groundPos === "low") { - return config.height.toNumber("meters") / 12 * 2; + return (config.height.toNumber("meters") / 12) * 2; } else if (config.groundPos === "very-low") { return config.height.toNumber("meters") / 12; } else { @@ -5521,11 +6128,11 @@ function moveGround(down) { if (down) { if (index < groundPosChoices.length - 1) { - config.groundPos = groundPosChoices[index + 1] + config.groundPos = groundPosChoices[index + 1]; } } else { if (index > 0) { - config.groundPos = groundPosChoices[index - 1] + config.groundPos = groundPosChoices[index - 1]; } } @@ -5534,8 +6141,8 @@ function moveGround(down) { } function updateScrollButtons() { - const up = document.querySelector("#scroll-up") - const down = document.querySelector("#scroll-down") + const up = document.querySelector("#scroll-up"); + const down = document.querySelector("#scroll-down"); up.disabled = false; down.disabled = false; @@ -5552,4 +6159,4 @@ function updateScrollButtons() { up.disabled = true; } } -} \ No newline at end of file +}