diff --git a/index.css b/index.css index 4d36325c..46719fe7 100644 --- a/index.css +++ b/index.css @@ -1763,6 +1763,7 @@ rect.fillRect { #ruler .gray { stroke: #3d3d3d; + pointer-events: none; } #ruler text { diff --git a/index.html b/index.html index 13f9793b..b26e2076 100644 --- a/index.html +++ b/index.html @@ -1423,7 +1423,7 @@ @@ -1432,7 +1432,7 @@ diff --git a/modules/ui/measurers.js b/modules/ui/measurers.js index 52bb769a..093be6b3 100644 --- a/modules/ui/measurers.js +++ b/modules/ui/measurers.js @@ -32,7 +32,8 @@ class Ruler { const dash = rn(30 / distanceScaleInput.value, 2); const el = this.el = ruler.append("g").attr("class", "ruler").call(d3.drag().on("start", this.drag)).attr("font-size", 10 * size) - el.append("polyline").attr("points", points).attr("class", "white").attr("stroke-width", size); + el.append("polyline").attr("points", points).attr("class", "white").attr("stroke-width", size) + .call(d3.drag().on("start", () => this.addControl(this))); el.append("polyline").attr("points", points).attr("class", "gray").attr("stroke-width", rn(size * 1.2, 2)).attr("stroke-dasharray", dash); el.append("g").attr("class", "rulerPoints").attr("stroke-width", .5 * size).attr("font-size", 2 * size); el.append("text").attr("dx", ".35em").attr("dy", "-.45em").on("click", this.remove); @@ -115,6 +116,16 @@ class Ruler { }); } + addControl(context) { + const x = rn(d3.event.x, 1); + const y = rn(d3.event.y, 1); + const pointId = getSegmentId(context.points, [x, y]); + + context.points.splice(pointId, 0, [x, y]); + context.renderPoints(context.el); + context.dragControl(context, pointId); + } + removePoint(context, pointId) { this.points.splice(pointId, 1); if (this.points.length < 2) context.el.remove(); diff --git a/modules/utils.js b/modules/utils.js index ef2fc914..38a9b856 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -358,6 +358,38 @@ void function addFindAll() { } }() +// get segment of any point on polyline +function getSegmentId(points, point) { + if (points.length === 2) return 1; + const d2 = (p1, p2) => (p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2; + + let minSegment = 1; + let minDist = Infinity; + + for (let i=0; i < points.length-1; i++) { + const p1 = points[i]; + const p2 = points[i+1]; + + const length = Math.sqrt(d2(p1, p2)); + const segments = Math.ceil(length / 10); + const dx = (p2[0] - p1[0]) / segments; + const dy = (p2[1] - p1[1]) / segments; + + for (let s=0; s < segments; s++) { + const x = p1[0] + s * dx; + const y = p1[1] + s * dy; + const dist2 = d2(point, [x, y]); + + if (dist2 >= minDist) continue; + minDist = dist2; + minSegment = i+1; + } + } + + console.log(minSegment); + return minSegment; +} + // normalization function function normalize(val, min, max) { return Math.min(Math.max((val - min) / (max - min), 0), 1);