【问题标题】:Scale a polygon so the edges match up缩放多边形以使边缘匹配
【发布时间】:2012-08-10 03:20:42
【问题描述】:

我正在使用 JavaScript 画布处理一个项目,并且需要能够将光标捕捉到距多边形一定距离的位置。我已经可以捕捉到多边形本身,但我需要让光标离得更远。

据我所知,最好的方法是缩放多边形并捕捉到它,但是当我缩放多边形时,旧多边形的边缘和新多边形的边缘之间的距离不要' t 总是匹配的。

这是一个问题的例子:

编辑:灰色代表原始多边形,红色是我正常缩放多边形时得到的,绿色是我想要完成的

我已经尝试将多边形转换为原点并乘以比例因子,但似乎无法将每条边缩放特定距离。

【问题讨论】:

  • 你能贴出你用于多边形、平移和缩放的代码吗?你所说的“并不总是匹配”是什么意思?什么情况下会失败?
  • 如果你有一个多边形,并且你想通过创建一个基于点集的新边界来创建一个多边形,这些点与多边形中的任何线相距x 个单位,忽略角点,并绘制或擦除线条,直到您的原始多边形周围形成形状,您通常不会生成原始多边形的比例版本(除非它可能是正多边形)。考虑一个可能是 90 个单位乘 1 个单位的细长矩形,然后在每边添加 500,000 个单位……实际上,您最终会得到一个正方形。

标签: javascript geometry 2d html5-canvas polygon


【解决方案1】:

我为给定的多边形创建了一个jsFiddle,它计算了一个我希望满足您要求的外部多边形。我已经把它背后的数学放在了这个pdf document 中。

更新:代码已用于处理垂直线。

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

一个多边形被放入一个点对象数组中。

函数draw(p) 在画布上绘制多边形p

给定的多边形在数组 poly 中,在数组 poly 中的外部。

spacing 是多边形之间的距离(沿着绿色图中的箭头)


在安格斯约翰逊的评论之后,我制作了更多的小提琴来展示他提出的问题。这个问题比我最初想象的要困难得多。

【讨论】:

  • 我快速查看了您的代码和 ISTM,它只会正确偏移最简单的多边形。例如,我看不到任何代码来管理非常锐角的凸角,其中顶点可以相对于偏移距离移动指数距离。此外,对于边相对较短的锐凹角,需要编写代码来删除由于简单偏移而产生的自相交。
  • @Angus Johnson 我刚刚在您指定的条件下进行了尝试,我明白您的意思。感谢您指出了这一点。我再考虑一下。也许 davey555 会评论这些条件是否会在他的申请中出现。
【解决方案2】:

我制作了一个 Clipper 的 javascript 端口,您可以使用它以您想要的方式进行缩放。

这是一个膨胀多边形的例子:

检查 Javascript Clipper 的LIVE DEMO

并从https://sourceforge.net/projects/jsclipper/ 获取clipper.js 文件。

Full code example 了解如何偏移多边形并将它们绘制在 html5 画布上。

如果需要,相反的(放气)也是可能的:

【讨论】:

    【解决方案3】:

    您所追求的是多边形偏移算法或库。
    An algorithm for inflating/deflating (offsetting, buffering) polygons

    【讨论】:

    • 在 Javascript 中进行多边形偏移有什么想法吗?
    • Java 库 JTS(Java 拓扑套件)会进行偏移(虽然它在那里被称为缓冲)。见sourceforge.net/projects/jts-topo-suite
    • 它是 Java,而不是 Javascript,并且还需要移植过程。它与 Clipper 相比如何?
    • 抱歉,我不知道有任何用 javascript 编写的多边形裁剪/偏移库。我猜你需要创建/编译一个可以从你的 javascript 代码中调用的外部模块。我不知道 JTS 与 Clipper 相比如何(尽管 Clipper 与 GEOS 的表现非常出色,GEOS 是 JTS 的 C++ 端口)。这里有多个剪辑库的对比:rogue-modron.blogspot.com.au/2011/04/…
    • Clipper 在上述测试中表现出色。似乎只有 boost.geometry 在某些情况下更快。我是对的,Clipper 许可证允许移植到 Javascript 吗?
    【解决方案4】:

    一种方法是找到多边形的每条边与光标点之间的距离,并保持最小。

    要计算点和线段之间的距离,请将点投影到支撑线上;如果投影落在端点之间,则解是点到线的距离;否则,解是到最近端点的距离。

    这很容易用向量演算计算出来。

    【讨论】:

      【解决方案5】:

      我可以使用JSTS 库(JTS 的 JavaScript 端口)提供解决方案。该库具有用于膨胀/放气(偏移)多边形的方法。

      如果您想获得具有不同类型边缘的膨胀多边形,您可以设置 cap 和 join 样式。您唯一需要做的就是将多边形坐标转换为非常简单的 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;
      }
      

      使用我在jsFiddle 上发布的代码,您将获得如下信息:

      附加信息: 我通常使用这种类型的充气/放气(稍作改动)来设置地图上绘制的多边形的半径边界(使用 Leaflet 或 Google 地图)。您只需将 lat,lng 对转换为 JSTS 坐标,其他一切都相同。示例:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-06-20
        • 2017-01-14
        • 1970-01-01
        • 2016-06-26
        • 1970-01-01
        • 2011-10-13
        • 1970-01-01
        相关资源
        最近更新 更多