是否有一种(有效的)方法来(a)计算 d3.js 中固定点和 svg:path 元素之间的最短距离,以及(b)确定路径上属于该距离的点?
问问题
2883 次
3 回答
3
在一般情况下,我不这么认为。SVG 路径是一个复杂的元素。例如,如果路径是贝塞尔曲线,则控制点可能不在所表示的线上,所表示的形状可能在控制点的边界框之外。
我认为,如果您有一组用于生成路径的点,您可以使用这些点来计算从这些点到给定点的距离并获得最小距离。在MDN SVG 路径教程中,您可以找到一些复杂形状的示例以及它们是如何制作的。
于 2013-06-18T21:24:10.543 回答
2
虽然我的微积分答案仍然有效,但您可以在这个 bl.ocks 示例中做所有事情:
var points = [[474,276],[586,393],[378,388],[338,323],[341,138],[547,252],[589,148],[346,227],[365,108],[562,62]];
var width = 960,
height = 500;
var line = d3.svg.line()
.interpolate("cardinal");
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var path = svg.append("path")
.datum(points)
.attr("d", line);
var line = svg.append("line");
var circle = svg.append("circle")
.attr("cx", -10)
.attr("cy", -10)
.attr("r", 3.5);
svg.append("rect")
.attr("width", width)
.attr("height", height)
.on("mousemove", mousemoved);
function mousemoved() {
var m = d3.mouse(this),
p = closestPoint(path.node(), m);
line.attr("x1", p[0]).attr("y1", p[1]).attr("x2", m[0]).attr("y2", m[1]);
circle.attr("cx", p[0]).attr("cy", p[1]);
}
function closestPoint(pathNode, point) {
var pathLength = pathNode.getTotalLength(),
precision = pathLength / pathNode.pathSegList.numberOfItems * .125,
best,
bestLength,
bestDistance = Infinity;
// linear scan for coarse approximation
for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) {
best = scan, bestLength = scanLength, bestDistance = scanDistance;
}
}
// binary search for precise estimate
precision *= .5;
while (precision > .5) {
var before,
after,
beforeLength,
afterLength,
beforeDistance,
afterDistance;
if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) {
best = before, bestLength = beforeLength, bestDistance = beforeDistance;
} else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) {
best = after, bestLength = afterLength, bestDistance = afterDistance;
} else {
precision *= .5;
}
}
best = [best.x, best.y];
best.distance = Math.sqrt(bestDistance);
return best;
function distance2(p) {
var dx = p.x - point[0],
dy = p.y - point[1];
return dx * dx + dy * dy;
}
}
path {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
line {
fill: none;
stroke: red;
stroke-width: 1.5px;
}
circle {
fill: red;
}
rect {
fill: none;
cursor: crosshair;
pointer-events: all;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
我把所有的时间都花在了上一个答案上,写了漂亮的 LaTeX!
于 2015-11-25T05:05:12.273 回答
1
我不知道对此有特定于 d3 的解决方案。但是,如果您的路径可以表示为函数的一部分,那么通过一点微积分就有希望了。
- 从线长方程开始。
- 将您的点插入 x1 和 y1。
- 将剩余的 y 替换为代表您的路径的函数。
- 化简,然后计算导数。
- 设置为 0 并求解。希望一个 x 值将在您的路径端点的范围内。将此 x 应用于您的路径功能,您就可以在路径上找到自己的观点。
一个更直观的例子。在 JavaScript 中实现这一点需要考虑很多因素:我的函数是什么?获取上述导数的最快方法是什么?这些是特定于您的情况的。
于 2015-11-25T04:55:57.823 回答