JavaScript-Clipper.js


Clipper.js下載地址:https://sourceforge.net/projects/jsclipper/files/latest/download
使用Javascript Clipper庫,您可以通過多種方式修改路徑(多邊形和折線)的幾何形狀。

特點:

  1. 主要布爾運算:和、相交、差和或。

  2. 用正數或負數抵消路徑。

其他功能包括:

  3. 簡化多邊形,這意味着將自相交的多邊形轉換為簡單的多邊形。執行此操作后,將具有自相交部分的面分解為多個簡單面。

  4. 計算多邊形的面積。

  5. 清潔多邊形。合並得太靠近頂點,這會在偏移時導致變形。

  6. 減輕多邊形。通過刪除不必要的頂點來減少頂點數量。

  7. 計算多邊形的Minkowski和和差。

  8. 計算路徑的周長。

翻譯說明

  這些功能與原始Clipper庫中的功能相同,但有以下區別:

    例如,某些PolyTree和PolyNode屬性被實現為函數。PolyTree.Total是PolyTree.Total()。

    C#的Int128結構是使用Tom Wu的大整數庫JSBN來實現的,這是Javascript中最快的可用大整數庫。由於Javascript缺乏64位整數支持,因此與C#版本相比,坐標空間受到的限制更大。

//  原始 (ç#)版本 具有 支持 用於 坐標 空間:
 + - 4611686018427387903  ( SQRT (2 ^ 127  - 1 )/ 2//  而 使用Javascript  版本 具有 支持 用於 坐標 空間:
 + - 4503599627370495  ( SQRT (2 ^ 106  - 1 )/ 2 )

    另外,由於Javascript中沒有整數類型,因此必須確保在調用ClipperLib.IntPoint()時,使用Math.round()將參數值舍入為整數。

    ClipperLib.JS對象提供用於計算面積,邊界和周長,清理,克隆,縮小頂點(lighten)和exPolygons相關函數的函數。

 A. 路徑的布爾運算

  首先,為忙碌的你們准備一個實例:

<!DOCTYPE html>
<html>
<head>
    <title>Starter Boolean</title>
    <script src="clipper.js"></script>
</head>
<body>
<div id="svgcontainer"></div>
<script>
    var subj_paths = [[{X:10,Y:10},{X:110,Y:10},{X:110,Y:110},{X:10,Y:110}],
        [{X:20,Y:20},{X:20,Y:100},{X:100,Y:100},{X:100,Y:20}]];
    var clip_paths = [[{X:50,Y:50},{X:150,Y:50},{X:150,Y:150},{X:50,Y:150}],
        [{X:60,Y:60},{X:60,Y:140},{X:140,Y:140},{X:140,Y:60}]];

    var cpr = new ClipperLib.Clipper();

    var scale = 100;
    ClipperLib.JS.ScaleUpPaths(subj_paths, scale);
    ClipperLib.JS.ScaleUpPaths(clip_paths, scale);

    cpr.AddPaths(subj_paths, ClipperLib.PolyType.ptSubject, true);  // true意味着閉合路徑
    cpr.AddPaths(clip_paths, ClipperLib.PolyType.ptClip, true);

    var solution_paths = new ClipperLib.Paths();
    var succeeded = cpr.Execute(ClipperLib.ClipType.ctUnion, solution_paths, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero);

    // 按比例縮小坐標並繪圖
    var svg = '<svg style="background-color:#dddddd" width="160" height="160">';
    svg += '<path stroke="black" fill="yellow" stroke-width="2" d="' + paths2string(solution_paths, scale) + '"/>';
    svg += '</svg>';
    document.getElementById("svgcontainer").innerHTML = svg;

    // 將路徑轉換為SVG路徑字符串,按比例縮小坐標
    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;
    }
    var paths = [[{"X":10,"Y":10},{"X":110,"Y":10},{"X":110,"Y":110},{"X":10,"Y":110}]];
    console.log(JSON.stringify(paths));
    ClipperLib.Clipper.ReversePaths(paths);
    console.log(JSON.stringify(paths));
</script>
</body>
</html>

在上圖中,兩個黃色框(外部和孔多邊形)合並為一種形狀(一個具有三個孔的外部多邊形)。

然后為需要更多信息的任何人提供更詳細的分步說明:

A1:引入clipper庫

<script src="clipper.js"></script>

