This commit is contained in:
Azgaar 2021-03-14 14:42:35 +03:00
commit 2ac6586489
3 changed files with 167 additions and 3 deletions

View file

@ -3099,6 +3099,11 @@
<div id="unitsBottom"> <div id="unitsBottom">
<button id="addLinearRuler" data-tip="Click to place a linear measurer (ruler)" class="icon-ruler"></button> <button id="addLinearRuler" data-tip="Click to place a linear measurer (ruler)" class="icon-ruler"></button>
<button id="addOpisometer" data-tip="Drag to measure a curve length (opisometer)" class="icon-drafting-compass"></button> <button id="addOpisometer" data-tip="Drag to measure a curve length (opisometer)" class="icon-drafting-compass"></button>
<button id="addRouteOpisometer" data-tip="Drag to measure a curve length that sticks to routes (route opisometer)">
<svg width="0.88em" height="0.88em">
<use xlink:href="#icon-route" />
</svg>
</button>
<button id="addPlanimeter" data-tip="Drag to measure a polygon area (planimeter)" class="icon-draw-polygon"></button> <button id="addPlanimeter" data-tip="Drag to measure a polygon area (planimeter)" class="icon-draw-polygon"></button>
<button id="removeRulers" data-tip="Remove all rulers from the map. Click on ruler label to remove a ruler separately" class="icon-trash"></button> <button id="removeRulers" data-tip="Remove all rulers from the map. Click on ruler label to remove a ruler separately" class="icon-trash"></button>
<button id="unitsRestore" data-tip="Restore default units settings" class="icon-ccw"></button> <button id="unitsRestore" data-tip="Restore default units settings" class="icon-ccw"></button>
@ -3438,6 +3443,10 @@
<path d="M15 4c0-0.547-0.453-1-1-1s-1 0.453-1 1 0.453 1 1 1 1-0.453 1-1zM28 18.5v5.5c0 0.203-0.125 0.391-0.313 0.469-0.063 0.016-0.125 0.031-0.187 0.031-0.125 0-0.25-0.047-0.359-0.141l-1.453-1.453c-2.453 2.953-6.859 4.844-11.688 4.844s-9.234-1.891-11.688-4.844l-1.453 1.453c-0.094 0.094-0.234 0.141-0.359 0.141-0.063 0-0.125-0.016-0.187-0.031-0.187-0.078-0.313-0.266-0.313-0.469v-5.5c0-0.281 0.219-0.5 0.5-0.5h5.5c0.203 0 0.391 0.125 0.469 0.313s0.031 0.391-0.109 0.547l-1.563 1.563c1.406 1.891 4.109 3.266 7.203 3.687v-10.109h-3c-0.547 0-1-0.453-1-1v-2c0-0.547 0.453-1 1-1h3v-2.547c-1.188-0.688-2-1.969-2-3.453 0-2.203 1.797-4 4-4s4 1.797 4 4c0 1.484-0.812 2.766-2 3.453v2.547h3c0.547 0 1 0.453 1 1v2c0 0.547-0.453 1-1 1h-3v10.109c3.094-0.422 5.797-1.797 7.203-3.687l-1.563-1.563c-0.141-0.156-0.187-0.359-0.109-0.547s0.266-0.313 0.469-0.313h5.5c0.281 0 0.5 0.219 0.5 0.5z"></path> <path d="M15 4c0-0.547-0.453-1-1-1s-1 0.453-1 1 0.453 1 1 1 1-0.453 1-1zM28 18.5v5.5c0 0.203-0.125 0.391-0.313 0.469-0.063 0.016-0.125 0.031-0.187 0.031-0.125 0-0.25-0.047-0.359-0.141l-1.453-1.453c-2.453 2.953-6.859 4.844-11.688 4.844s-9.234-1.891-11.688-4.844l-1.453 1.453c-0.094 0.094-0.234 0.141-0.359 0.141-0.063 0-0.125-0.016-0.187-0.031-0.187-0.078-0.313-0.266-0.313-0.469v-5.5c0-0.281 0.219-0.5 0.5-0.5h5.5c0.203 0 0.391 0.125 0.469 0.313s0.031 0.391-0.109 0.547l-1.563 1.563c1.406 1.891 4.109 3.266 7.203 3.687v-10.109h-3c-0.547 0-1-0.453-1-1v-2c0-0.547 0.453-1 1-1h3v-2.547c-1.188-0.688-2-1.969-2-3.453 0-2.203 1.797-4 4-4s4 1.797 4 4c0 1.484-0.812 2.766-2 3.453v2.547h3c0.547 0 1 0.453 1 1v2c0 0.547-0.453 1-1 1h-3v10.109c3.094-0.422 5.797-1.797 7.203-3.687l-1.563-1.563c-0.141-0.156-0.187-0.359-0.109-0.547s0.266-0.313 0.469-0.313h5.5c0.281 0 0.5 0.219 0.5 0.5z"></path>
</symbol> </symbol>
<symbol id="icon-route" viewBox="0 0 512 512">
<path d="M416 320h-96c-17.6 0-32-14.4-32-32s14.4-32 32-32h96s96-107 96-160-43-96-96-96-96 43-96 96c0 25.5 22.2 63.4 45.3 96H320c-52.9 0-96 43.1-96 96s43.1 96 96 96h96c17.6 0 32 14.4 32 32s-14.4 32-32 32H185.5c-16 24.8-33.8 47.7-47.3 64H416c52.9 0 96-43.1 96-96s-43.1-96-96-96zm0-256c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zM96 256c-53 0-96 43-96 96s96 160 96 160 96-107 96-160-43-96-96-96zm0 128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"></path>
</symbol>
<g id="defs-relief"> <g id="defs-relief">
<symbol id="relief-mount-1" viewBox="0 0 100 100"> <symbol id="relief-mount-1" viewBox="0 0 100 100">
<path d="m3,69 16,-12 31,-32 15,20 30,24" fill="#fff" stroke="#5c5c70" stroke-width="1"></path> <path d="m3,69 16,-12 31,-32 15,20 30,24" fill="#fff" stroke="#5c5c70" stroke-width="1"></path>

