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]);
}
}