A2: 創建路徑

  有兩種類型的路徑,Subject(主題)和 Clip(剪輯)。在Union(聯合)Intersection(相交)中,不管哪個是subject哪個是clip,結果都是一樣的。DifferenceXor中,結果不同。

  有兩種(或更多)創建路徑的方法,我先搞一個復雜的弄一下原理,然后顯示一個簡單的。

A2.1:創建 Subject 路徑

  創建路徑的主要方法是: ClipperLib.Path()ClipperLib.Paths()ClipperLib.IntPoint()

var subj_paths = new ClipperLib.Paths();
var subj_path = new ClipperLib.Path();
subj_path.push(
  new ClipperLib.IntPoint(0, 0),
  new ClipperLib.IntPoint(100, 0),
  new ClipperLib.IntPoint(100, 100),
  new ClipperLib.IntPoint(0, 100));
subj_paths.push(subj_path);

A2.2: 創建 clip 路徑  

var clip_paths = new ClipperLib.Paths();
var clip_path = new ClipperLib.Path();
clip_path.push(
  new ClipperLib.IntPoint(0, 0),
  new ClipperLib.IntPoint(100, 0),
  new ClipperLib.IntPoint(100, 100),
  new ClipperLib.IntPoint(0, 100));
clip_paths.push(clip_path);

A2.3:多邊形的孔

  如果你想添加一個洞,這是不可能只使用一個多邊形。你必須創建至少兩個子多邊形,一個用於外層,一個用於空穴。外多邊形的纏繞順序。非空穴)必須與空穴多邊形的纏繞順序相反。您可以根據需要添加任意多的子多邊形。

A2.4:太復雜了?

  或者,您可以創建更簡單(和更快)的路徑,因為ClipperLib.Path()和ClipperLib.Paths()是數組。

  下面的代碼創建了兩個Path,subj_paths和clip_paths,它們都由兩個子路徑組成。第二個是一個洞。

var subj_paths = [[{X:10,Y:10},{X:110,Y:10},{X:110,Y:110},{X:10,Y:110}],
                      [{X:20,Y:20},{X:20,Y:100},{X:100,Y:100},{X:100,Y:20}]]; 
var clip_paths = [[{X:50,Y:50},{X:150,Y:50},{X:150,Y:150},{X:50,Y:150}],
                      [{X:60,Y:60},{X:60,Y:140},{X:140,Y:140},{X:140,Y:60}]];

A3:創建 Clipper 對象的實例

var cpr = new ClipperLib.Clipper();

A4:縮放坐標(若有需要)

  因為Clipper庫將坐標處理為整數,所以您必須在將它們添加到Clipper之前將它們按比例放大(在繪圖之前將它們按比例縮小)。通過遍歷路徑數組很容易實現縮放。

var scale = 100;
ClipperLib.JS.ScaleUpPaths(subj_paths, scale);
ClipperLib.JS.ScaleUpPaths(clip_paths, scale);
// 使用 ClipperLib.js.ScaleUpPath (如果你有單個Path)

注意!將來的Clipper版本中可能會刪除此縮放要求,並在內部進行處理。

A5:添加路徑到Clipper

  使用 AddPaths() 方法向 Clipper (對象)添加 Subject 和 Clip 路徑。

cpr.AddPaths(subj_paths, ClipperLib.PolyType.ptSubject, true);
cpr.AddPaths(clip_paths, ClipperLib.PolyType.ptClip, true);

最后一個參數true表示路徑是閉合的(多邊形);false表示路徑是打開的(行)。Subject 可以打開或關閉或兩者的混合,但 Clip 必須關閉

A6:創建一個空的解決方案路徑

  我們需要一個持有者的布爾運算結果:一個空的路徑。

var solution_paths = new ClipperLib.Paths();

  或者更簡單:

var solution_paths = [];

A7:選擇ClipType和PolyFillType並執行剪切操作

A7.1:選擇ClipType

  選擇ClipType,這是實際的裁剪操作:

var clipType = ClipperLib.ClipType.ctIntersection;
// or simpler
var clipType = 0;

  剪貼類型可以是下列其中之一(或各自的數字0、1、2、3):

ClipperLib.ClipType.ctIntersection
ClipperLib.ClipType.ctUnion
ClipperLib.ClipType.ctDifference
ClipperLib.ClipType.ctXor

A7.2:選擇PolyFillType

  選擇PolyFillType,它告訴如何填充多邊形。PolyFillType在subject和clip多邊形中不一定是相同的,但是下面我們對兩者使用相同的。 

