dav*_*555 10 javascript geometry 2d polygon html5-canvas
我正在使用JavaScript画布处理项目,并且需要能够将光标捕捉到多边形的某个距离.我已经可以捕捉到多边形本身,但我需要让光标更远.
据我所知,最好的方法是缩放多边形并捕捉到它,但是当我缩放多边形时,旧多边形的边缘与新多边形的边缘之间的距离并不总是匹配起来.
这是一个问题的例子:

编辑:灰色代表原始多边形,如果我正常缩放多边形,红色是我得到的,绿色是我想要完成的
我已经尝试将多边形转换为原点并乘以比例因子,但似乎无法将每个边缘按特定距离缩放.
我创建了一个jsFiddle,对于给定的多边形,计算一个我希望符合您要求的外部多边形.我在这个pdf文档中加入了数学.
更新:已经编写了代码来处理垂直线.
function Vector2(x, y)
{
this.x = x;
this.y = y;
}
function straight_skeleton(poly, spacing)
{
// http://stackoverflow.com/a/11970006/796832
// Accompanying Fiddle: http://jsfiddle.net/vqKvM/35/
var resulting_path = [];
var N = poly.length;
var mi, mi1, li, li1, ri, ri1, si, si1, Xi1, Yi1;
for(var i = 0; i < N; i++)
{
mi = (poly[(i+1) % N].y - poly[i].y)/(poly[(i+1) % N].x - poly[i].x);
mi1 = (poly[(i+2) % N].y - poly[(i+1) % N].y)/(poly[(i+2) % N].x - poly[(i+1) % N].x);
li = Math.sqrt((poly[(i+1) % N].x - poly[i].x)*(poly[(i+1) % N].x - poly[i].x)+(poly[(i+1) % N].y - poly[i].y)*(poly[(i+1) % N].y - poly[i].y));
li1 = Math.sqrt((poly[(i+2) % N].x - poly[(i+1) % N].x)*(poly[(i+2) % N].x - poly[(i+1) % N].x)+(poly[(i+2) % N].y - poly[(i+1) % N].y)*(poly[(i+2) % N].y - poly[(i+1) % N].y));
ri = poly[i].x+spacing*(poly[(i+1) % N].y - poly[i].y)/li;
ri1 = poly[(i+1) % N].x+spacing*(poly[(i+2) % N].y - poly[(i+1) % N].y)/li1;
si = poly[i].y-spacing*(poly[(i+1) % N].x - poly[i].x)/li;
si1 = poly[(i+1) % N].y-spacing*(poly[(i+2) % N].x - poly[(i+1) % N].x)/li1;
Xi1 = (mi1*ri1-mi*ri+si-si1)/(mi1-mi);
Yi1 = (mi*mi1*(ri1-ri)+mi1*si-mi*si1)/(mi1-mi);
// Correction for vertical lines
if(poly[(i+1) % N].x - poly[i % N].x==0)
{
Xi1 = poly[(i+1) % N].x + spacing*(poly[(i+1) % N].y - poly[i % N].y)/Math.abs(poly[(i+1) % N].y - poly[i % N].y);
Yi1 = mi1*Xi1 - mi1*ri1 + si1;
}
if(poly[(i+2) % N].x - poly[(i+1) % N].x==0 )
{
Xi1 = poly[(i+2) % N].x + spacing*(poly[(i+2) % N].y - poly[(i+1) % N].y)/Math.abs(poly[(i+2) % N].y - poly[(i+1) % N].y);
Yi1 = mi*Xi1 - mi*ri + si;
}
//console.log("mi:", mi, "mi1:", mi1, "li:", li, "li1:", li1);
//console.log("ri:", ri, "ri1:", ri1, "si:", si, "si1:", si1, "Xi1:", Xi1, "Yi1:", Yi1);
resulting_path.push({
x: Xi1,
y: Yi1
});
}
return resulting_path;
}
var canvas = document.getElementById("Canvas");
var ctx = canvas.getContext("2d");
var poly = [
new Vector2(150, 170),
new Vector2(400, 120),
new Vector2(200, 270),
new Vector2(350, 400),
new Vector2(210, 470)
];
draw(poly);
draw(straight_skeleton(poly, 10));
function draw(p) {
ctx.beginPath();
ctx.moveTo(p[0].x, p[0].y);
for(var i = 1; i < p.length; i++)
{
ctx.lineTo(p[i].x, p[i].y);
}
ctx.strokeStyle = "#000000";
ctx.closePath();
ctx.stroke();
}
Run Code Online (Sandbox Code Playgroud)
将多边形放入点对象数组中.
该函数在画布上draw(p)绘制多边形p.
给定的多边形是数组poly,数组poly的外部.
spacing 是多边形之间的距离(如绿色图表中的箭头所示)
按照安格斯约翰逊的评论,我制作了一些更多的小提琴来展示他提出的问题.这个问题比我最初想的要困难得多.
我创建了一个Clipper的javascript端口,你可以用你想要的方式进行缩放.
这是一个膨胀多边形的例子:

检查Javascript Clipper 的LIVE DEMO.
并从https://sourceforge.net/projects/jsclipper/获取clipper.js文件.
完整的代码示例,说明如何偏移多边形并在html5画布上绘制它们.
如果需要,相反(放气)也是可能的:

我可以使用JSTS库( JTS的 JavaScript 端口)提供解决方案。该库具有用于多边形膨胀/收缩(偏移)的方法。
如果您想获得具有不同类型边缘的膨胀多边形,您可以设置封顶和连接样式。您唯一需要做的就是将多边形坐标转换为 JSTS 坐标,这非常简单:
function vectorCoordinates2JTS (polygon) {
var coordinates = [];
for (var i = 0; i < polygon.length; i++) {
coordinates.push(new jsts.geom.Coordinate(polygon[i].x, polygon[i].y));
}
return coordinates;
}
转换坐标后,您可以膨胀多边形:
function inflatePolygon(poly, spacing) {
var geoInput = vectorCoordinates2JTS(poly);
geoInput.push(geoInput[0]);
var geometryFactory = new jsts.geom.GeometryFactory();
var shell = geometryFactory.createPolygon(geoInput);
var polygon = shell.buffer(spacing);
//try with different cap style
//var polygon = shell.buffer(spacing, jsts.operation.buffer.BufferParameters.CAP_FLAT);
var inflatedCoordinates = [];
var oCoordinates;
oCoordinates = polygon.shell.points.coordinates;
for (i = 0; i < oCoordinates.length; i++) {
var oItem;
oItem = oCoordinates[i];
inflatedCoordinates.push(new Vector2(Math.ceil(oItem.x), Math.ceil(oItem.y)));
}
return inflatedCoordinates;
}
Run Code Online (Sandbox Code Playgroud)
使用我在jsFiddle上发布的代码,您将得到如下内容:
附加信息: 我通常使用这种类型的膨胀/放气(稍微改变)来设置在地图(使用 Leaflet 或 Google 地图)上绘制的多边形上的半径边界。您只需将 lat,lng 对转换为 JSTS 坐标,其他一切都是一样的。例子:
| 归档时间: |
|
| 查看次数: |
3714 次 |
| 最近记录: |