2015-07-26 15:32:10 +02:00
|
|
|
import ClipperLib from 'clipper-lib';
|
|
|
|
import THREE from 'three.js';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
export default class Paths extends Array {
|
|
|
|
constructor (paths = [], closed = true) {
|
|
|
|
super();
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
this.setPaths(paths);
|
|
|
|
this.closed = closed;
|
|
|
|
}
|
2015-07-06 22:59:58 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
setPaths (paths) {
|
|
|
|
for (var path of paths) {
|
|
|
|
if (path.length > 0) {
|
|
|
|
this.push(path);
|
|
|
|
}
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
return this;
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
_clip (path, type) {
|
|
|
|
var solution = new ClipperLib.PolyTree();
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
var clipper = new ClipperLib.Clipper();
|
|
|
|
clipper.AddPaths(this, ClipperLib.PolyType.ptSubject, this.closed);
|
|
|
|
clipper.AddPaths(path, ClipperLib.PolyType.ptClip, path.closed);
|
|
|
|
clipper.Execute(type, solution);
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
if (this.closed) {
|
|
|
|
var paths = ClipperLib.Clipper.ClosedPathsFromPolyTree(solution);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
var paths = ClipperLib.Clipper.OpenPathsFromPolyTree(solution);
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
return new Paths(paths, this.closed);
|
|
|
|
}
|
|
|
|
|
|
|
|
union (path) {
|
|
|
|
return this._clip(path, ClipperLib.ClipType.ctUnion);
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
difference (path) {
|
|
|
|
return this._clip(path, ClipperLib.ClipType.ctDifference);
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
intersect (path) {
|
|
|
|
return this._clip(path, ClipperLib.ClipType.ctIntersection);
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
xor (path) {
|
|
|
|
return this._clip(path, ClipperLib.ClipType.ctXor);
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
offset (offset) {
|
|
|
|
var solution = new ClipperLib.Paths();
|
|
|
|
var co = new ClipperLib.ClipperOffset(1, 1);
|
|
|
|
co.AddPaths(this, ClipperLib.JoinType.jtSquare, ClipperLib.EndType.etClosedPolygon);
|
|
|
|
co.Execute(solution, offset);
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
return new Paths(solution);
|
|
|
|
}
|
2015-05-15 11:14:44 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
scaleUp (factor) {
|
|
|
|
ClipperLib.JS.ScaleUpPaths(this, factor);
|
2015-05-15 11:14:44 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
return this;
|
|
|
|
}
|
2015-05-15 11:14:44 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
scaleDown (factor) {
|
|
|
|
ClipperLib.JS.ScaleDownPaths(this, factor);
|
2015-05-15 11:14:44 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
return this;
|
|
|
|
}
|
2015-05-15 11:14:44 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
lastPoint () {
|
|
|
|
var lastPath = this[this.length - 1];
|
|
|
|
var lastPoint = this.closed ? lastPath[0] : lastPath[lastPath.length - 1];
|
|
|
|
return new THREE.Vector2(lastPoint.X, lastPoint.Y);
|
|
|
|
}
|
2015-05-15 11:14:44 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
optimizePath (start) {
|
|
|
|
var optimizedPaths = new Paths([], this.closed);
|
|
|
|
var donePaths = [];
|
|
|
|
|
|
|
|
while (optimizedPaths.length !== this.length) {
|
|
|
|
var minLength = false;
|
|
|
|
var reverse;
|
|
|
|
var minPath;
|
|
|
|
var offset;
|
|
|
|
var pathIndex;
|
|
|
|
|
|
|
|
for (var i = 0; i < this.length; i += 1) {
|
|
|
|
var path = this[i];
|
|
|
|
|
|
|
|
if (donePaths.indexOf(i) === -1) {
|
|
|
|
|
|
|
|
if (this.closed) {
|
|
|
|
for (var j = 0; j < path.length; j += 1) {
|
|
|
|
var point = new THREE.Vector2(path[j].X, path[j].Y);
|
|
|
|
var length = point.sub(start).length();
|
|
|
|
if (minLength === false || length < minLength) {
|
|
|
|
minPath = path;
|
|
|
|
minLength = length;
|
|
|
|
offset = j;
|
|
|
|
pathIndex = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
var startPoint = new THREE.Vector2(path[0].X, path[0].Y);
|
|
|
|
var length = startPoint.sub(start).length();
|
2015-05-15 11:14:44 +02:00
|
|
|
if (minLength === false || length < minLength) {
|
|
|
|
minPath = path;
|
|
|
|
minLength = length;
|
2015-07-26 15:32:10 +02:00
|
|
|
reverse = false;
|
|
|
|
pathIndex = i;
|
|
|
|
}
|
|
|
|
var endPoint = new THREE.Vector2(path[path.length - 1].X, path[path.length - 1].Y);
|
|
|
|
var length = endPoint.sub(start).length();
|
|
|
|
if (length < minLength) {
|
|
|
|
minPath = path;
|
|
|
|
minLength = length;
|
|
|
|
reverse = true;
|
2015-05-15 11:14:44 +02:00
|
|
|
pathIndex = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
if (this.closed) {
|
|
|
|
minPath = minPath.concat(minPath.splice(0, offset));
|
|
|
|
var point = minPath[0];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (reverse) {
|
|
|
|
minPath.reverse();
|
|
|
|
}
|
|
|
|
var point = minPath[minPath.length - 1];
|
2015-05-15 11:14:44 +02:00
|
|
|
}
|
2015-07-26 15:32:10 +02:00
|
|
|
donePaths.push(pathIndex);
|
|
|
|
start = new THREE.Vector2(point.X, point.Y);
|
|
|
|
|
|
|
|
optimizedPaths.push(minPath);
|
2015-05-15 11:14:44 +02:00
|
|
|
}
|
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
return optimizedPaths;
|
|
|
|
}
|
2015-06-09 21:58:22 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
areas () {
|
|
|
|
var areas = [];
|
2015-06-09 21:58:22 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
for (var shape of this) {
|
|
|
|
if (shape.closed) {
|
|
|
|
var area = Math.abs(ClipperLib.Clipper.Area(shape));
|
|
|
|
areas.push(area);
|
|
|
|
}
|
|
|
|
}
|
2015-06-09 21:58:22 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
return areas;
|
2015-06-09 21:58:22 +02:00
|
|
|
}
|
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
area () {
|
|
|
|
var areas = this.areas();
|
|
|
|
var totalArea = 0;
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
for (area of areas) {
|
|
|
|
totalArea += area;
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
return totalArea;
|
|
|
|
}
|
|
|
|
|
|
|
|
tresholdArea (minArea) {
|
|
|
|
// code not tested yet
|
|
|
|
for (var shape of this) {
|
|
|
|
var area = ClipperLib.Clipper.Area(shape);
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
if (area < minArea) {
|
|
|
|
this.splice(i, 1);
|
|
|
|
i -= 1;
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
join (path) {
|
|
|
|
for (var i = 0; i < path.length; i += 1) {
|
|
|
|
this.push(path[i]);
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
return this;
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
clone () {
|
|
|
|
return new Paths(ClipperLib.JS.Clone(this), this.closed);
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
bounds () {
|
|
|
|
return ClipperLib.Clipper.GetBounds(this);
|
|
|
|
}
|
2015-06-12 15:58:26 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
clean (cleanDelta) {
|
|
|
|
return new Paths(ClipperLib.Clipper.CleanPolygons(this, cleanDelta), this.closed);
|
|
|
|
}
|
2015-06-10 18:25:49 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
boundSize () {
|
|
|
|
var bounds = this.bounds();
|
2015-06-10 18:25:49 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
var width = bounds.right - bounds.left;
|
|
|
|
var height = bounds.top - bounds.bottom;
|
2015-06-10 18:25:49 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
return width * height;
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
draw (context, color) {
|
|
|
|
context.strokeStyle = color;
|
|
|
|
for (var i = 0; i < this.length; i += 1) {
|
|
|
|
var shape = this[i];
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
// var point = shape[0];
|
|
|
|
// context.fillText(i, point.X*2, point.Y*2);
|
2015-05-15 11:14:44 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
context.beginPath();
|
|
|
|
for (var j = 0; j < shape.length; j += 1) {
|
|
|
|
var point = shape[j % shape.length];
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-07-26 15:32:10 +02:00
|
|
|
context.lineTo(point.X * 2, point.Y * 2);
|
|
|
|
}
|
|
|
|
if (this.closed) {
|
|
|
|
context.closePath();
|
|
|
|
}
|
|
|
|
context.stroke();
|
2015-07-06 22:59:58 +02:00
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
}
|
2015-07-26 15:32:10 +02:00
|
|
|
}
|