var subject_fillType = ClipperLib.PolyFillType.pftNonZero;
var clip_fillType = ClipperLib.PolyFillType.pftNonZero;

  或者更簡單的:

var subject_fillType = 1;
var clip_fillType = 1;

  該PolyFillType可以是下列其中之一(或各自的數字0、1、2、3):

ClipperLib.PolyFillType.pftEvenOdd
ClipperLib.PolyFillType.pftNonZero
ClipperLib.PolyFillType.pftPositive
ClipperLib.PolyFillType.pftNegative

注意!盡管Javascript裁剪器庫支持所有這些polyfilltype,但圖形后端可能只支持其中有限的子集。SVG格式支持EvenOdd(偶數)和NonZero(非零)。Html5 canvas目前似乎只有NonZero規則。

A7.3 執行布爾運算

var succeeded = cpr.Execute(clipType, solution_paths, subject_fillType, clip_fillType);

現在solution_paths由上述布爾運算的結果填充(在本例中是Intersection),可以在某處繪制(或修改或序列化以發送

 A8:繪制布爾運算的多邊形

  在網絡瀏覽器中,可以使用Javascript以多種方式繪制多邊形。我們在這里介紹了現代瀏覽器中使用的兩種流行方式:內聯SVG和canvas。其他可能性是使用Microsoft的VML或Walter Zorn的矢量圖形庫(盡管我不確定是否可以繪制帶有孔的多邊形)

  例如,有許多庫使用SVG,VML或canvas作為圖形后端。Raphael JS

A8.1 完整的代碼示例:使用SVG繪制布爾操作的多邊形 

  我們可以在SVG中以各種方式繪制多邊形。有一個本機<polygon>元素,但是缺點是它不支持孔和子多邊形。因此,更好的方法是使用<path>element。每個子多邊形被繪制為一個子路徑。每個子路徑均以M(moveTo)開頭,以Z(closePath)結尾。

  以下是對多邊形進行布爾運算並將其附加到SVG的完整示例。在這種情況下,我們將創建四個內聯SVG文檔,並在每個文檔上繪制一個布爾結果。

<html>
<head>
    <title>Javascript Clipper Library / Boolean operations / SVG example</title>
    <script src="clipper.js"></script>
    <style>
        h3{ margin-bottom:2px}
        body,th,td,input,legend,fieldset,p,b,button,select,textarea {
            font-size: 14px;
            font-family: Arial, Helvetica, sans-serif;
        }
    </style>
    <script>
        function draw() {
            var subj_paths = [[{X:10,Y:10},{X:110,Y:10},{X:110,Y:110},{X:10,Y:110}],
                [{X:20,Y:20},{X:20,Y:100},{X:100,Y:100},{X:100,Y:20}]];
            var clip_paths = [[{X:50,Y:50},{X:150,Y:50},{X:150,Y:150},{X:50,Y:150}],
                [{X:60,Y:60},{X:60,Y:140},{X:140,Y:140},{X:140,Y:60}]];
            var scale = 100;
            ClipperLib.JS.ScaleUpPaths(subj_paths, scale);
            ClipperLib.JS.ScaleUpPaths(clip_paths, scale);
            var cpr = new ClipperLib.Clipper();
            cpr.AddPaths(subj_paths, ClipperLib.PolyType.ptSubject, true);
            cpr.AddPaths(clip_paths, ClipperLib.PolyType.ptClip, true);
            var subject_fillType = ClipperLib.PolyFillType.pftNonZero;
            var clip_fillType = ClipperLib.PolyFillType.pftNonZero;
            var clipTypes = [ClipperLib.ClipType.ctUnion, ClipperLib.ClipType.ctDifference, ClipperLib.ClipType.ctXor, ClipperLib.ClipType.ctIntersection];
            var clipTypesTexts = "Union, Difference, Xor, Intersection";
            var solution_paths, svg, cont = document.getElementById('svgcontainer');
            var i;
            for(i = 0; i < clipTypes.length; i++) {
                solution_paths = new ClipperLib.Paths();
                cpr.Execute(clipTypes[i], solution_paths, subject_fillType, clip_fillType);
                console.log(JSON.stringify(solution_paths));
                svg = '<svg 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(solution_paths, scale) + '"/>';
                svg += '</svg>';
                cont.innerHTML += svg;
            }
            cont.innerHTML += "<br>" + clipTypesTexts;
        }

        // 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;
        }
    </script>
