JavaScript中的多边形偏移

14

EDIT5: 最终将Angus Johnson的Clipper库实现为Javascript并选择Sourceforge进行托管。

在线演示:http://jsclipper.sourceforge.net/6.1.1.1/main_demo.html

下载源代码:https://sourceforge.net/projects/jsclipper/

详细教程可在Wikipage中查看:https://sourceforge.net/p/jsclipper/wiki/Home%206/

演示程序包含数十个样例多边形,点击查看

我希望这能帮助需要折线和多边形剪切库及偏移特性的任何人。


EDIT4: 一种可能性是使用p2js将Pascal转换为Javascript。目前还未成功。p2js.exe clipper.pas给出了致命错误“找不到clipper使用的系统单元”。


EDIT: 我发现script#Github)可以将C#转换为Javascript。Clipper库可用于C#,所以是否可能使用Script#进行C#->JS转换,具体如何操作?

编辑3:使用Script#无法进行转换,但也有Emscripten可供选择,但将4000个cpp行转换为30万个JavaScript行,因此不是一个选项。手动转换似乎更可靠。
编辑2:我制作了一个示例,展示了问题。使用箭头左右移动来应用偏移量。在某些距离上它可以正常工作,但后来出现了问题。黄色笔画的多边形被称为原始偏移多边形,据我所知,Clipper库提供了一种方法来处理删除原始偏移多边形中不需要的部分。
Angus Johnson开发了一个用于偏移多边形的Clipper库。
我需要将这个功能用于JavaScript中的SVG多边形偏移。 有人制作过它的Javascript端口吗? 如果没有,我会感激您提供以下指导:
- 需要多大的任务量?
- 选择哪个源(Delphi、C#、C++)?
- 做多少才能完成偏移?

Clipper库产生的结果如下图所示,这正是所需的功能: Offset Polygons, polygons, delta, jointype, miterlimit, jtSquare jtRound jtMiter 一些链接:
- Sourceforge上的文件
- Clipper文档
- 一个Stackoverflow答案
- 偏移算法

你之前没有解决过这个问题吗?https://dev59.com/Z2cs5IYBdhLWcg3wlFE2#12723835 - Robert Longson
有点类似,但我更希望使用简单的新几何图形(多边形),而不是复杂的遮罩结构或相邻重复对象,因为这些可能在每个平台上都无法正常工作。 - Timo Kähkönen
4个回答

4
我将Clipper成功地移植到JS,并经过一段时间的彻底测试后,准备发布它。看起来所有功能都已经被移植了。
需要注意的是,128位支持被降低到106位。
其中一个优点是可以覆盖大量的浏览器空间,并且可以使用SVG、VML、HTML5画布作为图形界面。
有什么想法,哪个主机最容易发布,并具有演示的可能性?
编辑:
最终在Javascript中实现了Angus Johnson的Clipper库,并选择了Sourceforge作为主机。
实时演示:http://jsclipper.sourceforge.net/6.1.1.1/main_demo.html 下载:https://sourceforge.net/projects/jsclipper/ 带有逐步教程的Wikipage:https://sourceforge.net/p/jsclipper/wiki/Home%206/ 演示程序的介绍,包括数十个样本多边形:https://sourceforge.net/p/jsclipper/wiki/Main_Demo%206/ 我希望这能帮助任何需要带有偏移功能的折线和多边形剪辑库的人。

3
    function offsetPoints(pts, offset) {
        let newPoints = [];
        for (let j = 0; j < pts.length; j++) {
            let i = (j - 1);
            if (i < 0) i += pts.length;
            let k = (j + 1) % pts.length;

            let v1 = [pts[j].x - pts[i].x, pts[j].y - pts[i].y];
            let mag1 = Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1])
            v1 = [v1[0] / mag1, v1[1] / mag1]
            v1 = [v1[0] * offset, v1[1] * offset]
            let n1 = [-v1[1], v1[0]];
            let x1 = pts[i].x + n1[0];
            let y1 = pts[i].y + n1[1];
            let x2 = pts[j].x + n1[0];
            let y2 = pts[j].y + n1[1];

            let v2 = [pts[k].x - pts[j].x, pts[k].y - pts[j].y];
            let mag2 = Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1])
            v2 = [v2[0] / mag2, v2[1] / mag2]
            v2 = [v2[0] * offset, v2[1] * offset]
            let n2 = [-v2[1], v2[0]];
            let x3 = pts[j].x + n2[0];
            let y3 = pts[j].y + n2[1];
            let x4 = pts[k].x + n2[0];
            let y4 = pts[k].y + n2[1];

            let den = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
            let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / den;
            let x = x1 + ua * (x2 - x1);
            let y = y1 + ua * (y2 - y1);

            newPoints.push({ x, y });
        }

        return newPoints;
    }

似乎包含一些错误。你能否举个例子展示它是如何工作的? - Juan Antonio

2
在多边形膨胀方面,没有简单的解决方案。如果你有一个凹多边形,当你减小偏移量时,它迟早会分裂成几个较小的多边形。因此,我建议使用现有的、经过验证的算法(Clipper 应该是一个不错的选择)。
关于将 C# 移植到 JS 的问题,我认为这是可能的,但问题在于需要多长时间以及自动移植工具是否有用。根据this discussion,我对此表示怀疑。
我尝试使用ScriptSharp将C#代码翻译成Javascript,但由于存在太多不兼容的结构,因此无法使用它,并且我无法将其输出为javascript文件。尝试在Javascript中实现Vatti剪辑算法似乎是下一步。并且,使用各种自动转换工具是没有用的。Clipper有像Int64或Int128这样的数据结构,在JS或AS中不存在。我完全删除了它们。对于大多数情况,Int32应该足够了,除非您处理与地理相关或巨大地图相关的内容。用户提到的ActionScript端口不再可用,遗憾。

1
这是至少可用的:https://github.com/ChrisDenham/PolygonClipper.AS3。你说得对,自动转换不是一个选项。使用Emscripten将4000个cpp行转换为300,000个JS行。我甚至没有测试它是否有效... - Timo Kähkönen

0
以下是一个工作示例,使用了Angus Johnson这里提到的代码。它使用了Javascript Clipper,这是Angus的代码的一个移植版本。
这是从这里复制过来的,作为一个参考,以防网站的URL发生变化,因为它是旧的。

// https://jsclipper.sourceforge.net/6.4.2.2/index.html?p=sources_as_text/basic_demo_offset_svg.txt
function draw() {
    var svg, cont = document.getElementById('svgcontainer'), i, ii, j;
    var paths = [[{ X: 30, Y: 30 }, { X: 130, Y: 30 }, { X: 130, Y: 130 }, { X: 30, Y: 130 }],
    [{ X: 60, Y: 60 }, { X: 60, Y: 100 }, { X: 100, Y: 100 }, { X: 100, Y: 60 }]];
    var scale = 100;
    ClipperLib.JS.ScaleUpPaths(paths, scale);
    var joinTypes = [ClipperLib.JoinType.jtSquare, ClipperLib.JoinType.jtRound, ClipperLib.JoinType.jtMiter];
    var endType = ClipperLib.EndType.etClosedPolygon;

    var joinTypesTexts = ["Square", "Round", "Miter"];
    var deltas = [-10, 0, 10];

    var co = new ClipperLib.ClipperOffset(); // constructor
    var offsetted_paths = new ClipperLib.Paths(); // empty solution

    for (i = 0; i < joinTypes.length; i++) {
        for (ii = 0; ii < deltas.length; ii++) {
            co.Clear();
            co.AddPaths(paths, joinTypes[i], endType);
            co.MiterLimit = 2;
            co.ArcTolerance = 0.25;

            co.Execute(offsetted_paths, deltas[ii] * scale);
            //console.log(JSON.stringify(offsetted_paths));

            svg = `<svg id="${i.toString() + ii.toString()}" style="margin-top:10px;margin-right:10px;margin-bottom:10px;background-color:#dddddd" width="160" height="160">`;
            svg += '<path stroke="black" fill="yellow" stroke-width="2" d="' + paths2string(offsetted_paths, scale) + '"/>';
            svg += '</svg>';
            cont.innerHTML += svg;
        }
        cont.innerHTML += "<br>" + joinTypesTexts[i] + " " + deltas[0] + ", ";
        cont.innerHTML += joinTypesTexts[i] + " " + deltas[1] + ", ";
        cont.innerHTML += joinTypesTexts[i] + " " + deltas[2] + "<hr>";
    }
}

// Converts Paths to SVG path string
// and scales down the coordinates
function paths2string(paths, scale) {
    var svgpath = "", i, j;
    if (!scale) scale = 1;
    for (i = 0; i < paths.length; i++) {
        for (j = 0; j < paths[i].length; j++) {
            if (!j) svgpath += "M";
            else svgpath += "L";
            svgpath += (paths[i][j].X / scale) + ", " + (paths[i][j].Y / scale);
        }
        svgpath += "Z";
    }
    if (svgpath == "") svgpath = "M0,0";
    return svgpath;
}
h3 {
  margin-bottom: 2px
}

body,
th,
td,
input,
legend,
fieldset,
p,
b,
button,
select,
textarea {
  font-size: 14px;
  font-family: Arial, Helvetica, sans-serif;
}
<!-- ref: https://jsclipper.sourceforge.net/6.4.2.2/index.html?p=sources_as_text/basic_demo_offset_svg.txt -->

<html>

<head>
  <title>Javascript Clipper Library / Offsetting polygons / SVG example</title>
  <!-- <script src="clipper.js"></script> -->
  <script src="https://cdn.jsdelivr.net/npm/clipper-lib@6.4.2/clipper.min.js"></script>

</head>

<body onload="draw()">
  <h2>Javascript Clipper Library / Offsetting polygons / SVG example</h2>
  This page shows an example of offsetting polygons and drawing them using SVG.
  <div id="svgcontainer"></div>
</body>

</html>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接