Donate Javascript Clipper Project
In C# Clipper 6 there is not anymore ExPolygons structure, but in Javascript Clipper 6 we have implemented conversion functions that allow conversion from PolyTree to ExPolygons as well as from ExPolygons to Polygons as well as from PolyTree to Polygons. This decision is due to the fact that ExPolygons structure is simpler, more efficient and easily serializable.
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 Paths structure. The answer is ExPolygons or PolyTree structure.
ExPolygon is an 'extended' polygon structure that encapsulates an outer polygon contour together with any contained polygon holes. ExPolygon is a javascript object, that has two members: "outer" (Polygon structure ie array of points) and "holes" (Array of Polygon structures). "Outer" contains zero or one Polygon structures, but "holes" can contain zero, one or several Polygon structures.
The solution parameter in Clipper's overloaded Execute method can return either a Paths structure or a PolyTree structure, which can be converted to ExPolygons structure.
Note: The PolyTree structure is not used (or needed) for adding polygons to Clipper objects. The simpler Path structure is perfectly sufficient for that since inner 'hole' polygon contours are indicated by having their Orientation opposite that of outer container polygon contours. PolyTree and ExPolygons structures are provided simply for those users who need to have inner 'hole' polygons explicitly associated with their containers.
ExPolygons (Javascript Array) encapsulates one or a number of ExPolygon structures (Javascript Objects).
Using PolyTree and ExPolygons structure is nearly identical as using Paths structure (as described here). The procedure is as follows:
:::javascript
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;
scaleup(subj_paths, scale);
scaleup(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 clipType = ClipperLib.ClipType.ctUnion;
var solution_polytree = new ClipperLib.PolyTree();
cpr.Execute(clipTypes[i], solution_polytree, subject_fillType, clip_fillType);
solution_expolygons = new ClipperLib.ExPolygons();
ClipperLib.Clipper.PolyTreeToExPolygons(solution_polytree, solution_expolygons);
// console.log(JSON.stringify(solution_expolygons));
As you see in the above code, polygons are added as Polygons, not as PolyTrees. The difference compared to Polygons lies in the following line:
:::javascript
var solution_polytree = new ClipperLib.PolyTree();
The PolyTree structure is created using new ClipperLib.PolyTree()
, while Paths structure is created using new ClipperLib.Paths()
. This is the only difference in this case.
While the difference seems to be so small in usage, the solution (eg. union result) is more different. Let's first see, what the solution looks like, when cpr.Execute
is called using new ClipperLib.Paths()
(coordinates are scaled down for simplicity):
[
[
{"X":50,"Y":150},
{"X":50,"Y":110},
{"X":10,"Y":110},
{"X":10,"Y":10},
{"X":110,"Y":10},
{"X":110,"Y":50},
{"X":150,"Y":50},
{"X":150,"Y":150}
],
[
{"X":60,"Y":140},
{"X":140,"Y":140},
{"X":140,"Y":60},
{"X":110,"Y":60},
{"X":110,"Y":110},
{"X":60,"Y":110}
],
[
{"X":20,"Y":100},
{"X":50,"Y":100},
{"X":50,"Y":50},
{"X":100,"Y":50},
{"X":100,"Y":20},
{"X":20,"Y":20}
],
[
{"X":60,"Y":100},
{"X":100,"Y":100},
{"X":100,"Y":60},
{"X":60,"Y":60}
]
]
Above four polygons are in more or less random order, and you cannot be sure which is hole and which is contour without examining eg. the area of polygons.
When cpr.Execute
is called using new ClipperLib.PolyTree()
and then converted to ExPolygons structure using ClipperLib.Clipper.PolyTreeToExPolygons
, the solution in JSON format is after execution (coordinates are scaled down for simplicity):
[
{
"outer":
[
{"X":50,"Y":150},
{"X":50,"Y":110},
{"X":10,"Y":110},
{"X":10,"Y":10},
{"X":110,"Y":10},
{"X":110,"Y":50},
{"X":150,"Y":50},
{"X":150,"Y":150}
],
"holes":
[
[
{"X":60,"Y":140},
{"X":140,"Y":140},
{"X":140,"Y":60},
{"X":110,"Y":60},
{"X":110,"Y":110},
{"X":60,"Y":110}
],
[
{"X":20,"Y":100},
{"X":50,"Y":100},
{"X":50,"Y":50},
{"X":100,"Y":50},
{"X":100,"Y":20},
{"X":20,"Y":20}
],
[
{"X":60,"Y":100},
{"X":100,"Y":100},
{"X":100,"Y":60},
{"X":60,"Y":60}
]
]
}
]
The above contains one ExPolygon object, that has one outer polygon contour and three holes. You can now be sure that those holes are contained by the contour in question and not by some other contour (if there were such ones). Also you can be sure, which is hole and which is contour.
As you see, the structure above is an array (ExPolygons structure). This array can contain more than one ExPolygon objects.
If you need the full parent-child relationship information of holes and contours, ExPolygons cannot tell it for you. In Clipper Ver 5.1, the ExPolygons structure was replaced with the PolyTree class, which better represents the parent-child relationships of the returned polygons. The PolyTree structure is explained well in
original Clipper documentation.
As we made in the examples of boolean operated polygons and offsetting polygons, we'll present full functional example of boolean operations using ExPolygons and drawing them on inline SVG.
As in Polygons sample code, we use <path>
element of SVG to draw ExPolygons. 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 ExPolygons and appending them to SVG. In this case we create four inline SVG documents and draw one boolean result on each one. At the right side of each SVG document is the corresponding JSON string of exPolygons in question. You can easily modify subj_paths
and clip_paths
to see how the changes affect to the solutions.
Side note: Function scaleDownExPolygon()
is needed only to scale down ExPolygon coordinates, but in real world it is not needed, because downscaling is done in polys2path()
function.
Get full source code.
When executed the above code generates the following page (subject and clip polygons and colors are added to make the image more understandable):