</head>
<body onload="draw()">
<h2>Javascript Clipper Library / Boolean operations / SVG example</h2>
This page shows an example of boolean operations on polygons and drawing them using SVG.
<div id="svgcontainer"></div>
</body>
</html>

  執行完上面的代碼后,您應該在svgcontainer元素中具有四個剪切的多邊形作為SVG路徑,如下圖所示。

 

 A8.2 完整的代碼示例:使用<canvas>繪制布爾操作的多邊形

  畫布不同於SVG。我們不能像在SVG中那樣使用文本字符串。路徑被繪制為命令moveTo()lineTo()。注意!繪制后,無法像在SVG中一樣對其進行修改。如果要更新某些內容,則需要重新繪制整個畫布。

<html>
<head>
    <title>Javascript Clipper Library / Boolean operations / Canvas example</title>
    <script src="clipper.js"></script>
    <style>
        h3{ margin-bottom:2px}
        body,th,td,input,legend,fieldset,p,b,button,select,textarea {
            font-size: 14px;
            font-family: Arial, Helvetica, sans-serif;
        }
    </style>
    <script>
        function draw() {
            var subj_paths = [[{X:10,Y:10},{X:110,Y:10},{X:110,Y:110},{X:10,Y:110}],
                [{X:20,Y:20},{X:20,Y:100},{X:100,Y:100},{X:100,Y:20}]];
            var clip_paths = [[{X:50,Y:50},{X:150,Y:50},{X:150,Y:150},{X:50,Y:150}],
                [{X:60,Y:60},{X:60,Y:140},{X:140,Y:140},{X:140,Y:60}]];
            var scale = 100;
            ClipperLib.JS.ScaleUpPaths(subj_paths, scale);
            ClipperLib.JS.ScaleUpPaths(clip_paths, scale);
            var cpr = new ClipperLib.Clipper();
            cpr.AddPaths(subj_paths, ClipperLib.PolyType.ptSubject, true);
            cpr.AddPaths(clip_paths, ClipperLib.PolyType.ptClip, true);
            var subject_fillType = ClipperLib.PolyFillType.pftNonZero;
            var clip_fillType = ClipperLib.PolyFillType.pftNonZero;
            var clipTypes = [ClipperLib.ClipType.ctUnion, ClipperLib.ClipType.ctDifference, ClipperLib.ClipType.ctXor, ClipperLib.ClipType.ctIntersection];
            var clipTypesTexts = "Union, Difference, Xor, Intersection";
            var solution_paths, canvas_str, canvas, ctx, desc = document.getElementById('desc'), i, i2, j, x, y;
            for(i = 0; i < clipTypes.length; i++) {
                solution_paths = new ClipperLib.Paths();
                cpr.Execute(clipTypes[i], solution_paths, subject_fillType, clip_fillType);
                //console.log(JSON.stringify(solution_paths));
                canvas = document.getElementById("canvas"+i);
                canvas.width = canvas.height = 160;
                ctx = canvas.getContext("2d");
                ctx.fillStyle = "red";
                ctx.strokeStyle = "black";
                ctx.lineWidth = 2;
                ctx.beginPath();
                for(i2 = 0; i2 < solution_paths.length; i2++) {
                    for(j = 0; j < solution_paths[i2].length; j++) {
                        x = solution_paths[i2][j].X / scale;
                        y = solution_paths[i2][j].Y / scale;
                        if (!j) ctx.moveTo(x, y);
                        else ctx.lineTo(x, y);
                    }
                    ctx.closePath();
                }
                ctx.fill();
                ctx.stroke();
            }
            desc.innerHTML = clipTypesTexts;
        }
    </script>
