diff --git a/index.css b/index.css index 4a7a1dd0..c1985428 100644 --- a/index.css +++ b/index.css @@ -1289,8 +1289,13 @@ div.states > div.biomeArea { cursor: pointer; } +#picker text { + cursor: default; +} + #pickerHeader { fill: #916e7f; + stroke: #5d4651; cursor: move; } @@ -1302,8 +1307,17 @@ div.states > div.biomeArea { cursor: move !important; } -#picker text { - cursor: default; +#pickerCloseRect { + cursor: pointer; + fill: #916e7f; + stroke: #f8ffff; +} + +#pickerCloseText { + fill: #f8ffff; + font-size: 10px; + font-family: Arial, Helvetica, sans-serif; + pointer-events: none; } #pickerControls line { @@ -1322,6 +1336,20 @@ div.states > div.biomeArea { stroke: #000000; } +#pickerSpaces input { + height: 8px; + width: 16px; + font-size: smaller; + text-align: center; + -moz-appearance: textfield; +} + +#pickerSpaces input::-webkit-inner-spin-button, +#pickerSpaces input::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} + #pickerColors rect, #pickerHatches rect { cursor: pointer; } diff --git a/index.html b/index.html index 0eaea9dc..7d32395e 100644 --- a/index.html +++ b/index.html @@ -1788,9 +1788,9 @@

Cell info:

- Coord: / - Cell: - Height: + Coord: /
+ Cell:
+ Height:
@@ -2369,7 +2369,7 @@ - + diff --git a/main.js b/main.js index dbad159c..945f45be 100644 --- a/main.js +++ b/main.js @@ -391,7 +391,7 @@ function focusOn() { } const b = +params.get("burg"); - if (b) { + if (b && pack.burgs[b]) { x = pack.burgs[b].x; y = pack.burgs[b].y; } diff --git a/modules/ui/editors.js b/modules/ui/editors.js index bf5037fb..4f544f4b 100644 --- a/modules/ui/editors.js +++ b/modules/ui/editors.js @@ -252,9 +252,13 @@ function clearLegend() { // draw color (fill) picker function createPicker() { + const pos = () => tip("Drag to change the picker position"); + const cl = () => tip("Click to close the picker"); + const closePicker = () => contaiter.style("display", "none"); + const contaiter = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%"); - const curtain = contaiter.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("opacity", .2); - curtain.on("click", () => contaiter.style("display", "none")).on("mousemove", () => tip("Click to close the picker")); + contaiter.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("opacity", .2) + .on("mousemove", cl).on("click", closePicker); const picker = contaiter.append("g").attr("id", "picker").call(d3.drag().on("start", dragPicker)); const controls = picker.append("g").attr("id", "pickerControls"); @@ -279,6 +283,24 @@ function createPicker() { controls.selectAll("line").on("click", clickPickerControl); controls.selectAll("circle").call(d3.drag().on("start", dragPickerControl)); + const spaces = picker.append("foreignObject").attr("id", "pickerSpaces") + .attr("x", 4).attr("y", 20).attr("width", 303).attr("height", 20) + .on("mousemove", () => tip("Color value in different color spaces. Edit to change")); + const html = ` + + + `; + spaces.node().insertAdjacentHTML('beforeend', html); + spaces.selectAll("input").on("change", changePickerSpace); + const colors = picker.append("g").attr("id", "pickerColors").attr("stroke", "#333333"); const hatches = picker.append("g").attr("id", "pickerHatches").attr("stroke", "#333333"); const hatching = d3.selectAll("g#hatching > pattern"); @@ -287,12 +309,12 @@ function createPicker() { const clr = d3.range(number).map(i => d3.hsl(i/number*360, .7, .7).hex()); clr.forEach(function(d, i) { colors.append("rect").attr("id", "picker_" + d).attr("fill", d).attr("class", i?"":"selected") - .attr("x", i*22+4).attr("y", 20).attr("width", 16).attr("height", 16); + .attr("x", i*22+4).attr("y", 40).attr("width", 16).attr("height", 16); }); hatching.each(function(d, i) { hatches.append("rect").attr("id", "picker_" + this.id).attr("fill", "url(#" + this.id + ")") - .attr("x", i*22+4).attr("y", 41).attr("width", 16).attr("height", 16); + .attr("x", i*22+4).attr("y", 61).attr("width", 16).attr("height", 16); }); colors.selectAll("rect").on("click", pickerFillClicked).on("mousemove", () => tip("Click to fill with the color")); @@ -302,8 +324,10 @@ function createPicker() { const bbox = picker.node().getBBox(); const width = bbox.width + 8; const height = bbox.height + 9; - const pos = () => tip("Drag to change the picker position"); - picker.insert("rect", ":first-child").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "#ffffff").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", -11).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("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.attr("transform", `translate(${(svgWidth-width)/2},${(svgHeight-height)/2})`); @@ -314,6 +338,25 @@ function updateSelectedRect(fill) { document.getElementById("picker").querySelector("rect[fill='"+fill+"']").classList.add("selected"); } +function updateSpaces() { + // hsl + const h = getPickerControl(pickerH, 360); + const s = getPickerControl(pickerS, 1); + const l = getPickerControl(pickerL, 1); + pickerHSL_H.value = rn(h); + pickerHSL_S.value = rn(s * 100); // multiplied by 100 + pickerHSL_L.value = rn(l * 100); // multiplied by 100 + + // rgb + const rgb = d3.color(d3.hsl(h, s, l)); + pickerRGB_R.value = rgb.r; + pickerRGB_G.value = rgb.g; + pickerRGB_B.value = rgb.b; + + // hex + pickerHEX.value = rgb.hex(); +} + function updatePickerColors() { const colors = d3.select("#picker > #pickerColors").selectAll("rect"); const number = colors.size(); @@ -339,6 +382,7 @@ function openPicker(fill, callback) { if (!isNaN(hsl.h)) setPickerControl(pickerH, hsl.h, 360); if (!isNaN(hsl.s)) setPickerControl(pickerS, hsl.s, 1); if (!isNaN(hsl.l)) setPickerControl(pickerL, hsl.l, 1); + updateSpaces(); updatePickerColors(); } @@ -380,13 +424,20 @@ function dragPicker() { } function pickerFillClicked() { - updateSelectedRect(this.getAttribute("fill")); + const fill = this.getAttribute("fill"); + updateSelectedRect(fill); openPicker.updateFill(); + + const hsl = d3.hsl(fill); + if (isNaN(hsl.h)) return; // not a color + setPickerControl(pickerH, hsl.h, 360); + updateSpaces(); } function clickPickerControl() { const min = this.getScreenCTM().e; this.nextSibling.setAttribute("cx", d3.event.x - min); + updateSpaces(); updatePickerColors(); openPicker.updateFill(); } @@ -398,11 +449,33 @@ function dragPickerControl() { d3.event.on("drag", function() { const x = Math.max(Math.min(d3.event.x, max), min); this.setAttribute("cx", x); + updateSpaces(); updatePickerColors(); openPicker.updateFill(); }); } +function changePickerSpace() { + const valid = this.checkValidity(); + if (!valid) {tip("You must provide a correct value", false, "error"); return;} + + const space = this.dataset.space; + const i = Array.from(this.parentNode.querySelectorAll("input")).map(input => input.value); // inputs + const fill = space === "hex" ? d3.rgb(this.value) + : space === "rgb" ? d3.rgb(i[0], i[1], i[2]) + : d3.hsl(i[0], i[1]/100, i[2]/100); + + const hsl = d3.hsl(fill); + if (isNaN(hsl.l)) {tip("You must provide a correct value", false, "error"); return;} + if (!isNaN(hsl.h)) setPickerControl(pickerH, hsl.h, 360); + if (!isNaN(hsl.s)) setPickerControl(pickerS, hsl.s, 1); + if (!isNaN(hsl.l)) setPickerControl(pickerL, hsl.l, 1); + + updateSpaces(); + updatePickerColors(); + openPicker.updateFill(); +} + // remove all fogging function unfog() { defs.select("#fog").selectAll("path").remove(); diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js index c8f3035a..2738c790 100644 --- a/modules/ui/heightmap-editor.js +++ b/modules/ui/heightmap-editor.js @@ -82,9 +82,9 @@ function editHeightmap() { heightmapInfoX.innerHTML = rn(p[0]); heightmapInfoY.innerHTML = rn(p[1]); heightmapInfoCell.innerHTML = cell; - heightmapInfoHeight.innerHTML = grid.cells.h[cell]; - - tip("Height: " + getFriendlyHeight(grid.cells.h[cell])); + const h = grid.cells.h[cell]; + heightmapInfoHeight.innerHTML = `${h} (${getFriendlyHeight(h)})`; + if (tooltip.dataset.main) showMainTip(); // move radius circle if drag mode is active const pressed = document.querySelector("#brushesButtons > button.pressed"); @@ -218,6 +218,14 @@ function editHeightmap() { if (grid.cells.h[i] < 20) grid.cells.h[i] = 20; } + // save culture centers x and y to restore center cell id after re-graph + for (const c of pack.cultures) { + if (!c.i || c.removed) continue; + const p = pack.cells.p[c.center]; + c.x = p[0]; + c.y = p[1]; + } + markFeatures(); OceanLayers(); calculateTemperatures(); @@ -299,6 +307,11 @@ function editHeightmap() { else {p.center = provCells[0]; p.burg = pack.cells.burg[p.center];} } + for (const c of pack.cultures) { + if (!c.i || c.removed) continue; + c.center = findCell(c.x, c.y); + } + BurgsAndStates.drawStateLabels(); drawStates(); drawBorders(); @@ -883,7 +896,12 @@ function editHeightmap() { setOverlayOpacity(0); document.getElementById("convertImageLoad").classList.add("glow"); // add glow effect - tip('Image Converter is opened. Upload the image and assign the colors to desired heights', true); // main tip + tip('Image Converter is opened. Upload the image and assign the colors to desired heights', true, "warn"); // main tip + + // remove all heights + grid.cells.h = new Uint8Array(grid.cells.i.length); + terrs.selectAll("*").remove(); + updateHistory(); if (modules.openImageConverter) return; modules.openImageConverter = true; @@ -944,8 +962,8 @@ function editHeightmap() { colorsUnassigned.style.display = "block"; colorsAssigned.style.display = "none"; - const gridColors = grid.points.map(function(p) { - const x = Math.floor(p[0]), y = Math.floor(p[1]); + const gridColors = grid.points.map(p => { + const x = Math.floor(p[0]-.01), y = Math.floor(p[1]-.01); const i = (x + y * graphWidth) * 4; const r = data[i], g = data[i+1], b = data[i+2]; return [r, g, b]; @@ -961,7 +979,7 @@ function editHeightmap() { return clr; }).on("click", mapClicked); - const unassigned = [...usedColors].sort((a, b) => d3.lab(a).b - d3.lab(b).b); + const unassigned = [...usedColors].sort((a, b) => d3.lab(a).l - d3.lab(b).l); const unassignedContainer = d3.select("#colorsUnassigned"); unassignedContainer.selectAll("div").data(unassigned).enter().append("div") .attr("data-color", i => i).style("background-color", i => i) @@ -1002,7 +1020,7 @@ function editHeightmap() { const height = +this.getAttribute("data-color"); const rgb = color(1 - height/100); - const selectedColor = imageConverter.querySelector("div.selectedColor"); + const selectedColor = imageConverter.querySelector("div.selectedColor"); selectedColor.style.backgroundColor = rgb; selectedColor.setAttribute("data-color", rgb); selectedColor.setAttribute("data-height", height); @@ -1027,19 +1045,19 @@ function editHeightmap() { unassigned.forEach(function(el) { const colorFrom = el.getAttribute("data-color"); const lab = d3.lab(colorFrom); - const normalized = type === "hue" ? rn(normalize(lab.b + lab.a / 2, -50, 200), 2) : rn(normalize(lab.l, 0, 100), 2); + const normalized = type === "hue" ? rn(normalize(lab.b + lab.a / 2, -50, 200), 2) : rn(normalize(lab.l, -15, 100), 2); const colorTo = color(1 - normalized); const heightTo = normalized * 100; - + terrs.selectAll("polygon[fill='" + colorFrom + "']").attr("fill", colorTo).attr("data-height", heightTo); el.style.backgroundColor = colorTo; el.setAttribute("data-color", colorTo); el.setAttribute("data-height", heightTo); - colorsAssigned.appendChild(el); + colorsAssigned.appendChild(el); }); colorsAssigned.style.display = "block"; - colorsUnassigned.style.display = "none"; + colorsUnassigned.style.display = "none"; } function changeConvertColorsNumber(change) { diff --git a/modules/utils.js b/modules/utils.js index 55f8efe7..ea6ed102 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -24,13 +24,14 @@ function getBoundaryPoints(width, height, spacing) { // get points on a regular square grid and jitter them a bit function getJitteredGrid(width, height, spacing) { const radius = spacing / 2; // square radius - const jittering = radius * 0.9; // max deviation - const jitter = function() {return Math.random() * 2 * jittering - jittering;}; + const jittering = radius * .9; // max deviation + const jitter = () => Math.random() * 2 * jittering - jittering; + let points = []; for (let y = radius; y < height; y += spacing) { for (let x = radius; x < width; x += spacing) { - let xj = rn(x + jitter(), 2); - let yj = rn(y + jitter(), 2); + const xj = Math.min(rn(x + jitter(), 2), width); + const yj = Math.min(rn(y + jitter(), 2), height); points.push([xj, yj]); } }