View file

@ -22,6 +22,7 @@ class Rulers {
const points = pointsString.split(" ").map(el => el.split(",").map(n => +n)); const points = pointsString.split(" ").map(el => el.split(",").map(n => +n));
const Type = type === "Ruler" ? Ruler : const Type = type === "Ruler" ? Ruler :
type === "Opisometer" ? Opisometer : type === "Opisometer" ? Opisometer :
type === "RouteOpisometer" ? RouteOpisometer :
type === "Planimeter" ? Planimeter : null; type === "Planimeter" ? Planimeter : null;
this.create(Type, points); this.create(Type, points);
} }
@ -306,6 +307,112 @@ class Opisometer extends Measurer {
} }
} }
class RouteOpisometer extends Measurer {
constructor(points) {
super(points);
if (pack.cells) {
this.cellStops = points.map(p => findCell(p[0], p[1]));
} else {
this.cellStops = null;
}
}
checkCellStops() {
if (!this.cellStops) {
this.cellStops = this.points.map(p => findCell(p[0], p[1]));
}
}
trackCell(cell, rigth) {
this.checkCellStops();
const cellStops = this.cellStops;
const foundIndex = cellStops.indexOf(cell);
if (rigth) {
if (last(cellStops) === cell) {
return;
} else if (cellStops.length > 1 && foundIndex != -1) {
cellStops.splice(foundIndex + 1);
this.points.splice(foundIndex + 1);
} else {
cellStops.push(cell);
this.points.push(this.getCellRouteCoord(cell));
}
} else {
if (cellStops[0] === cell) {
return;
} else if (cellStops.length > 1 && foundIndex != -1) {
cellStops.splice(0, foundIndex);
this.points.splice(0, foundIndex);
} else {
cellStops.unshift(cell);
this.points.unshift(this.getCellRouteCoord(cell));
}
}
this.updateCurve();
this.updateLabel();
}
getCellRouteCoord(c) {
const cells = pack.cells;
const burgs = pack.burgs;
const b = cells.burg[c];
const x = b ? burgs[b].x : cells.p[c][0];
const y = b ? burgs[b].y : cells.p[c][1];
return [x, y];
}
draw() {
if (this.el) this.el.selectAll("*").remove();
const size = this.getSize();
const dash = this.getDash();
const context = this;
const el = this.el = ruler.append("g").attr("class", "opisometer").attr("font-size", 10 * size);
el.append("path").attr("class", "white").attr("stroke-width", size);
el.append("path").attr("class", "gray").attr("stroke-width", size).attr("stroke-dasharray", dash);
const rulerPoints = el.append("g").attr("class", "rulerPoints").attr("stroke-width", .5 * size).attr("font-size", 2 * size);
rulerPoints.append("circle").attr("r", "1em").call(d3.drag().on("start", function() {context.dragControl(context, 0)}));
rulerPoints.append("circle").attr("r", "1em").call(d3.drag().on("start", function() {context.dragControl(context, 1)}));
el.append("text").attr("dx", ".35em").attr("dy", "-.45em").on("click", () => rulers.remove(this.id));
this.updateCurve();
this.updateLabel();
return this;
}
updateCurve() {
lineGen.curve(d3.curveCatmullRom.alpha(.5));
const path = round(lineGen(this.points));
this.el.selectAll("path").attr("d", path);
const left = this.points[0];
const right = last(this.points);
this.el.select(".rulerPoints > circle:first-child").attr("cx", left[0]).attr("cy", left[1]);
this.el.select(".rulerPoints > circle:last-child").attr("cx", right[0]).attr("cy", right[1]);
}
updateLabel() {
const length = this.el.select("path").node().getTotalLength();
const text = rn(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
const [x, y] = last(this.points);
this.el.select("text").attr("x", x).attr("y", y).text(text);
}
dragControl(context, rigth) {
d3.event.on("drag", function() {
const mousePoint = [d3.event.x | 0, d3.event.y | 0];
const cells = pack.cells;
const c = findCell(mousePoint[0], mousePoint[1]);
if (!cells.road[c] && !d3.event.sourceEvent.shiftKey) {
return;
}
context.trackCell(c, rigth);
});
}
}
class Planimeter extends Measurer { class Planimeter extends Measurer {
constructor(points) { constructor(points) {
super(points); super(points);

View file

@ -35,6 +35,7 @@ function editUnits() {
document.getElementById("addLinearRuler").addEventListener("click", addRuler); document.getElementById("addLinearRuler").addEventListener("click", addRuler);
document.getElementById("addOpisometer").addEventListener("click", toggleOpisometerMode); document.getElementById("addOpisometer").addEventListener("click", toggleOpisometerMode);
document.getElementById("addRouteOpisometer").addEventListener("click", toggleRouteOpisometerMode);
document.getElementById("addPlanimeter").addEventListener("click", togglePlanimeterMode); document.getElementById("addPlanimeter").addEventListener("click", togglePlanimeterMode);
document.getElementById("removeRulers").addEventListener("click", removeAllRulers); document.getElementById("removeRulers").addEventListener("click", removeAllRulers);
document.getElementById("unitsRestore").addEventListener("click", restoreDefaultUnits); document.getElementById("unitsRestore").addEventListener("click", restoreDefaultUnits);
@ -242,6 +243,53 @@ function editUnits() {
} }
} }
function toggleRouteOpisometerMode() {
if (this.classList.contains("pressed")) {
restoreDefaultEvents();
clearMainTip();
this.classList.remove("pressed");
} else {
if (!layerIsOn("toggleRulers")) toggleRulers();
tip("Draw a curve along routes to measure length. Hold Shift to measure away from roads.", true);
unitsBottom.querySelectorAll(".pressed").forEach(button => button.classList.remove("pressed"));
this.classList.add("pressed");
viewbox.style("cursor", "crosshair").call(d3.drag().on("start", function() {
const cells = pack.cells;
const burgs = pack.burgs;
const point = d3.mouse(this);
const c = findCell(point[0], point[1]);
if (cells.road[c] || d3.event.sourceEvent.shiftKey) {
const b = cells.burg[c];
const x = b ? burgs[b].x : cells.p[c][0];
const y = b ? burgs[b].y : cells.p[c][1];
const routeOpisometer = rulers.create(RouteOpisometer, [[x, y]]).draw();
d3.event.on("drag", function () {
const point = d3.mouse(this);
const c = findCell(point[0], point[1]);
if (cells.road[c] || d3.event.sourceEvent.shiftKey) {
routeOpisometer.trackCell(c, true);
}
});
d3.event.on("end", function () {
restoreDefaultEvents();
clearMainTip();
addRouteOpisometer.classList.remove("pressed");
if (routeOpisometer.points.length < 2) {
rulers.remove(routeOpisometer.id);
}
});
} else {
restoreDefaultEvents();
clearMainTip();
addRouteOpisometer.classList.remove("pressed");
tip("Must start in a cell with a route in it", false, "error");
}
}));
}
}
function togglePlanimeterMode() { function togglePlanimeterMode() {
if (this.classList.contains("pressed")) { if (this.classList.contains("pressed")) {
restoreDefaultEvents(); restoreDefaultEvents();