Donate Javascript Clipper Project
Javascript translation of Angus Johnson's C# Clipper Library
With Javascript Clipper library you can modify polygon geometry in various ways. The main functionalities are:
A) Principal boolean operations: Union, Intersection, Difference and Xor.
B) Offsetting polygons with positive or negative amount. Other terms are inflating/deflating, dilating/eroding.
The other functionalities are:
C) Simplifying polygons, which means that selfintersecting polygons are converted to simple polygons. After this operation polygon that has selfintersecting parts is split into multiple simple polygons.
D) Calculating area of polygons.
E) Cleaning polygons. Merges too near vertices which causes distortion when offsetting.
F) Lightening polygons. Reduces count of vertices by removing unnecessary vertices.
In this wiki we will cover all the above mentioned features of this library and provide both step-by-step instructions and full functional sample codes.
Please check the LIVE DEMO in http://jsclipper.sourceforge.net/5.0.2.3/main_demo.html
You may want to take a look at the wiki of Main Demo. There are plenty of screen captures and description of interesting Polygon Explorer feature of the Main Demo.
The features are the same as in original Clipper library with the following differences:
Implemented Clean() and Lighten() that are not introduced in the original Clipper library.
Int128 struct of C# is implemented using big integer library JSBN of Tom Wu, which is the fastest of available big integer libraries in Javascript. Because of Javascript lack of 64-bit integer support, the coordinate space is a little more restricted than in C# version.
// Original (C#) version has support for coordinate space:
+-4611686018427387903 ( sqrt(2^127 -1)/2 )
// while Javascript version has support for coordinate space:
+-4503599627370495 ( sqrt(2^106 -1)/2 )
~~~~~
Author: Timo
-----
## A. Boolean operations of polygons
### A1. Include library
Include Clipper library using
~~~~~
:::html
<script src="clipper.js"></script>
~~~~~
#####
### A2. Create polygons
There are two types of polygons: Subject and Clip. In **Union** and **Intersection** the result is the same despite of which is subject and which is clip polygon. In **Difference** and **Xor** the result differs.
There are two (or more) ways to create polygons. We show first the complicated one (that you'll understand the principle) and then the simpler one.
#### A2.1 Create subject polygon
The main methods in creating polygons are: `ClipperLib.Polygons()`, `ClipperLib.Polygon()`, `ClipperLib.IntPoint()`.
~~~~~~
:::javascript
var subj_polygons = new ClipperLib.Polygons();
var subj_polygon = new ClipperLib.Polygon();
subj_polygon.push(new ClipperLib.IntPoint(0, 0),
new ClipperLib.IntPoint(0, 0),
new ClipperLib.IntPoint(100, 0),
new ClipperLib.IntPoint(100, 100));
new ClipperLib.IntPoint(0, 100);
subj_polygons.push(subj_polygon);
~~~~~~
#####
#### A2.2 Create clip polygon
~~~~~
:::javascript
var clip_polygons = new ClipperLib.Polygons();
var clip_polygon = new ClipperLib.Polygon();
clip_polygon.push(new ClipperLib.IntPoint(10, 10),
new ClipperLib.IntPoint(110, 10),
new ClipperLib.IntPoint(110, 110),
new ClipperLib.IntPoint(10, 110));
clip_polygons.push(clip_polygon);
~~~~~
#####
#### A2.3 Holes in polygons
If you want to add a hole, it's not possible using only one polygon. You have to create at least two sub polygons, one for outer and one for hole. The winding order of outer polygon (ie. non-hole) has to be CW, and the winding order of hole polygon has to be CCW. You can add as many sub polygons as needed.
#### A2.4 Too complicated?
Alternatively you can create polygons more simpler way, because
ClipperLib.Polygons() and ClipperLib.Polygon() are arrays. The following code creates two Polygons, `subj_polygons` and `clip_polygons`, which both consist of two sub polygons. The second one of each is a hole.
~~~~~
:::javascript
var subj_polygons = [[{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_polygons = [[{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. Create Clipper object
Create an instance of Clipper object.
~~~~~
:::javascript
var cpr = new ClipperLib.Clipper();
~~~~~
#####
### A4. Scale coordinates if needed
Because Clipper library handles coordinates as integers, you have to scale them up before adding them to Clipper (and scale them back down before drawing). The scaling is done easily with traversing polygon arrays.
~~~~~
var scale = 100;
subj_polygons = scaleup(subj_polygons, scale);
clip_polygons = scaleup(clip_polygons, scale);
function scaleup(poly, scale) {
var i, j;
if (!scale) scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
poly[i][j].X *= scale;
poly[i][j].Y *= scale;
}
}
return poly;
}
~~~~~
####
**Note!** This scaling requirement may be removed in future Clipper version and handled internally.
### A5. Add polygons to Clipper
Add subject and clip polygons to Clipper (object) using `AddPolygons()` method.
~~~~~
:::javascript
cpr.AddPolygons(subj_polygons, ClipperLib.PolyType.ptSubject);
cpr.AddPolygons(clip_polygons, ClipperLib.PolyType.ptClip);
~~~~~
####
### A6. Create an empty solution polygon
We need a holder for boolean operation result: an empty polygon.
~~~~~
:::javascript
var solution_polygons = new ClipperLib.Polygons();
~~~~~
####
or simpler:
~~~~~
:::javascript
var solution_polygons = [[]];
~~~~~
####
### A7. Select ClipType and PolyFillType and execute clipping operation
#### A7.1 Select ClipType
Select the `ClipType`, which is the actual clipping operation:
var clipType = ClipperLib.ClipType.ctIntersection;
// or simpler
var clipType = 0;
####
The ClipType can be one of the following (or respective numbers 0, 1, 2, 3):
~~~~
ClipperLib.ClipType.ctIntersection
ClipperLib.ClipType.ctUnion
ClipperLib.ClipType.ctDifference
ClipperLib.ClipType.ctXor
~~~~
####
#### A7.2 Select PolyFillType
Select the `PolyFillType`, which tells how polygon is filled. The PolyFillType have not to be the same in subject and clip polygons, but below we use the same for both.
~~~~
var subject_fillType = ClipperLib.PolyFillType.pftNonZero;
var clip_fillType = ClipperLib.PolyFillType.pftNonZero;
~~~~
####
or simpler
~~~~
var subject_fillType = 1;
var clip_fillType = 1;
The PolyFillType can be one of the following (or respective numbers 0, 1, 2, 3):
ClipperLib.PolyFillType.pftEvenOdd
ClipperLib.PolyFillType.pftNonZero
ClipperLib.PolyFillType.pftPositive
ClipperLib.PolyFillType.pftNegative
Note! Although all those PolyFillTypes a supported by Javascript Clipper Library, the graphics backend may support only limited subset of those. EvenOdd and NonZero are supported by SVG format. Html5 canvas seems to have currently only NonZero rule.
var succeeded = cpr.Execute(clipType, solution_polygons, subject_fillType, clip_fillType);
That's it!
Now solution_polygons has got the result of the above boolean operation (Intersection in this case) and can be drawn (or modified more or serialized for sending) somewhere.
Polygons can be drawn in various ways using Javascript in web browsers. We cover here the two popular ways used in modern browsers: inline SVG and canvas. The other possibilities are using Microsoft's VML or Walter Zorn's Vector Graphics Library (although I'm not sure if it's possible to draw polygons with holes with it).
There are many libraries that use SVG, VML or canvas as graphical backend, eg. Raphaël JS.
We can draw polygons in various ways in SVG. There is native <polygon> element, but the disadvantage is that it does not support holes and sub polygons. So better way is to use <path> element. Each sub polygon is drawn as one subpath. Each subpath starts with M (moveTo) and ends with Z (closePath).
The following is a full example of making boolean operations to polygons and appending them to SVG. In this case we create four inline SVG documents and draw one boolean result on each one.
<html>
<head>
<title>Javascript Clipper Library / Boolean operations / SVG example</title>
<script src="clipper.js"></script>
<script>
function draw() {
var subj_polygons = [[{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_polygons = [[{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;
subj_polygons = scaleup(subj_polygons, scale);
clip_polygons = scaleup(clip_polygons, scale);
var cpr = new ClipperLib.Clipper();
cpr.AddPolygons(subj_polygons, ClipperLib.PolyType.ptSubject);
cpr.AddPolygons(clip_polygons, ClipperLib.PolyType.ptClip);
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_polygons, svg, cont = document.getElementById('svgcontainer');
var i;
for(i = 0; i < clipTypes.length; i++) {
solution_polygons = new ClipperLib.Polygons();
cpr.Execute(clipTypes[i], solution_polygons, subject_fillType, clip_fillType);
//console.log(JSON.stringify(solution_polygons));
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="' + polys2path(solution_polygons, scale) + '"/>';
svg += '</svg>';
cont.innerHTML += svg;
}
cont.innerHTML += "<br>" + clipTypesTexts;
}
// helper function to scale up polygon coordinates
function scaleup(poly, scale) {
var i, j;
if (!scale) scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
poly[i][j].X *= scale;
poly[i][j].Y *= scale;
}
}
return poly;
}
// converts polygons to SVG path string
function polys2path (poly, scale) {
var path = "", i, j;
if (!scale) scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
if (!j) path += "M";
else path += "L";
path += (poly[i][j].X / scale) + ", " + (poly[i][j].Y / scale);
}
path += "Z";
}
return path;
}
</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>
Now you should have four clipped polygons as SVG paths in your svgcontainer
element like the image below shows.

<canvas>Canvas differs from SVG. We cannot use textual strings like in SVG. Paths are drawn as commands moveTo() and lineTo(). Note! after they are drawn they cannot be modified as in SVG. The whole canvas is needed to be redrawn if something is wanted to be updated.
<html>
<head>
<title>Javascript Clipper Library / Boolean operations / Canvas example</title>
<script src="clipper.js"></script>
<script>
function draw() {
var subj_polygons = [[{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_polygons = [[{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;
subj_polygons = scaleup(subj_polygons, scale);
clip_polygons = scaleup(clip_polygons, scale);
var cpr = new ClipperLib.Clipper();
cpr.AddPolygons(subj_polygons, ClipperLib.PolyType.ptSubject);
cpr.AddPolygons(clip_polygons, ClipperLib.PolyType.ptClip);
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_polygons, canvas_str, canvas, ctx, desc = document.getElementById('desc'), i, i2, j, x, y;
for(i = 0; i < clipTypes.length; i++) {
solution_polygons = new ClipperLib.Polygons();
cpr.Execute(clipTypes[i], solution_polygons, subject_fillType, clip_fillType);
//console.log(JSON.stringify(solution_polygons));
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_polygons.length; i2++) {
for(j = 0; j < solution_polygons[i2].length; j++) {
x = solution_polygons[i2][j].X / scale;
y = solution_polygons[i2][j].Y / scale;
if (!j) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.closePath();
}
ctx.fill();
ctx.stroke();
}
desc.innerHTML = clipTypesTexts;
}
// helper function to scale up polygon coordinates
function scaleup(poly, scale) {
var i, j;
if (!scale) scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
poly[i][j].X *= scale;
poly[i][j].Y *= scale;
}
}
return poly;
}
</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>
Canvas result is very similar with SVG:

The other main functionality in Clipper library is polygon offsetting. It's purpose is to fatten (bolden, inflate, dilate, positive offset) or thin (deflate, erode, negative offset) polygons by certain amount.
Here are step-by-step instructions of offsetting polygons.
If not included yet, include Clipper library using
:::html
<script src="clipper.js"></script>
First we create a polygon. In boolean operations we have to create two Polygon() objects, subject and clip one, but when offsetting, only one object is created.
var polygons = [[{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}]];
The polygons is above a multi-polygon, ie. consists of two sub polygons, the outer one and a hole.
Because Clipper library uses integer coordinates, we have to scale up coordinates to maintain precision. The scale value should be large enough, but for performance reasons not too high. If coordinates have a precision of 2 decimals, the sufficient scale coefficient is 100.
Note! Although main principle is the above, please be careful when deciding the proper scaling factor. If coordinates are originally integers, it may not always be sufficient to scale with coefficient 1 (eg. not to scale up at all). For example if joinType is jtRound and up-scaling is not done, then it's not always true that integers can represent the rounded corners of result. So safest is to always scale up at least by 10 or 100.
var scale = 100;
polygons = scaleup(polygons, scale);
function scaleup(poly, scale) {
var i, j;
if (!scale) scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
poly[i][j].X *= scale;
poly[i][j].Y *= scale;
}
}
return poly;
}
If not yet created, create an instance of Clipper object using:
var cpr = new ClipperLib.Clipper();
The requirement for Offsetting is that polygons are free of all self-intersections, although all of them doesn't cause problems in all cases.
To convert a complex polygon to a simple one, Clipper library provides Simplify() method. Complex polygon means a polygon that has self-intersections. Simple polygon means a polygon that has not such ones. Simplify means usually vertices reduction method, which try to maintain the characteristics (main shape) of polygon while reducing the count of vertices. On the contrary in Clipper Simplify means removing self-intersections.
Simplifying polygons is optional. If polygon has not self-intersections, then simplifying is not needed. If there may be, then you should simplify polygon before offsetting. It's not a bad mistake to simplify for certainty.
To remove self-intersections use:
polygons = cpr.SimplifyPolygons(polygons, ClipperLib.PolyFillType.pftNonZero);
// or ClipperLib.PolyFillType.pftEvenOdd
The Clipper Simplify feature (or more specifically the internal Union operation) has a limitation regarding to minor self-intersections. It can't remove all of them. It well should, but for the moment it can't. The creator of the original library is aware of this problem, but has not provided a solution so far, but instead provided a CleanPolygon() function which merges too-near points and the problem seems to be solved.
The following image shows an example of the problem. Both the left side and right side have polygons offsetted using joinType jtMiter and MiterLimit 100. The right one has cleaned using Clean() function and the distortion is disappeared. The distortion is bad when joinType is jtMiter and MiterLimit is big, but is clearly noticeable also with other joinTypes. Increasing the scaling factor may help in some cases, but is not a solution, because it increases also processing time and is not a general solution.

To remove minor self-intersections use:
var cleandelta = 0.1; // 0.1 should be the appropriate delta in different cases
polygons = ClipperLib.Clean(polygons, cleandelta * scale);
Internally Clean() uses Radial Reduction algorithm, which merges sequential vertices, if they are at or under certain distance of each other.
Note! Cleaning is not (yet) included in the original Clipper Library and the implementation may change in near future.
Offsetting method is OffsetPolygons(), which accepts the following parameters:
ArrayNumberClipperLib.JoinType.jtSquare, ClipperLib.JoinType.jtRound, ClipperLib.JoinType.jtMiter or respective numbers 0, 1 or 2Number equal or more than 1BooleanBelow we make an offset operation using joinType jtMiter.
var delta = -10 * scale
var joinType = ClipperLib.JoinType.jtMiter;
var miterLimit = 2;
var AutoFix = true;
var offsetted_polygon = cpr.OffsetPolygons(polygons, delta, joinType, miterLimit, AutoFix);
That's it. Now offsetted_polygon has got the result of offsetting operation, in this case thinned by -10 * scale. Please remember that before drawing this, the coordinates have to be scaled down by the scale factor, because the coordinates in result set are always as integers.
Note! The result may be an empty array or have more or less sub polygons than in the original.
Note! If joinType is jtRound, rounded corners in result are represented as lines.
As we made in the examples of boolean operated polygons, we'll present full functional examples of offsetting polygons and drawing them on two popular graphical backend in modern browsers: inline SVG and canvas.
This is the full functional example of offsetting polygons and drawing them using SVG.
<html>
<head>
<title>Javascript Clipper Library / Offsetting polygons / SVG example</title>
<script src="clipper.js"></script>
<script>
function draw() {
var polygons = [[{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;
polygons = scaleup(polygons, scale);
var cpr = new ClipperLib.Clipper();
var deltas = [-10, 0, 10];
var joinTypes = [ClipperLib.JoinType.jtSquare, ClipperLib.JoinType.jtRound, ClipperLib.JoinType.jtMiter];
var joinTypesTexts = ["Square", "Round", "Miter"];
var svg, offsetted_polygon, miterLimit, AutoFix,
cont = document.getElementById('svgcontainer'), i, ii, j;
for(i = 0; i < joinTypes.length; i++) {
for(ii = 0; ii < deltas.length; ii++) {
miterLimit = 2;
AutoFix = true;
offsetted_polygon = cpr.OffsetPolygons(polygons, deltas[ii] * scale, joinTypes[i], miterLimit, AutoFix);
//console.log(JSON.stringify(offsetted_polygon));
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="' + polys2path(offsetted_polygon, 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>";
}
}
// helper function to scale up polygon coordinates
function scaleup(poly, scale) {
var i, j;
if (!scale) scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
poly[i][j].X *= scale;
poly[i][j].Y *= scale;
}
}
return poly;
}
// converts polygons to SVG path string
function polys2path (poly, scale) {
var path = "", i, j;
if (!scale) scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++){
if (!j) path += "M";
else path += "L";
path += (poly[i][j].X / scale) + ", " + (poly[i][j].Y / scale);
}
path += "Z";
}
return path;
}
</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>
Offsetted polygons are as follows when drawn on SVG:

This is the full functional example of offsetting polygons and drawing them using canvas.
~~~~~
<title>Javascript Clipper Library / Offsetting polygons / Canvas example</title> <script src="clipper.js"></script> <script> function draw() { var polygons = [[{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; polygons = scaleup(polygons, scale); var cpr = new ClipperLib.Clipper(); var deltas = [-10, 0, 10]; var joinTypes = [ClipperLib.JoinType.jtSquare, ClipperLib.JoinType.jtRound, ClipperLib.JoinType.jtMiter]; var joinTypesTexts = ["Square", "Round", "Miter"]; var offsetted_polygon, miterLimit = 2, AutoFix = true; var canvas, ctx, i2, ii, i, j, x, y; for(i = 0; i < joinTypes.length; i++) { for(ii = 0; ii < deltas.length; ii++) { offsetted_polygon = cpr.OffsetPolygons(polygons, deltas[ii] * scale, joinTypes[i], miterLimit, AutoFix); //console.log(JSON.stringify(offsetted_polygon)); 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_polygon.length; i2++) { for(j = 0; j < offsetted_polygon[i2].length; j++) { x = offsetted_polygon[i2][j].X / scale; y = offsetted_polygon[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]; } } // helper function to scale up polygon coordinates function scaleup(poly, scale) { var i, j; if (!scale) scale = 1; for(i = 0; i < poly.length; i++) { for(j = 0; j < poly[i].length; j++) { poly[i][j].X *= scale; poly[i][j].Y *= scale; } } return poly; } </script> <style> canvas { background-color:#dddddd; margin:0px; margin-right:10px; margin-bottom:10px; } </style>####
And here is the result of this program:
[[img src="https://sourceforge.net/p/jsclipper/wiki/Image holder page/attachment/Screen shot 2012-12-11 at 4.21.12 AM.png" alt=""]]
----
## C. Boolean operation on ExPolygons
Sometimes you need to know, which hole belongs to which contour. This is the case eg. in triangulating polygons for use in 3D programs. This is not possible using Polygons structure. The answer is ExPolygons structure.
ExPolygon is an 'extended' polygon structure that encapsulates an outer polygon contour together with any contained polygon holes. Please read more about [Boolean operations on ExPolygons](https://sourceforge.net/p/jsclipper/wiki/ExPolygons/).
----
## D. Using Web Workers with Clipper
Clipper can handle very complex polygons, and page update can take several seconds. In these cases it may be wise to use Web Workers to avoid browser hanging. Please read more about [using Web Workers with Clipper](https://sourceforge.net/p/jsclipper/wiki/Web%20Workers/).
----
##Lightening Polygons
In many cases polygons have too much vertices, which causes overload in drawing. This is eg. true when bitmaps are traced to vector graphics, in cartography and also when polygons are offsetted using Clipper with `joinType` `jtRound`.
To remove unnecessary vertices, we provide a method `ClipperLib.Lighten()`. It uses <i>Perpendicular Reduction</i> algorithm; all the vertices are traversed and the middle vertex of three sequential vertices is removed, if it's perpendicular distance to the line between start and end point of current three point sequence is at or under certain tolerance (distance).
To reduce vertices, use:
var tolerance = 0.1;
polygons = ClipperLib.Lighten(polygons, tolerance * scale);
####
The `tolerance` above is negative or positive `Number` or zero. If negative, the clone of the original polygon is returned. The higher the `tolerance` is, the more vertices will be deleted.
**Note!** The reduction algorithm is not best suited for larger tolerance values, because it determines the polygon border line very locally (in sequences of three vertices) and doesn't add new vertices in places where the addition could improve the fidelity to the original. By the way, it is fast. And the fidelity is ideal, if the `tolerance` is rather low, eg. 0.1 - 0.2 units.
----
##Calculating Area of Polygon
To get the area of the polygon, the Clipper library provide `Area()` function:
var area = cpr.Area(polygon);
// or ClipperLib.Clipper.Area(polygon);
####
Area accepts a `Polygon` object as an argument, not `Polygons`. To get the area of multipolygon, use this:
var area = cpr.Area(polygon[0]);
area += cpr.Area(polygon[1]);
// or traverse using loop
~~~~
Javascript Clipper Library is dual licenced.
The actual Clipper Library is licenced as the original Angus Johnson's Clipper Library, which uses Boost Software Licence.
Javascript Clipper Library uses an implementation of the biginteger library JSBN of Tom Wu, which is licenced under a BSD license. See LICENSE for details.