markers rework - editor functions

This commit is contained in:
Azgaar 2021-09-20 02:29:50 +03:00
parent def32b7f74
commit 95f9d990d9
8 changed files with 633 additions and 214 deletions

View file

@ -238,8 +238,7 @@ i.icon-lock {
} }
#routeEditor > *, #routeEditor > *,
#labelEditor div, #labelEditor div {
#markerEditor div {
display: inline-block; display: inline-block;
} }
@ -1525,7 +1524,8 @@ div.states > .coaIcon > use {
#stateNameEditor div.label, #stateNameEditor div.label,
#provinceNameEditor div.label, #provinceNameEditor div.label,
#regimentBody div.label { #regimentBody div.label,
#markerEditor div.label {
display: inline-block; display: inline-block;
width: 5.5em; width: 5.5em;
} }
@ -1974,10 +1974,6 @@ div.textual span,
dominant-baseline: central; dominant-baseline: central;
} }
#markerEditor > button {
vertical-align: top;
}
.highlighted { .highlighted {
outline-width: 2px; outline-width: 2px;
outline-style: dashed; outline-style: dashed;

View file

@ -2036,39 +2036,66 @@
</div> </div>
<div id="markerEditor" class="dialog" style="display: none"> <div id="markerEditor" class="dialog" style="display: none">
<div id="markerBody" style="padding-bottom: .3em">
<div data-tip="Marker type. Style changes will apply to all markers of the same type. Leave blank if the marker is unique">
<div class="label">Type:</div>
<input id="markerType" />
</div>
<button id="markerGroup" data-tip="Change marker type" class="icon-tags"></button> <div data-tip="Marker icon. Paste any Unicode symbol or select from the predefined list">
<div id="markerGroupSection" style="display: none"> <div class="label">Icon:</div>
<select id="markerSelectGroup" data-tip="Select type for the selected marker" style="width: 10em"></select> <input id="markerIcon" style="width:5em" />
<input id="markerInputGroup" data-tip="Create new type for selected marker" placeholder="type new name" style="display: none; width: 10em"/> <button id="markerIconSelect" style="padding: 0; width: 4.5em">select</button>
<i id="markerAddGroup" data-tip="Create new markers type" class="icon-plus pointer"></i> </div>
<i id="markerRemoveGroup" data-tip="Remove all markers of that type" class="icon-trash pointer"></i>
<div data-tip="Marker icon size in pixels">
<div class="label">Icon size:</div>
<input id="markerIconSize" type="number" min="2" max="20" step="0.5" />
</div>
<div data-tip="Marker icon shift (by X and by Y axis), percent. Set to 50 to position icon in center">
<div class="label">Icon shift:</div>
<input id="markerIconShiftX" type="number" min="0" max="100" step="1" style="width:5em" />
<input id="markerIconShiftY" type="number" min="0" max="100" step="1" style="width:5em" />
</div>
<div data-tip="Size of the marker">
<div class="label">Marker size:</div>
<input id="markerSize" type="number" min="2" max="500" />
</div>
<div data-tip="Marker pin shape">
<div class="label">Pin shape:</div>
<select id="markerPin" >
<option value="bubble">Bubble</option>
<option value="pin">Pin</option>
<option value="square">Square</option>
<option value="squarish">Squarish</option>
<option value="diamond">Diamond</option>
<option value="hex">Hex</option>
<option value="hexy">Hexy</option>
<option value="shieldy">Shieldy</option>
<option value="shield">Shield</option>
<option value="pentagon">Pentagon</option>
<option value="heptagon">Heptagon</option>
<option value="circle">Circle</option>
<option value="no">No</option>
</select>
</div>
<div data-tip="Pin fill and stroke colors">
<div class="label">Pin colors:</div>
<input id="markerFill" type="color" style="width:5em" />
<input id="markerStroke" type="color" style="width:5em" />
</div>
</div> </div>
<button id="markerIcon" data-tip="Change marker icon and edit positioning" class="icon-star"></button> <div id="markerBottom">
<div id="markerIconSection" style="display: none"> <button id="markerNotes" data-tip="Edit place legend (notes)" class="icon-edit"></button>
<i data-tip="Change marker icon size" class="icon-resize-full"></i> <button id="markerLock" class="icon-lock-open"></button>
<input id="markerIconSize" data-tip="Change marker icon size" type="range" min=5 max=30 step=.5 value=22 style="width:12em"><br> <button id="markerAdd" data-tip="Add additional marker of that type" class="icon-plus"></button>
<i data-tip="Marker Icon" class="icon-info"></i> <button id="markerRemove" data-tip="Remove the marker. Shortcut: Delete" class="icon-trash fastDelete"></button>
<button id="markerIconSelect" data-tip="Click to select icon"></button>
<i data-tip="Change marker horizontal shift" class="icon-resize-horizontal"></i>
<input id="markerIconShiftX" data-tip="Change icon horizontal shift" type="number" value=50>
<i data-tip="Change marker vertical shift" class="icon-resize-vertical"></i>
<input id="markerIconShiftY" data-tip="Change vertical shift" type="number" min=0 max=100 value=50>
</div> </div>
<button id="markerStyle" data-tip="Change marker size and colors" class="icon-brush"></button>
<div id="markerStyleSection" style="display: none">
<i data-tip="Change marker base (pin) style" class="icon-map-pin"></i>
<input id="markerSize" data-tip="Change marker size" type="range" min=.01 max=10 step=.1 value=1 style="width:12em">
<input id="markerBaseStroke" data-tip="Change pin stroke color" type="color" value="#ffffff">
<input id="markerBaseFill" data-tip="Change pin fill color" type="color" alue="#000000">
</div>
<button id="markerToggleBubble" data-tip="Toggle pin (bubble) display" class="icon-info-circled"></button>
<button id="markerLegendButton" data-tip="Edit place legend (free text notes)" class="icon-edit"></button>
<button id="markerAdd" data-tip="Add additional marker of that type" class="icon-plus"></button>
<button id="markerRemove" data-tip="Remove the marker. Shortcut: Delete" class="icon-trash fastDelete"></button>
</div> </div>
<div id="regimentEditor" class="dialog" style="display: none"> <div id="regimentEditor" class="dialog" style="display: none">

49
main.js
View file

@ -338,11 +338,39 @@ function findBurgForMFCG(params) {
// apply default biomes data // apply default biomes data
function applyDefaultBiomesSystem() { function applyDefaultBiomesSystem() {
const name = ["Marine", "Hot desert", "Cold desert", "Savanna", "Grassland", "Tropical seasonal forest", "Temperate deciduous forest", "Tropical rainforest", "Temperate rainforest", "Taiga", "Tundra", "Glacier", "Wetland"]; const name = [
"Marine",
"Hot desert",
"Cold desert",
"Savanna",
"Grassland",
"Tropical seasonal forest",
"Temperate deciduous forest",
"Tropical rainforest",
"Temperate rainforest",
"Taiga",
"Tundra",
"Glacier",
"Wetland"
];
const color = ["#466eab", "#fbe79f", "#b5b887", "#d2d082", "#c8d68f", "#b6d95d", "#29bc56", "#7dcb35", "#409c43", "#4b6b32", "#96784b", "#d5e7eb", "#0b9131"]; const color = ["#466eab", "#fbe79f", "#b5b887", "#d2d082", "#c8d68f", "#b6d95d", "#29bc56", "#7dcb35", "#409c43", "#4b6b32", "#96784b", "#d5e7eb", "#0b9131"];
const habitability = [0, 4, 10, 22, 30, 50, 100, 80, 90, 12, 4, 0, 12]; const habitability = [0, 4, 10, 22, 30, 50, 100, 80, 90, 12, 4, 0, 12];
const iconsDensity = [0, 3, 2, 120, 120, 120, 120, 150, 150, 100, 5, 0, 150]; const iconsDensity = [0, 3, 2, 120, 120, 120, 120, 150, 150, 100, 5, 0, 150];
const icons = [{}, {dune: 3, cactus: 6, deadTree: 1}, {dune: 9, deadTree: 1}, {acacia: 1, grass: 9}, {grass: 1}, {acacia: 8, palm: 1}, {deciduous: 1}, {acacia: 5, palm: 3, deciduous: 1, swamp: 1}, {deciduous: 6, swamp: 1}, {conifer: 1}, {grass: 1}, {}, {swamp: 1}]; const icons = [
{},
{dune: 3, cactus: 6, deadTree: 1},
{dune: 9, deadTree: 1},
{acacia: 1, grass: 9},
{grass: 1},
{acacia: 8, palm: 1},
{deciduous: 1},
{acacia: 5, palm: 3, deciduous: 1, swamp: 1},
{deciduous: 6, swamp: 1},
{conifer: 1},
{grass: 1},
{},
{swamp: 1}
];
const cost = [10, 200, 150, 60, 50, 70, 70, 80, 90, 200, 1000, 5000, 150]; // biome movement cost const cost = [10, 200, 150, 60, 50, 70, 70, 80, 90, 200, 1000, 5000, 150]; // biome movement cost
const biomesMartix = [ const biomesMartix = [
// hot ↔ cold [>19°C; <-4°C]; dry ↕ wet // hot ↔ cold [>19°C; <-4°C]; dry ↕ wet
@ -494,9 +522,7 @@ function invokeActiveZooming() {
+markers.attr("rescale") && +markers.attr("rescale") &&
pack.markers?.forEach(marker => { pack.markers?.forEach(marker => {
const {i, x, y, size = 30, hidden} = marker; const {i, x, y, size = 30, hidden} = marker;
if (hidden) return; const el = !hidden && document.getElementById(`marker${i}`);
const el = document.getElementById(`marker${i}`);
if (!el) return; if (!el) return;
const zoomedSize = Math.max(rn(size / 5 + 24 / scale, 2), 1); const zoomedSize = Math.max(rn(size / 5 + 24 / scale, 2), 1);
@ -1483,7 +1509,18 @@ function addZones(number = 1) {
}); });
} }
const invasion = rw({Invasion: 4, Occupation: 3, Raid: 2, Conquest: 2, Subjugation: 1, Foray: 1, Skirmishes: 1, Incursion: 2, Pillaging: 1, Intervention: 1}); const invasion = rw({
Invasion: 4,
Occupation: 3,
Raid: 2,
Conquest: 2,
Subjugation: 1,
Foray: 1,
Skirmishes: 1,
Incursion: 2,
Pillaging: 1,
Intervention: 1
});
const name = getAdjective(invader.name) + " " + invasion; const name = getAdjective(invader.name) + " " + invasion;
data.push({name, type: "Invasion", cells: cellsArray, fill: "url(#hatch1)"}); data.push({name, type: "Invasion", cells: cellsArray, fill: "url(#hatch1)"});
} }

View file

@ -10,6 +10,7 @@ window.Markers = (function () {
TIME && console.time("addMarkers"); TIME && console.time("addMarkers");
const culturesSet = document.getElementById("culturesSet").value; const culturesSet = document.getElementById("culturesSet").value;
// TODO: don't put multiple markers to the same cell
addVolcanoes(); addVolcanoes();
addHotSprings(); addHotSprings();
@ -120,7 +121,9 @@ window.Markers = (function () {
const {cells, burgs} = pack; const {cells, burgs} = pack;
const meanFlux = d3.mean(cells.fl.filter(fl => fl)); const meanFlux = d3.mean(cells.fl.filter(fl => fl));
let bridges = Array.from(cells.i.filter(i => cells.burg[i] && cells.t[i] !== 1 && burgs[cells.burg[i]].population > 20 && cells.r[i] && cells.fl[i] > meanFlux)); let bridges = Array.from(
cells.i.filter(i => cells.burg[i] && cells.t[i] !== 1 && burgs[cells.burg[i]].population > 20 && cells.r[i] && cells.fl[i] > meanFlux)
);
let quantity = getQuantity(bridges, 1, 5); let quantity = getQuantity(bridges, 1, 5);
if (!quantity) return; if (!quantity) return;
@ -143,13 +146,233 @@ window.Markers = (function () {
let quantity = getQuantity(taverns, 1, 100); let quantity = getQuantity(taverns, 1, 100);
if (!quantity) return; if (!quantity) return;
const colors = ["Dark", "Light", "Bright", "Golden", "White", "Black", "Red", "Pink", "Purple", "Blue", "Green", "Yellow", "Amber", "Orange", "Brown", "Grey"]; const colors = [
const animals = ["Antelope", "Ape", "Badger", "Bear", "Beaver", "Bison", "Boar", "Buffalo", "Cat", "Crane", "Crocodile", "Crow", "Deer", "Dog", "Eagle", "Elk", "Fox", "Goat", "Goose", "Hare", "Hawk", "Heron", "Horse", "Hyena", "Ibis", "Jackal", "Jaguar", "Lark", "Leopard", "Lion", "Mantis", "Marten", "Moose", "Mule", "Narwhal", "Owl", "Panther", "Rat", "Raven", "Rook", "Scorpion", "Shark", "Sheep", "Snake", "Spider", "Swan", "Tiger", "Turtle", "Wolf", "Wolverine", "Camel", "Falcon", "Hound", "Ox"]; "Dark",
const adjectives = ["New", "Good", "High", "Old", "Great", "Big", "Major", "Happy", "Main", "Huge", "Far", "Beautiful", "Fair", "Prime", "Ancient", "Golden", "Proud", "Lucky", "Fat", "Honest", "Giant", "Distant", "Friendly", "Loud", "Hungry", "Magical", "Superior", "Peaceful", "Frozen", "Divine", "Favorable", "Brave", "Sunny", "Flying"]; "Light",
const methods = ["Boiled", "Grilled", "Roasted", "Spit-roasted", "Stewed", "Stuffed", "Jugged", "Mashed", "Baked", "Braised", "Poached", "Marinated", "Pickled", "Smoked", "Dried", "Dry-aged", "Corned", "Fried", "Pan-fried", "Deep-fried", "Dressed", "Steamed", "Cured", "Syrupped", "Flame-Broiled"]; "Bright",
const courses = ["beef", "pork", "bacon", "chicken", "lamb", "chevon", "hare", "rabbit", "hart", "deer", "antlers", "bear", "buffalo", "badger", "beaver", "turkey", "pheasant", "duck", "goose", "teal", "quail", "pigeon", "seal", "carp", "bass", "pike", "catfish", "sturgeon", "escallop", "pie", "cake", "pottage", "pudding", "onions", "carrot", "potato", "beet", "garlic", "cabbage", "eggplant", "eggs", "broccoli", "zucchini", "pepper", "olives", "pumpkin", "spinach", "peas", "chickpea", "beans", "rice", "pasta", "bread", "apples", "peaches", "pears", "melon", "oranges", "mango", "tomatoes", "cheese", "corn", "rat tails", "pig ears"]; "Golden",
"White",
"Black",
"Red",
"Pink",
"Purple",
"Blue",
"Green",
"Yellow",
"Amber",
"Orange",
"Brown",
"Grey"
];
const animals = [
"Antelope",
"Ape",
"Badger",
"Bear",
"Beaver",
"Bison",
"Boar",
"Buffalo",
"Cat",
"Crane",
"Crocodile",
"Crow",
"Deer",
"Dog",
"Eagle",
"Elk",
"Fox",
"Goat",
"Goose",
"Hare",
"Hawk",
"Heron",
"Horse",
"Hyena",
"Ibis",
"Jackal",
"Jaguar",
"Lark",
"Leopard",
"Lion",
"Mantis",
"Marten",
"Moose",
"Mule",
"Narwhal",
"Owl",
"Panther",
"Rat",
"Raven",
"Rook",
"Scorpion",
"Shark",
"Sheep",
"Snake",
"Spider",
"Swan",
"Tiger",
"Turtle",
"Wolf",
"Wolverine",
"Camel",
"Falcon",
"Hound",
"Ox"
];
const adjectives = [
"New",
"Good",
"High",
"Old",
"Great",
"Big",
"Major",
"Happy",
"Main",
"Huge",
"Far",
"Beautiful",
"Fair",
"Prime",
"Ancient",
"Golden",
"Proud",
"Lucky",
"Fat",
"Honest",
"Giant",
"Distant",
"Friendly",
"Loud",
"Hungry",
"Magical",
"Superior",
"Peaceful",
"Frozen",
"Divine",
"Favorable",
"Brave",
"Sunny",
"Flying"
];
const methods = [
"Boiled",
"Grilled",
"Roasted",
"Spit-roasted",
"Stewed",
"Stuffed",
"Jugged",
"Mashed",
"Baked",
"Braised",
"Poached",
"Marinated",
"Pickled",
"Smoked",
"Dried",
"Dry-aged",
"Corned",
"Fried",
"Pan-fried",
"Deep-fried",
"Dressed",
"Steamed",
"Cured",
"Syrupped",
"Flame-Broiled"
];
const courses = [
"beef",
"pork",
"bacon",
"chicken",
"lamb",
"chevon",
"hare",
"rabbit",
"hart",
"deer",
"antlers",
"bear",
"buffalo",
"badger",
"beaver",
"turkey",
"pheasant",
"duck",
"goose",
"teal",
"quail",
"pigeon",
"seal",
"carp",
"bass",
"pike",
"catfish",
"sturgeon",
"escallop",
"pie",
"cake",
"pottage",
"pudding",
"onions",
"carrot",
"potato",
"beet",
"garlic",
"cabbage",
"eggplant",
"eggs",
"broccoli",
"zucchini",
"pepper",
"olives",
"pumpkin",
"spinach",
"peas",
"chickpea",
"beans",
"rice",
"pasta",
"bread",
"apples",
"peaches",
"pears",
"melon",
"oranges",
"mango",
"tomatoes",
"cheese",
"corn",
"rat tails",
"pig ears"
];
const types = ["hot", "cold", "fire", "ice", "smoky", "misty", "shiny", "sweet", "bitter", "salty", "sour", "sparkling", "smelly"]; const types = ["hot", "cold", "fire", "ice", "smoky", "misty", "shiny", "sweet", "bitter", "salty", "sour", "sparkling", "smelly"];
const drinks = ["wine", "brandy", "jinn", "whisky", "rom", "beer", "cider", "mead", "liquor", "spirit", "vodka", "tequila", "absinthe", "nectar", "milk", "kvass", "kumis", "tea", "water", "juice", "sap"]; const drinks = [
"wine",
"brandy",
"jinn",
"whisky",
"rom",
"beer",
"cider",
"mead",
"liquor",
"spirit",
"vodka",
"tequila",
"absinthe",
"nectar",
"milk",
"kvass",
"kumis",
"tea",
"water",
"juice",
"sap"
];
while (quantity) { while (quantity) {
const [cell] = extractAnyElement(taverns); const [cell] = extractAnyElement(taverns);
@ -282,7 +505,14 @@ window.Markers = (function () {
const subjects = ["Locals", "Old folks", "Old books", "Tipplers"]; const subjects = ["Locals", "Old folks", "Old books", "Tipplers"];
const species = ["Ogre", "Troll", "Cyclops", "Giant", "Monster", "Beast", "Dragon", "Undead", "Ghoul", "Vampire"]; const species = ["Ogre", "Troll", "Cyclops", "Giant", "Monster", "Beast", "Dragon", "Undead", "Ghoul", "Vampire"];
const modusOperandi = ["steals their cattle", "doesn't mind eating children", "doesn't mind of human flesh", "keeps the region at bay", "eats their kids", "abducts young women"]; const modusOperandi = [
"steals their cattle",
"doesn't mind eating children",
"doesn't mind of human flesh",
"keeps the region at bay",
"eats their kids",
"abducts young women"
];
while (quantity) { while (quantity) {
const [cell] = extractAnyElement(hills); const [cell] = extractAnyElement(hills);
@ -376,7 +606,38 @@ window.Markers = (function () {
let quantity = getQuantity(roads, 50, 100); let quantity = getQuantity(roads, 50, 100);
if (!quantity) return; if (!quantity) return;
const animals = ["Apes", "Badgers", "Bears", "Beavers", "Bisons", "Boars", "Cats", "Crows", "Dogs", "Foxes", "Hares", "Hawks", "Hyenas", "Jackals", "Jaguars", "Leopards", "Lions", "Owls", "Panthers", "Rats", "Ravens", "Rooks", "Scorpions", "Sharks", "Snakes", "Spiders", "Tigers", "Wolfs", "Wolverines", "Falcons"]; const animals = [
"Apes",
"Badgers",
"Bears",
"Beavers",
"Bisons",
"Boars",
"Cats",
"Crows",
"Dogs",
"Foxes",
"Hares",
"Hawks",
"Hyenas",
"Jackals",
"Jaguars",
"Leopards",
"Lions",
"Owls",
"Panthers",
"Rats",
"Ravens",
"Rooks",
"Scorpions",
"Sharks",
"Snakes",
"Spiders",
"Tigers",
"Wolfs",
"Wolverines",
"Falcons"
];
const types = {brigands: 4, bandits: 3, robbers: 1, highwaymen: 1}; const types = {brigands: 4, bandits: 3, robbers: 1, highwaymen: 1};
while (quantity) { while (quantity) {
@ -385,7 +646,18 @@ window.Markers = (function () {
const culture = cells.culture[cell]; const culture = cells.culture[cell];
const biome = cells.biome[cell]; const biome = cells.biome[cell];
const height = cells.p[cell]; const height = cells.p[cell];
const locality = height >= 70 ? "highlander" : [1, 2].includes(biome) ? "desert" : [3, 4].includes(biome) ? "mounted" : [5, 6, 7, 8, 9].includes(biome) ? "forest" : biome === 12 ? "swamp" : "angry"; const locality =
height >= 70
? "highlander"
: [1, 2].includes(biome)
? "desert"
: [3, 4].includes(biome)
? "mounted"
: [5, 6, 7, 8, 9].includes(biome)
? "forest"
: biome === 12
? "swamp"
: "angry";
const name = `${Names.getCulture(culture)} ${ra(animals)}`; const name = `${Names.getCulture(culture)} ${ra(animals)}`;
const legend = `A gang of ${locality} ${rw(types)}`; const legend = `A gang of ${locality} ${rw(types)}`;
notes.push({id, name, legend}); notes.push({id, name, legend});

View file

@ -29,7 +29,7 @@ function clicked() {
else if (grand.id === "burgIcons") editBurg(); else if (grand.id === "burgIcons") editBurg();
else if (parent.id === "ice") editIce(); else if (parent.id === "ice") editIce();
else if (parent.id === "terrain") editReliefIcon(); else if (parent.id === "terrain") editReliefIcon();
else if (grand.id === "markers") editMarker(); else if (grand.id === "markers" || great.id === "markers") editMarker();
else if (grand.id === "coastline") editCoastline(); else if (grand.id === "coastline") editCoastline();
else if (great.id === "armies") editRegiment(); else if (great.id === "armies") editRegiment();
else if (pack.cells.t[i] === 1) { else if (pack.cells.t[i] === 1) {
@ -332,7 +332,15 @@ function drawLegend(name, data) {
const width = bbox.width + colOffset * 2; const width = bbox.width + colOffset * 2;
const height = bbox.height + colOffset / 2 + vOffset; const height = bbox.height + colOffset / 2 + vOffset;
legend.insert("rect", ":first-child").attr("id", "legendBox").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", backClr).attr("fill-opacity", opacity); legend
.insert("rect", ":first-child")
.attr("id", "legendBox")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill", backClr)
.attr("fill-opacity", opacity);
fitLegendBox(); fitLegendBox();
} }
@ -385,7 +393,15 @@ function createPicker() {
const closePicker = () => contaiter.style("display", "none"); const closePicker = () => contaiter.style("display", "none");
const contaiter = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%"); const contaiter = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%");
contaiter.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("opacity", 0.2).on("mousemove", cl).on("click", closePicker); contaiter
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "100%")
.attr("height", "100%")
.attr("opacity", 0.2)
.on("mousemove", cl)
.on("click", closePicker);
const picker = contaiter const picker = contaiter
.append("g") .append("g")
.attr("id", "picker") .attr("id", "picker")
@ -484,9 +500,25 @@ function createPicker() {
const width = bbox.width + 8; const width = bbox.width + 8;
const height = bbox.height + 9; const height = bbox.height + 9;
picker.insert("rect", ":first-child").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "#ffffff").attr("stroke", "#5d4651").on("mousemove", pos); picker
.insert("rect", ":first-child")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill", "#ffffff")
.attr("stroke", "#5d4651")
.on("mousemove", pos);
picker.insert("text", ":first-child").attr("x", 291).attr("y", -10).attr("id", "pickerCloseText").text("✕"); picker.insert("text", ":first-child").attr("x", 291).attr("y", -10).attr("id", "pickerCloseText").text("✕");
picker.insert("rect", ":first-child").attr("x", 288).attr("y", -21).attr("id", "pickerCloseRect").attr("width", 14).attr("height", 14).on("mousemove", cl).on("click", closePicker); picker
.insert("rect", ":first-child")
.attr("x", 288)
.attr("y", -21)
.attr("id", "pickerCloseRect")
.attr("width", 14)
.attr("height", 14)
.on("mousemove", cl)
.on("click", closePicker);
picker.insert("text", ":first-child").attr("x", 12).attr("y", -10).attr("id", "pickerLabel").text("Color Picker").on("mousemove", pos); picker.insert("text", ":first-child").attr("x", 12).attr("y", -10).attr("id", "pickerLabel").text("Color Picker").on("mousemove", pos);
picker.insert("rect", ":first-child").attr("x", 0).attr("y", -30).attr("width", width).attr("height", 30).attr("id", "pickerHeader").on("mousemove", pos); picker.insert("rect", ":first-child").attr("x", 0).attr("y", -30).attr("width", width).attr("height", 30).attr("id", "pickerHeader").on("mousemove", pos);
picker.attr("transform", `translate(${(svgWidth - width) / 2},${(svgHeight - height) / 2})`); picker.attr("transform", `translate(${(svgWidth - width) / 2},${(svgHeight - height) / 2})`);
@ -705,7 +737,14 @@ function highlightElement(element) {
const highlight = debug.append("rect").attr("x", box.x).attr("y", box.y).attr("width", box.width).attr("height", box.height).attr("transform", transform); const highlight = debug.append("rect").attr("x", box.x).attr("y", box.y).attr("width", box.width).attr("height", box.height).attr("transform", transform);
highlight.classed("highlighted", 1).transition(enter).style("outline-offset", "0px").transition(exit).style("outline-color", "transparent").delay(1000).remove(); highlight
.classed("highlighted", 1)
.transition(enter)
.style("outline-offset", "0px")
.transition(exit)
.style("outline-color", "transparent")
.delay(1000)
.remove();
const tr = parseTransform(transform); const tr = parseTransform(transform);
let x = box.x + box.width / 2; let x = box.x + box.width / 2;
@ -922,6 +961,7 @@ function selectIcon(initial, callback) {
} }
} }
input.oninput = e => callback(input.value);
table.onclick = e => { table.onclick = e => {
if (e.target.tagName === "TD") { if (e.target.tagName === "TD") {
input.value = e.target.innerHTML; input.value = e.target.innerHTML;
@ -948,6 +988,31 @@ function selectIcon(initial, callback) {
}); });
} }
function confirmationDialog(options) {
const {
title = "Confirm action",
message = "Are you sure you want to continue? <br>The action cannot be reverted",
cancel = "Cancel",
confirm = "Continue",
onCancel,
onConfirm
} = options;
const buttons = {
[confirm]: function () {
if (onConfirm) onConfirm();
$(this).dialog("close");
},
[cancel]: function () {
if (onCancel) onCancel();
$(this).dialog("close");
}
};
document.getElementById("alertMessage").innerHTML = message;
$("#alert").dialog({resizable: false, title, buttons});
}
// add and register event listeners to clean up on editor closure // add and register event listeners to clean up on editor closure
function listen(element, event, handler) { function listen(element, event, handler) {
element.addEventListener(event, handler); element.addEventListener(event, handler);

View file

@ -196,7 +196,8 @@ function drawHeightmap() {
for (const i of d3.range(20, 101)) { for (const i of d3.range(20, 101)) {
if (paths[i].length < 10) continue; if (paths[i].length < 10) continue;
const color = getColor(i, scheme); const color = getColor(i, scheme);
if (terracing) terrs.append("path").attr("d", paths[i]).attr("transform", "translate(.7,1.4)").attr("fill", d3.color(color).darker(terracing)).attr("data-height", i); if (terracing)
terrs.append("path").attr("d", paths[i]).attr("transform", "translate(.7,1.4)").attr("fill", d3.color(color).darker(terracing)).attr("data-height", i);
terrs.append("path").attr("d", paths[i]).attr("fill", color).attr("data-height", i); terrs.append("path").attr("d", paths[i]).attr("fill", color).attr("data-height", i);
} }
@ -798,7 +799,10 @@ function drawReligions() {
if (!vArray[r]) vArray[r] = []; if (!vArray[r]) vArray[r] = [];
vArray[r].push(points); vArray[r].push(points);
body[r] += "M" + points.join("L"); body[r] += "M" + points.join("L");
gap[r] += "M" + vertices.p[chain[0][0]] + chain.reduce((r2, v, i, d) => (!i ? r2 : !v[2] ? r2 + "L" + vertices.p[v[0]] : d[i + 1] && !d[i + 1][2] ? r2 + "M" + vertices.p[v[0]] : r2), ""); gap[r] +=
"M" +
vertices.p[chain[0][0]] +
chain.reduce((r2, v, i, d) => (!i ? r2 : !v[2] ? r2 + "L" + vertices.p[v[0]] : d[i + 1] && !d[i + 1][2] ? r2 + "M" + vertices.p[v[0]] : r2), "");
} }
const bodyData = body.map((p, i) => [p.length > 10 ? p : null, i, religions[i].color]).filter(d => d[0]); const bodyData = body.map((p, i) => [p.length > 10 ? p : null, i, religions[i].color]).filter(d => d[0]);
@ -966,7 +970,12 @@ function drawStates() {
const gapString = gapData.map(d => `<path id="state-gap${d[1]}" d="${d[0]}" fill="none" stroke="${d[2]}"/>`).join(""); const gapString = gapData.map(d => `<path id="state-gap${d[1]}" d="${d[0]}" fill="none" stroke="${d[2]}"/>`).join("");
const clipString = bodyData.map(d => `<clipPath id="state-clip${d[1]}"><use href="#state${d[1]}"/></clipPath>`).join(""); const clipString = bodyData.map(d => `<clipPath id="state-clip${d[1]}"><use href="#state${d[1]}"/></clipPath>`).join("");
const haloString = haloData const haloString = haloData
.map(d => `<path id="state-border${d[1]}" d="${d[0]}" clip-path="url(#state-clip${d[1]})" stroke="${d3.color(d[2]) ? d3.color(d[2]).darker().hex() : "#666666"}"/>`) .map(
d =>
`<path id="state-border${d[1]}" d="${d[0]}" clip-path="url(#state-clip${d[1]})" stroke="${
d3.color(d[2]) ? d3.color(d[2]).darker().hex() : "#666666"
}"/>`
)
.join(""); .join("");
statesBody.html(bodyString + gapString); statesBody.html(bodyString + gapString);
@ -1219,7 +1228,10 @@ function getProvincesVertices() {
if (!vArray[p]) vArray[p] = []; if (!vArray[p]) vArray[p] = [];
vArray[p].push(points); vArray[p].push(points);
body[p] += "M" + points.join("L"); body[p] += "M" + points.join("L");
gap[p] += "M" + vertices.p[chain[0][0]] + chain.reduce((r, v, i, d) => (!i ? r : !v[2] ? r + "L" + vertices.p[v[0]] : d[i + 1] && !d[i + 1][2] ? r + "M" + vertices.p[v[0]] : r), ""); gap[p] +=
"M" +
vertices.p[chain[0][0]] +
chain.reduce((r, v, i, d) => (!i ? r : !v[2] ? r + "L" + vertices.p[v[0]] : d[i + 1] && !d[i + 1][2] ? r + "M" + vertices.p[v[0]] : r), "");
} }
// find province visual center // find province visual center
@ -1300,7 +1312,12 @@ function drawGrid() {
const maxWidth = Math.max(+mapWidthInput.value, graphWidth); const maxWidth = Math.max(+mapWidthInput.value, graphWidth);
const maxHeight = Math.max(+mapHeightInput.value, graphHeight); const maxHeight = Math.max(+mapHeightInput.value, graphHeight);
d3.select(pattern).attr("stroke", stroke).attr("stroke-width", width).attr("stroke-dasharray", dasharray).attr("stroke-linecap", linecap).attr("patternTransform", tr); d3.select(pattern)
.attr("stroke", stroke)
.attr("stroke-width", width)
.attr("stroke-dasharray", dasharray)
.attr("stroke-linecap", linecap)
.attr("patternTransform", tr);
gridOverlay gridOverlay
.append("rect") .append("rect")
.attr("width", maxWidth) .attr("width", maxWidth)
@ -1510,23 +1527,23 @@ function toggleMarkers(event) {
drawMarkers(); drawMarkers();
if (event && isCtrlClick(event)) editStyle("markers"); if (event && isCtrlClick(event)) editStyle("markers");
} else { } else {
if (event && isCtrlClick(event)) { if (event && isCtrlClick(event)) return editStyle("markers");
editStyle("markers");
return;
}
markers.selectAll("*").remove(); markers.selectAll("*").remove();
turnButtonOff("toggleMarkers"); turnButtonOff("toggleMarkers");
} }
} }
function drawMarkers() { function drawMarkers() {
const html = pack.markers.map(drawMarker); const rescale = +markers.attr("rescale");
const html = pack.markers.map(marker => drawMarker(marker, rescale));
markers.html(html.join("")); markers.html(html.join(""));
} }
const getPin = (shape = "bubble", fill = "#fff", stroke = "#000") => { const getPin = (shape = "bubble", fill = "#fff", stroke = "#000") => {
if (shape === "bubble") return `<path d="M6,19 l9,10 L24,19" fill="${stroke}" stroke="none" /><circle cx="15" cy="15" r="10" fill="${fill}" stroke="${stroke}"/>`; if (shape === "bubble")
if (shape === "pin") return `<path d="m 15,3 c -5.5,0 -9.7,4.09 -9.7,9.3 0,6.8 9.7,17 9.7,17 0,0 9.7,-10.2 9.7,-17 C 24.7,7.09 20.5,3 15,3 Z" fill="${fill}" stroke="${stroke}"/>`; return `<path d="M6,19 l9,10 L24,19" fill="${stroke}" stroke="none" /><circle cx="15" cy="15" r="10" fill="${fill}" stroke="${stroke}"/>`;
if (shape === "pin")
return `<path d="m 15,3 c -5.5,0 -9.7,4.09 -9.7,9.3 0,6.8 9.7,17 9.7,17 0,0 9.7,-10.2 9.7,-17 C 24.7,7.09 20.5,3 15,3 Z" fill="${fill}" stroke="${stroke}"/>`;
if (shape === "square") return `<path d="m 20,25 -5,4 -5,-4 z" fill="${stroke}"/><path d="M 5,5 H 25 V 25 H 5 Z" fill="${fill}" stroke="${stroke}"/>`; if (shape === "square") return `<path d="m 20,25 -5,4 -5,-4 z" fill="${stroke}"/><path d="M 5,5 H 25 V 25 H 5 Z" fill="${fill}" stroke="${stroke}"/>`;
if (shape === "squarish") return `<path d="m 5,5 h 20 v 20 h -6 l -4,4 -4,-4 H 5 Z" fill="${fill}" stroke="${stroke}" />`; if (shape === "squarish") return `<path d="m 5,5 h 20 v 20 h -6 l -4,4 -4,-4 H 5 Z" fill="${fill}" stroke="${stroke}" />`;
if (shape === "diamond") return `<path d="M 2,15 15,1 28,15 15,29 Z" fill="${fill}" stroke="${stroke}" />`; if (shape === "diamond") return `<path d="M 2,15 15,1 28,15 15,29 Z" fill="${fill}" stroke="${stroke}" />`;
@ -1540,14 +1557,14 @@ const getPin = (shape = "bubble", fill = "#fff", stroke = "#000") => {
if (shape === "no") return ""; if (shape === "no") return "";
}; };
function drawMarker(marker) { function drawMarker(marker, rescale = 1) {
const {i, icon, x, y, dx = 50, dy = 50, px = 12, size = 30} = marker; const {i, icon, x, y, dx = 50, dy = 50, px = 12, size = 30} = marker;
const id = `marker${i}`; const id = `marker${i}`;
const zoomSize = Math.max(rn(size / 5 + 24 / scale, 2), 1); const zoomSize = rescale ? Math.max(rn(size / 5 + 24 / scale, 2), 1) : 1;
const viewX = rn(x - zoomSize / 2, 1); const viewX = rn(x - zoomSize / 2, 1);
const viewY = rn(y - zoomSize, 1); const viewY = rn(y - zoomSize, 1);
return `<svg id="${id}" viewbox="0 0 30 30" width="${zoomSize}" height="${zoomSize}" x="${viewX}" y="${viewY}">${getPin()}<text x="${dx}%" y="${dy}%" font-size="${px}px" >${icon}</text></svg>`; return `<svg id="${id}" viewbox="0 0 30 30" width="${zoomSize}" height="${zoomSize}" x="${viewX}" y="${viewY}"><g>${getPin()}</g><text x="${dx}%" y="${dy}%" font-size="${px}px" >${icon}</text></svg>`;
} }
function toggleLabels(event) { function toggleLabels(event) {
@ -1717,15 +1734,21 @@ function drawEmblems() {
} }
const burgNodes = nodes.filter(node => node.type === "burg"); const burgNodes = nodes.filter(node => node.type === "burg");
const burgString = burgNodes.map(d => `<use data-i="${d.i}" x="${rn(d.x - d.shift)}" y="${rn(d.y - d.shift)}" width="${d.size}em" height="${d.size}em"/>`).join(""); const burgString = burgNodes
.map(d => `<use data-i="${d.i}" x="${rn(d.x - d.shift)}" y="${rn(d.y - d.shift)}" width="${d.size}em" height="${d.size}em"/>`)
.join("");
emblems.select("#burgEmblems").attr("font-size", sizeBurgs).html(burgString); emblems.select("#burgEmblems").attr("font-size", sizeBurgs).html(burgString);
const provinceNodes = nodes.filter(node => node.type === "province"); const provinceNodes = nodes.filter(node => node.type === "province");
const provinceString = provinceNodes.map(d => `<use data-i="${d.i}" x="${rn(d.x - d.shift)}" y="${rn(d.y - d.shift)}" width="${d.size}em" height="${d.size}em"/>`).join(""); const provinceString = provinceNodes
.map(d => `<use data-i="${d.i}" x="${rn(d.x - d.shift)}" y="${rn(d.y - d.shift)}" width="${d.size}em" height="${d.size}em"/>`)
.join("");
emblems.select("#provinceEmblems").attr("font-size", sizeProvinces).html(provinceString); emblems.select("#provinceEmblems").attr("font-size", sizeProvinces).html(provinceString);
const stateNodes = nodes.filter(node => node.type === "state"); const stateNodes = nodes.filter(node => node.type === "state");
const stateString = stateNodes.map(d => `<use data-i="${d.i}" x="${rn(d.x - d.shift)}" y="${rn(d.y - d.shift)}" width="${d.size}em" height="${d.size}em"/>`).join(""); const stateString = stateNodes
.map(d => `<use data-i="${d.i}" x="${rn(d.x - d.shift)}" y="${rn(d.y - d.shift)}" width="${d.size}em" height="${d.size}em"/>`)
.join("");
emblems.select("#stateEmblems").attr("font-size", sizeStates).html(stateString); emblems.select("#stateEmblems").attr("font-size", sizeStates).html(stateString);
invokeActiveZooming(); invokeActiveZooming();

View file

@ -3,23 +3,28 @@ function editMarker() {
if (customization) return; if (customization) return;
closeDialogs(".stable"); closeDialogs(".stable");
const element = d3.event.target.parentElement; const element = d3.event.target.closest("svg");
elSelected = d3.select(element).call(d3.drag().on("start", dragMarker)).classed("draggable", true); const marker = pack.markers.find(({i}) => Number(element.id.slice(6)) === i);
const marker = pack.markers.find(({i}) => Number(elSelected.attr("id").slice(6)) === i);
if (!marker) return; if (!marker) return;
elSelected = d3.select(element).raise().call(d3.drag().on("start", dragMarker)).classed("draggable", true);
// dom elements // dom elements
const markerSelectGroup = document.getElementById("markerSelectGroup"); const markerType = document.getElementById("markerType");
const markerIcon = document.getElementById("markerIcon");
const markerIconSelect = document.getElementById("markerIconSelect");
const markerIconSize = document.getElementById("markerIconSize"); const markerIconSize = document.getElementById("markerIconSize");
const markerIconShiftX = document.getElementById("markerIconShiftX"); const markerIconShiftX = document.getElementById("markerIconShiftX");
const markerIconShiftY = document.getElementById("markerIconShiftY"); const markerIconShiftY = document.getElementById("markerIconShiftY");
const markerSize = document.getElementById("markerSize"); const markerSize = document.getElementById("markerSize");
const markerBaseStroke = document.getElementById("markerBaseStroke"); const markerPin = document.getElementById("markerPin");
const markerBaseFill = document.getElementById("markerBaseFill"); const markerFill = document.getElementById("markerFill");
const markerStroke = document.getElementById("markerStroke");
const markerToggleBubble = document.getElementById("markerToggleBubble"); const markerNotes = document.getElementById("markerNotes");
const markerIconSelect = document.getElementById("markerIconSelect"); const addMarker = document.getElementById("addMarker");
const markerAdd = document.getElementById("markerAdd");
const markerRemove = document.getElementById("markerRemove");
updateInputs(); updateInputs();
@ -31,22 +36,27 @@ function editMarker() {
}); });
const listeners = [ const listeners = [
listen(markerSelectGroup, "change", changeGroup), listen(markerType, "change", changeMarkerType),
listen(document.getElementById("markerIcon"), "click", toggleIconSection), listen(markerIcon, "input", changeMarkerIcon),
listen(markerIconSelect, "click", selectMarkerIcon),
listen(markerIconSize, "input", changeIconSize), listen(markerIconSize, "input", changeIconSize),
listen(markerIconShiftX, "input", changeIconShiftX), listen(markerIconShiftX, "input", changeIconShiftX),
listen(markerIconShiftY, "input", changeIconShiftY), listen(markerIconShiftY, "input", changeIconShiftY),
listen(document.getElementById("markerIconSelect"), "click", selectMarkerIcon),
listen(document.getElementById("markerStyle"), "click", toggleStyleSection),
listen(markerSize, "input", changeMarkerSize), listen(markerSize, "input", changeMarkerSize),
listen(markerBaseStroke, "input", changePinStroke), listen(markerPin, "change", changeMarkerPin),
listen(markerBaseFill, "input", changePinFill), listen(markerFill, "input", changePinFill),
listen(markerToggleBubble, "click", togglePinVisibility), listen(markerStroke, "input", changePinStroke),
listen(document.getElementById("markerLegendButton"), "click", editMarkerLegend), listen(markerNotes, "click", editMarkerLegend),
listen(document.getElementById("markerAdd"), "click", toggleAddMarker), listen(markerAdd, "click", toggleAddMarker),
listen(document.getElementById("markerRemove"), "click", removeMarker) listen(markerRemove, "click", confirmMarkerDeletion)
]; ];
function getSameTypeMarkers() {
const currentType = marker.type;
if (!currentType) return [marker];
return pack.markers.filter(({type}) => type === currentType);
}
function dragMarker() { function dragMarker() {
const dx = +this.getAttribute("x") - d3.event.x; const dx = +this.getAttribute("x") - d3.event.x;
const dy = +this.getAttribute("y") - d3.event.y; const dy = +this.getAttribute("y") - d3.event.y;
@ -70,173 +80,147 @@ function editMarker() {
} }
function updateInputs() { function updateInputs() {
const {icon, type = "", size = 30, dx = 50, dy = 50, px = 12, stroke = "#000", fill = "#fff", pin = "bubble"} = marker; const {icon, type = "", size = 30, dx = 50, dy = 50, px = 12, stroke = "#000000", fill = "#ffffff", pin = "bubble"} = marker;
markerSelectGroup.value = type; markerType.value = type;
markerIcon.value = icon;
markerIconSize.value = px; markerIconSize.value = px;
markerIconShiftX.value = dx; markerIconShiftX.value = dx;
markerIconShiftY.value = dy; markerIconShiftY.value = dy;
markerSize.value = size; markerSize.value = size;
markerBaseStroke.value = stroke; markerPin.value = pin;
markerBaseFill.value = fill; markerFill.value = fill;
markerStroke.value = stroke;
markerToggleBubble.className = pin;
markerIconSelect.innerHTML = icon;
} }
function toggleGroupSection() { function changeMarkerType() {
if (markerGroupSection.style.display === "inline-block") { marker.type = this.value;
markerEditor.querySelectorAll("button:not(#markerGroup)").forEach(b => (b.style.display = "inline-block"));
markerGroupSection.style.display = "none";
} else {
markerEditor.querySelectorAll("button:not(#markerGroup)").forEach(b => (b.style.display = "none"));
markerGroupSection.style.display = "inline-block";
}
} }
function changeGroup() { function changeMarkerIcon() {
elSelected.attr("xlink:href", "#" + this.value); const icon = this.value;
elSelected.attr("data-id", "#" + this.value); getSameTypeMarkers().forEach(marker => {
} marker.icon = icon;
redrawIcon(marker);
function toggleIconSection() { });
console.log(marker);
if (markerIconSection.style.display === "inline-block") {
markerEditor.querySelectorAll("button:not(#markerIcon)").forEach(b => (b.style.display = "inline-block"));
markerIconSection.style.display = "none";
markerIconSelect.style.display = "none";
} else {
markerEditor.querySelectorAll("button:not(#markerIcon)").forEach(b => (b.style.display = "none"));
markerIconSection.style.display = "inline-block";
markerIconSelect.style.display = "inline-block";
}
} }
function selectMarkerIcon() { function selectMarkerIcon() {
selectIcon(this.innerHTML, v => { selectIcon(marker.icon, icon => {
this.innerHTML = v; markerIcon.value = icon;
const id = elSelected.attr("data-id"); getSameTypeMarkers().forEach(marker => {
d3.select("#defs-markers").select(id).select("text").text(v); marker.icon = icon;
redrawIcon(marker);
});
}); });
} }
function changeIconSize() { function changeIconSize() {
const id = elSelected.attr("data-id"); const px = +this.value;
d3.select("#defs-markers") getSameTypeMarkers().forEach(marker => {
.select(id) marker.px = px;
.select("text") redrawIcon(marker);
.attr("font-size", this.value + "px"); });
} }
function changeIconShiftX() { function changeIconShiftX() {
const id = elSelected.attr("data-id"); const dx = +this.value;
d3.select("#defs-markers") getSameTypeMarkers().forEach(marker => {
.select(id) marker.dx = dx;
.select("text") redrawIcon(marker);
.attr("x", this.value + "%"); });
} }
function changeIconShiftY() { function changeIconShiftY() {
const id = elSelected.attr("data-id"); const dy = +this.value;
d3.select("#defs-markers") getSameTypeMarkers().forEach(marker => {
.select(id) marker.dy = dy;
.select("text") redrawIcon(marker);
.attr("y", this.value + "%"); });
}
function toggleStyleSection() {
if (markerStyleSection.style.display === "inline-block") {
markerEditor.querySelectorAll("button:not(#markerStyle)").forEach(b => (b.style.display = "inline-block"));
markerStyleSection.style.display = "none";
} else {
markerEditor.querySelectorAll("button:not(#markerStyle)").forEach(b => (b.style.display = "none"));
markerStyleSection.style.display = "inline-block";
}
} }
function changeMarkerSize() { function changeMarkerSize() {
const id = elSelected.attr("data-id"); const size = +this.value;
document.querySelectorAll("use[data-id='" + id + "']").forEach(e => { const rescale = +markers.attr("rescale");
const x = +e.dataset.x,
y = +e.dataset.y;
const desired = (e.dataset.size = +markerSize.value);
const size = Math.max(desired * 5 + 25 / scale, 1);
e.setAttribute("x", x - size / 2); getSameTypeMarkers().forEach(marker => {
e.setAttribute("y", y - size / 2); marker.size = size;
e.setAttribute("width", size); const {i, x, y, hidden} = marker;
e.setAttribute("height", size); const el = !hidden && document.getElementById(`marker${i}`);
if (!el) return;
const zoomedSize = rescale ? Math.max(rn(size / 5 + 24 / scale, 2), 1) : size;
el.setAttribute("width", zoomedSize);
el.setAttribute("height", zoomedSize);
el.setAttribute("x", rn(x - zoomedSize / 2, 1));
el.setAttribute("y", rn(y - zoomedSize, 1));
}); });
invokeActiveZooming();
} }
function changePinStroke() { function changeMarkerPin() {
const id = elSelected.attr("data-id"); const pin = this.value;
d3.select(id).select("path").attr("fill", this.value); getSameTypeMarkers().forEach(marker => {
d3.select(id).select("circle").attr("stroke", this.value); marker.pin = pin;
redrawPin(marker);
});
} }
function changePinFill() { function changePinFill() {
const id = elSelected.attr("data-id"); const fill = this.value;
d3.select(id).select("circle").attr("fill", this.value); getSameTypeMarkers().forEach(marker => {
marker.fill = fill;
redrawPin(marker);
});
} }
function changeIconStrokeWidth() { function changePinStroke() {
const id = elSelected.attr("data-id"); const stroke = this.value;
d3.select("#defs-markers").select(id).select("text").attr("stroke-width", this.value); getSameTypeMarkers().forEach(marker => {
marker.stroke = stroke;
redrawPin(marker);
});
} }
function changeIconStroke() { function redrawIcon({i, hidden, icon, dx = 50, dy = 50, px = 12}) {
const id = elSelected.attr("data-id"); const iconElement = !hidden && document.querySelector(`#marker${i} > text`);
d3.select("#defs-markers").select(id).select("text").attr("stroke", this.value); if (iconElement) {
iconElement.innerHTML = icon;
iconElement.setAttribute("x", dx + "%");
iconElement.setAttribute("y", dy + "%");
iconElement.setAttribute("font-size", px + "px");
}
} }
function changeIconFill() { function redrawPin({i, hidden, pin = "bubble", fill = "#fff", stroke = "#000"}) {
const id = elSelected.attr("data-id"); const pinGroup = !hidden && document.querySelector(`#marker${i} > g`);
d3.select("#defs-markers").select(id).select("text").attr("fill", this.value); if (pinGroup) pinGroup.innerHTML = getPin(pin, fill, stroke);
}
function togglePinVisibility() {
const id = elSelected.attr("data-id");
let show = 1;
if (this.className === "icon-info-circled") {
this.className = "icon-info";
show = 0;
} else this.className = "icon-info-circled";
d3.select(id).select("circle").attr("opacity", show);
d3.select(id).select("path").attr("opacity", show);
} }
function editMarkerLegend() { function editMarkerLegend() {
const id = elSelected.attr("id"); const id = element.id;
editNotes(id, id); editNotes(id, id);
} }
function toggleAddMarker() { function toggleAddMarker() {
document.getElementById("addMarker").click(); addMarker.click();
} }
function removeMarker() { function confirmMarkerDeletion() {
alertMessage.innerHTML = "Are you sure you want to remove the marker?"; confirmationDialog({
$("#alert").dialog({
resizable: false,
title: "Remove marker", title: "Remove marker",
buttons: { message: "Are you sure you want to remove this marker? The action cannot be reverted",
Remove: function () { confirm: "Remove",
$(this).dialog("close"); onConfirm: deleteMarker
const index = notes.findIndex(n => n.id === elSelected.attr("id"));
if (index != -1) notes.splice(index, 1);
elSelected.remove();
$("#markerEditor").dialog("close");
},
Cancel: function () {
$(this).dialog("close");
}
}
}); });
} }
function deleteMarker() {
notes = notes.filter(note => note.id !== element.id);
pack.markers = pack.markers.filter(m => m.i !== marker.i);
element.remove();
$("#markerEditor").dialog("close");
}
function closeMarkerEditor() { function closeMarkerEditor() {
listeners.forEach(removeListener => removeListener()); listeners.forEach(removeListener => removeListener());

View file

@ -49,7 +49,9 @@ toolsContent.addEventListener("click", function (event) {
}, },
open: function () { open: function () {
const pane = $(this).dialog("widget").find(".ui-dialog-buttonpane"); const pane = $(this).dialog("widget").find(".ui-dialog-buttonpane");
$('<span><input id="dontAsk" class="checkbox" type="checkbox"><label for="dontAsk" class="checkbox-label dontAsk"><i>do not ask again</i></label><span>').prependTo(pane); $(
'<span><input id="dontAsk" class="checkbox" type="checkbox"><label for="dontAsk" class="checkbox-label dontAsk"><i>do not ask again</i></label><span>'
).prependTo(pane);
}, },
close: function () { close: function () {
const box = $(this).dialog("widget").find(".checkbox")[0]; const box = $(this).dialog("widget").find(".checkbox")[0];
@ -262,7 +264,8 @@ function regenerateBurgs() {
const score = new Int16Array(cells.s.map(s => s * Math.random())); // cell score for capitals placement const score = new Int16Array(cells.s.map(s => s * Math.random())); // cell score for capitals placement
const sorted = cells.i.filter(i => score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes const sorted = cells.i.filter(i => score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes
const burgsCount = manorsInput.value == 1000 ? rn(sorted.length / 5 / (grid.points.length / 10000) ** 0.8) + states.length : +manorsInput.value + states.length; const burgsCount =
manorsInput.value == 1000 ? rn(sorted.length / 5 / (grid.points.length / 10000) ** 0.8) + states.length : +manorsInput.value + states.length;
const spacing = (graphWidth + graphHeight) / 150 / (burgsCount ** 0.7 / 66); // base min distance between towns const spacing = (graphWidth + graphHeight) / 150 / (burgsCount ** 0.7 / 66); // base min distance between towns
//clear locked list since ids will change //clear locked list since ids will change
@ -414,6 +417,7 @@ function regenerateIce() {
} }
function regenerateMarkers(event) { function regenerateMarkers(event) {
// TODO: rework for new markers system
if (isCtrlClick(event)) prompt("Please provide markers number multiplier", {default: 1, step: 0.01, min: 0, max: 100}, v => addNumberOfMarkers(v)); if (isCtrlClick(event)) prompt("Please provide markers number multiplier", {default: 1, step: 0.01, min: 0, max: 100}, v => addNumberOfMarkers(v));
else addNumberOfMarkers(gauss(1, 0.5, 0.3, 5, 2)); else addNumberOfMarkers(gauss(1, 0.5, 0.3, 5, 2));
@ -475,7 +479,18 @@ function addLabelOnClick() {
const id = getNextId("label"); const id = getNextId("label");
let group = labels.select("#addedLabels"); let group = labels.select("#addedLabels");
if (!group.size()) group = labels.append("g").attr("id", "addedLabels").attr("fill", "#3e3e4b").attr("opacity", 1).attr("stroke", "#3a3a3a").attr("stroke-width", 0).attr("font-family", "Almendra SC").attr("font-size", 18).attr("data-size", 18).attr("filter", null); if (!group.size())
group = labels
.append("g")
.attr("id", "addedLabels")
.attr("fill", "#3e3e4b")
.attr("opacity", 1)
.attr("stroke", "#3a3a3a")
.attr("stroke-width", 0)
.attr("font-family", "Almendra SC")
.attr("font-size", 18)
.attr("data-size", 18)
.attr("filter", null);
const example = group.append("text").attr("x", 0).attr("x", 0).text(name); const example = group.append("text").attr("x", 0).attr("x", 0).text(name);
const width = example.node().getBBox().width; const width = example.node().getBBox().width;