</head>
<style>
    canvas { background-color:#dddddd; margin:0px; margin-right:10px; margin-bottom:10px; }
</style>
<body onload="draw()">
<h2>Javascript Clipper Library / Boolean operations / Canvas example</h2>
This page shows an example of boolean operations on polygons and drawing them on html5 canvas as bitmap images.
<br><br>
<div id="canvascontainer">
    <canvas id="canvas0"></canvas>
    <canvas id="canvas1"></canvas>
    <canvas id="canvas2"></canvas>
    <canvas id="canvas3"></canvas>
</div>
<div id="desc"></div>
</body>
</html>

當您執行的代碼,你會看到畫布結果與SVG很相似:


 B:抵消路徑

   Clipper庫的另一個主要功能是多邊形和折線偏移。其目的是通過一定量來增添(粗化,膨脹,擴張,正偏移)或細化(縮小,削弱,負偏移)多邊形或使折線變寬。

  在B部分中,我們首先提供用於補償的快速入門代碼,然后提供詳細的逐步說明,最后提供更復雜的完整代碼示例。

  首先,為忙碌的你們提供一個簡單的快速入門實例:

<html>
<head>
    <title>Starter Offset</title>
    <script src="clipper.js"></script>
</head>
<body>
<div id="svgcontainer"></div>
<script>
    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);
    // Possibly ClipperLib.Clipper.SimplifyPolygons() here
    // Possibly ClipperLib.Clipper.CleanPolygons() here
    var co = new ClipperLib.ClipperOffset(2, 0.25);

    // ClipperLib.EndType = {etOpenSquare: 0, etOpenRound: 1, etOpenButt: 2, etClosedPolygon: 3, etClosedLine : 4 };
    co.AddPaths(paths, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
    var offsetted_paths = new ClipperLib.Paths();
    co.Execute(offsetted_paths, -10 * scale);
    //console.log(JSON.stringify(offsetted_paths));

    // Scale down coordinates and draw ...
    var svg = '<svg style="background-color:#dddddd" width="160" height="160">';
    svg += '<path stroke="black" fill="yellow" stroke-width="2" d="' + paths2string(offsetted_paths, scale) + '"/>';
    svg += '</svg>';
    document.getElementById("svgcontainer").innerHTML = svg;

    // 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;
    }
</script>

</body>
</html>

  在上圖中,將幀(帶孔的外部多邊形)細化了10個單位。

B1:引入 Clipper庫

<script src="clipper.js"></script>

B2:創建路徑

首先,我們創建一個將被偏移的路徑。 

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}]];

上面的路徑是多路徑的。由兩個子路徑組成,外層路徑和一個孔。

B3:放大多邊形坐標

因為Clipper庫使用“整數”坐標,所以我們必須按比例放大坐標以保持精度。標度值應足夠大,但出於性能方面的考慮,不要過高。如果坐標的精度為2位小數,則足夠的比例系數為100

注意!盡管以上是主要原理,但在確定合適的比例因子時請小心。如果坐標原本是整數,則用系數1縮放可能並不總是足夠的(即根本沒有放大)。如果joinTypejtRound並且未完成放大,則整數不能表示結果的圓角並不總是正確的。最安全的方法是始終至少放大10或100。

var  scale  =  100 ; 
ClipperLib.JS.ScaleUpPaths(paths, scale); 
// 使用 ClipperLib.JS.ScaleUpPath()( 如果具有單個Path)

B4:簡化和清潔

B4.1 簡化

  偏移的要求是多邊形沒有自相交,盡管它們在所有情況下都不會引起問題。

   為了將復雜的多邊形轉換為簡單的多邊形,Clipper庫提供了SimplifyPolygon()SimplifyPolygons()方法。復雜多邊形表示具有自相交的多邊形,簡單多邊形表示相反。

  簡化是可選的。如果多邊形沒有自相交,則不需要此步驟。如果可能,則應在偏移之前簡化多邊形。簡化確定性並不是一個壞錯誤。

  如果路徑是開放的(折線),則無需簡化,因為如果折線自身交叉,則沒有危害。

要刪除自相交,請使用:

paths = ClipperLib.Clipper.SimplifyPolygons(paths, ClipperLib.PolyFillType.pftNonZero);
                                             // or ClipperLib.PolyFillType.pftEvenOdd

B4.2 清潔

Clipper簡化功能(或更具體地講是內部Union操作)在較小的自相交方面具有局限性。它無法刪除所有這些文件。它本來應該,但是目前還不能。原始庫的創建者已經意識到了這個問題,但是到目前為止還沒有提供解決方案,而是提供了一個CleanPolygon()合並了太近點的函數,似乎可以解決該問題。

下圖顯示了該問題的示例。左側和右側都有使用joinType jtMiter和偏移的多邊形MiterLimit。右側已使用Clean()函數進行了清理,並且失真消失了。當失真是壞的joinTypejtMiterMiterLimit大,但顯然也明顯與其他joinTypes。在某些情況下,增加縮放比例可能會有所幫助,但不是解決方案,因為它還會增加處理時間,因此不是一般解決方案。

 要刪除較小的自相交,請使用:

