在 Javascript 中偏移多边形

问题描述 投票:0回答:4

EDIT5:终于用 Javascript 实现了 Angus Johnson 的 Clipper 库,并选择了 Sourceforge 作为主机。

现场演示:http://jsclipper.sourceforge.net/6.1.1.1/main_demo.html

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

包含分步教程的维基页面: https://sourceforge.net/p/jsclipper/wiki/Home%206/

演示程序演示,包括数十个示例多边形: https://sourceforge.net/p/jsclipper/wiki/Main_Demo%206/

我希望这可以帮助任何需要具有偏移功能的折线和多边形裁剪库的人。


EDIT4:一种可能性是将 pascal 转换为 javascript 使用 http://p2js.gelicon.biz/en/。还没有成功。

p2js.exe clipper.pas
给出致命错误“找不到 Clipper 使用的单位系统”。


编辑:我找到了script#Github),它似乎能够将C#转换为Javascript。 Clipper lib 在 C# 中可用,那么是否可以使用 Script# 进行 C#->JS 转换以及如何进行?

EDIT3:没有用 script# 进行转换,但也有 Emscripten,但 4000 cpp 行转换为 300 000 Javascript 行,所以不是一个选项。手动转换似乎才是王道。


EDIT2:我做了一个例子,它显示了问题。使用左右箭头应用偏移。在一定距离内它工作正常,但随后就会出现问题。黄色描边多边形被称为原始偏移多边形,据我所知 Clipper lib 提供了一种方法来删除原始偏移多边形中不需要的部分。


Angus Johnson 有一个 Clipper 库,用于偏移多边形。

我需要 Javascript 中的此功能来偏移 SVG 多边形。

有人做了一个 Javascript 移植吗?

如果没有,我会很感激一些指导方针,例如。以下:
- 这将是多么艰巨的任务?
- 选择哪一个作为源代码(Delphi、C#、C++)?
- lib 中的所有内容都需要用于抵消吗?

Clipper 库产生以下结果,这正是所需的功能:

Offset Polygons, polygons, delta, jointype, miterlimit, jtSquare jtRound jtMiter

一些链接:
- Sourceforge 中的文件
- Clipper 文档
- 一个 Stackoverflow 答案
- 偏移算法

javascript svg geometry script#
4个回答
4
投票

我已经成功将 Clipper 移植到 JS,过了一段时间,经过彻底的测试后,将发布它。似乎所有功能都可以移植。

需要注意的是,128 位支持已降至 106 位。

优点之一是可以使用较大的浏览器空间,并且可以使用 svg、vml、html5 canvas 作为图形界面。

有什么想法吗?哪个主机最容易发布,并且可以进行演示?


编辑:

终于用Javascript实现了Angus Johnson的Clipper库,并选择了Sourceforge作为宿主。

现场演示: http://jsclipper.sourceforge.net/6.1.1.1/main_demo.html

下载: https://sourceforge.net/projects/jsclipper/

包含分步教程的维基页面: 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;
    }

2
投票

对于多边形膨胀,没有简单的解决方案。如果你有一个凹多边形,如果你足够减小偏移量,它迟早会分裂成几个较小的多边形。所以我建议使用现有的、经过验证的算法(Clipper 应该是一个很好的算法)。

关于你关于将C#移植到JS的问题,我想说这当然是可能的,但问题是需要多少时间以及自动移植工具是否有任何用处。从这个讨论来看,我对此表示怀疑:

我快速尝试使用 ScriptSharp 将 C# 代码转换为 Javascript,但有太多不兼容的结构无法使用 我无法让它输出 javascript 文件。尝试实施 Javascript 中的 Vatti 裁剪算法似乎是下一步。

...

是的,它不会帮助您使用各种自动转换 工具。裁剪器具有像 Int64 或 Int128 这样的数据结构 JS 或 AS 中不存在。我刚刚完全删除了它们。Int32 应该 对于大多数情况来说已经足够了,除非你从事与地理相关的工作 或巨大的地图。

不幸的是,其中一位用户提到的 ActionScript 端口不再可用。


0
投票

下面是这里提到的Angus Johnson代码的工作示例。它使用 Javascript Clipper,它是 Angus 代码的端口。

这是从这里复制的,作为参考,以防网站网址自旧版本以来发生变化。

// 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/[email protected]/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>

© www.soinside.com 2019 - 2024. All rights reserved.