var cleandelta = 0.1; // 在不同的情況下,0.1應該是合適的增量
paths = ClipperLib.JS.Clean(paths, cleandelta * scale);

內部Clean()使用“ 徑向歸約”算法,該算法可以合並順序的頂點(如果它們彼此之間處於一定距離或相距一定距離)

注意!Clipper中還有清潔功能:CleanPolygonCleanPolygons。它們與Clean()不同,因為它們還合並了共線的頂點:

var cleandelta = 0.1; // 在不同的情況下,0.1應該是合適的增量
paths = ClipperLib.Clipper.CleanPolygons(paths, cleandelta * scale);

B5:創建ClipperOffset對象的實例

  ClipperOffset對象封裝了打開和關閉路徑的偏移(膨脹/放氣)過程。

  可以使用不同的偏移量(Delta)多次調用Execute方法,而不必重新分配路徑。可以在單個操作中混合使用開放路徑和封閉路徑來執行偏移 

  如果尚未創建,請使用以下方法創建ClipperOffset對象的實例:

var miterLimit = 2;
var arcTolerance = 0.25;
var co = new ClipperLib.ClipperOffset(miterLimit, arcTolerance);

另請參見文檔:ClipperOffsetArcToleranceMiterLimit

B6:添加路徑

添加ClipperOffset使用AddPathAddPaths方法的路徑:

co.AddPaths(paths, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon); 

B7:創建一個空解決方案並執行偏移操作

現在可以使用delta值執行偏移操作,可以是負數,也可以是正數:

var delta = -10;
var offsetted_paths = new ClipperLib.Paths();
co.Execute(offsetted_paths, delta*scale);

 或者如果你想要PolyTree:

var delta = -10;
var offsetted_polytree = new ClipperLib.PolyTree();
co.Execute(offsetted_polytree, delta*scale);

就是這樣。現在,offsetted_paths或offsetted_polytree由補償操作的結果填充,在本例中為-10 * scale。請記住,在繪制這個之前,坐標必須按比例因子縮小,因為結果集中的坐標總是“整數”。

注意!結果可能是空的,或者有比原來更多或更少的子路徑。

注意!如果joinType是jtRound,則結果中的圓角表示為直線。

B8:繪制偏移的多邊形

正如我們在布爾運算多邊形的示例中所做的那樣,我們將提供偏移多邊形的完整功能示例,並將其繪制在現代瀏覽器中的兩個流行圖形后端上:內聯SVG和canvas。

B8.1 完整代碼示例:使用svg繪制偏移的多邊形

<html>
<head>
    <title>Javascript Clipper Library / Offsetting polygons / SVG example</title>
    <script src="clipper.js"></script>
    <style> h3{ margin-bottom:2px} body,th,td,input,legend,fieldset,p,b,button,select,textarea { font-size: 14px; font-family: Arial, Helvetica, sans-serif;
        }
    </style>
    <script>
        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 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; } </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>

當“和”是在SVG上繪制后的結果時,偏移的多邊形如下所示:

 B8.2 完整的代碼示例:使用畫布繪制偏移的多邊形

<html>
<head>
    <title>Javascript Clipper Library / Offsetting polygons / Canvas example</title>
    <script src="clipper.js"></script>
    <style>
        h3{ margin-bottom:2px}
        body,th,td,input,legend,fieldset,p,b,button,select,textarea {
            font-size: 14px;
            font-family: Arial, Helvetica, sans-serif;
        }
    </style>
    <script>
        function draw() {
            var canvas, ctx, i2, ii, i, j, x, y;
            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
            co.AddPaths(paths, true); // we add paths only once
                                      // true means closed paths (polygons)
            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));

                    canvas = document.getElementById("canvas" + i + "." + ii);
                    canvas.width = canvas.height = 160;
                    ctx = canvas.getContext("2d");
                    ctx.fillStyle = "red";
                    ctx.strokeStyle = "black";
                    ctx.lineWidth = 2;
                    ctx.beginPath();
                    for(i2 = 0; i2 < offsetted_paths.length; i2++) {
                        for(j = 0; j < offsetted_paths[i2].length; j++) {
                            x = offsetted_paths[i2][j].X / scale;
                            y = offsetted_paths[i2][j].Y / scale;
                            if (!j) ctx.moveTo(x, y);
                            else ctx.lineTo(x, y);
                        }
                        ctx.closePath();
                    }
                    ctx.fill();
                    ctx.stroke();
                }
                document.getElementById('desc' + i).innerHTML = joinTypesTexts[i] + " " + deltas[0] + ", " + joinTypesTexts[i] + " " + deltas[1] + ", " + joinTypesTexts[i] + " " + deltas[2];
            }
        }
    </script>
    <style>
        canvas { background-color:#dddddd; margin:0px; margin-right:10px; margin-bottom:10px; }
    </style>
</head>
<body onload="draw()">
<h2>Javascript Clipper Library / Offsetting polygons / Canvas example</h2>
This page shows an example of offsetting polygons and drawing them on html5 canvas as bitmap images.
<br><br>
<canvas id="canvas0.0"></canvas>
<canvas id="canvas0.1"></canvas>
<canvas id="canvas0.2"></canvas>
<div id="desc0"></div>
<hr>
<canvas id="canvas1.0"></canvas>
<canvas id="canvas1.1"></canvas>
<canvas id="canvas1.2"></canvas>
<div id="desc1"></div>
<hr>
<canvas id="canvas2.0"></canvas>
<canvas id="canvas2.1"></canvas>
<canvas id="canvas2.2"></canvas>
<div id="desc2"></div>
<hr>
</body>
</html>


 C:對ExPolygons和PolyTree進行布爾運算

有時您需要知道哪個孔屬於哪個輪廓。例如,就是這種情況。在用於3D程序的三角剖分中。使用Polygons結構是不可能的。答案是ExPolygons或PolyTree結構。

ExPolygon是一種“擴展”多邊形結構,它將外部多邊形輪廓與所有包含的多邊形孔封裝在一起。請閱讀有關ExPolygons和PolyTree的布爾運算的更多信息。


 D:將Web Worker與Clipper一起使用

Clipper可以處理非常復雜的路徑,並且頁面更新可能需要幾秒鍾。在這些情況下,使用Web Worker避免瀏覽器掛起可能是明智的。請閱讀有關將Web Workers與Clipper一起使用的更多信息。


 Lightening Paths

在許多情況下,多邊形和折線的頂點過多,這會導致工程圖過載。這是例如。當位圖追溯到矢量圖形,制圖時以及使用Clipper通過偏移多邊形時,為true joinType jtRound

為了刪除不必要的頂點,我們提供了一種方法ClipperLib.Lighten()。它采用垂直歸約算法;如果與當前三點序列的起點和終點之間的線的垂直距離等於或小於一定公差(距離),則遍歷所有頂點並移除三個連續頂點的中間頂點。

要減少頂點,請使用:

var tolerance = 0.1;
polygons = ClipperLib.JS.Lighten(polygons, tolerance * scale);

tolerance上面是負的或正Number或零。如果為負,則返回原始路徑的克隆。越高tolerance,將刪除更多的頂點。

注意!約簡算法不是最適合較大公差值的算法,因為它非常局部地(按三個頂點的順序)確定邊界線,並且不會在添加可以提高原始保真度的位置添加新頂點。順便說一句,它很快。如果保真度tolerance較低,則保真度是理想的。0.1-0.2單位。


 計算多邊形的面積

要獲取多邊形的面積,Javascript Clipper庫提供AreaOfPolygon()並提供以下AreaOfPolygons()功能:

var area = ClipperLib.JS.AreaOfPolygon(polygon);

要獲得多邊形的面積,請使用以下命令:

var area = ClipperLib.JS.AreaOfPolygons(polygons);

 計算多邊形的周長

為了獲取多邊形的周長,Javascript Clipper庫提供PerimeterOfPath()並提供以下PerimeterOfPaths()功能:

var polygonal_perimeter = ClipperLib.JS.PerimeterOfPath(path, true, 1);

要獲得多邊形的周長,請使用以下命令:

var polygonal_perimeter = ClipperLib.JS.PerimeterOfPaths(paths, true, 1);

上面的兩個示例計算了多邊形的周長,這意味着該周長是從第一個點到第一個點進行測量的,而不管最后一個點是否與第一個點相同。如果要測量線的周長,請使用以下方法:

var line_perimeter = ClipperLib.JS.PerimeterOfPath(path, false, 1);

要獲得多邊形的周長,請使用以下命令:

var line_perimeter = ClipperLib.JS.PerimeterOfPaths(paths, false, 1);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM