2015-05-13 12:12:15 +02:00
|
|
|
/******************************************************
|
|
|
|
*
|
|
|
|
* Path
|
|
|
|
*
|
|
|
|
* Abstraction layer for annoying clipper js
|
2015-05-20 19:10:18 +02:00
|
|
|
* ! inherrits from Array !
|
2015-05-13 12:12:15 +02:00
|
|
|
*
|
|
|
|
******************************************************/
|
|
|
|
|
2015-05-13 17:37:52 +02:00
|
|
|
D3D.Paths = function (paths, closed) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
Array.call(this);
|
|
|
|
|
|
|
|
this.setPaths(paths || []);
|
|
|
|
|
2015-05-13 17:37:52 +02:00
|
|
|
this.closed = (closed !== undefined) ? closed : true;
|
2015-05-13 12:12:15 +02:00
|
|
|
};
|
|
|
|
D3D.Paths.prototype = Object.create(Array.prototype);
|
|
|
|
D3D.Paths.prototype.setPaths = function (paths) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
for (var i = 0; i < paths.length; i ++) {
|
|
|
|
var path = paths[i];
|
2015-05-15 17:35:18 +02:00
|
|
|
if (path.length > 0) {
|
|
|
|
this.push(path);
|
|
|
|
}
|
2015-05-13 12:12:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
D3D.Paths.prototype.clip = function (path, type) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
var solution = new ClipperLib.Paths();
|
|
|
|
|
|
|
|
var clipper = new ClipperLib.Clipper();
|
|
|
|
clipper.AddPaths(this, ClipperLib.PolyType.ptSubject, this.closed);
|
2015-05-13 17:37:52 +02:00
|
|
|
clipper.AddPaths(path, ClipperLib.PolyType.ptClip, path.closed);
|
2015-05-13 12:12:15 +02:00
|
|
|
clipper.Execute(type, solution);
|
|
|
|
|
2015-05-15 17:35:18 +02:00
|
|
|
return new D3D.Paths(solution, this.closed);
|
2015-05-13 12:12:15 +02:00
|
|
|
};
|
|
|
|
D3D.Paths.prototype.union = function (path) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
return this.clip(path, ClipperLib.ClipType.ctUnion);
|
|
|
|
};
|
|
|
|
D3D.Paths.prototype.difference = function (path) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
return this.clip(path, ClipperLib.ClipType.ctDifference);
|
|
|
|
};
|
|
|
|
D3D.Paths.prototype.intersect = function (path) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
return this.clip(path, ClipperLib.ClipType.ctIntersection);
|
|
|
|
};
|
|
|
|
D3D.Paths.prototype.xor = function () {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
return this.clip(path, ClipperLib.ClipType.ctXor);
|
|
|
|
};
|
|
|
|
D3D.Paths.prototype.offset = function (offset) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
var solution = new ClipperLib.Paths();
|
|
|
|
var co = new ClipperLib.ClipperOffset(1, 1);
|
|
|
|
co.AddPaths(this, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
|
|
|
|
co.Execute(solution, offset);
|
|
|
|
|
|
|
|
return new D3D.Paths(solution);
|
|
|
|
};
|
|
|
|
D3D.Paths.prototype.scaleUp = function (factor) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
var path = ClipperLib.JS.ScaleUpPaths(this, factor);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
D3D.Paths.prototype.scaleDown = function (factor) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
var path = ClipperLib.JS.ScaleDownPaths(this, factor);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
2015-05-15 11:14:44 +02:00
|
|
|
D3D.Paths.prototype.lastPoint = function () {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-15 11:14:44 +02:00
|
|
|
|
|
|
|
var lastPath = this[this.length - 1];
|
|
|
|
var lastPoint = this.closed ? lastPath[0] : lastPath[lastPath.length - 1];
|
|
|
|
return new THREE.Vector2(lastPoint.X, lastPoint.Y);
|
|
|
|
};
|
|
|
|
D3D.Paths.prototype.optimizePath = function (start) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-15 11:14:44 +02:00
|
|
|
|
|
|
|
var optimizedPaths = new D3D.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 ++) {
|
|
|
|
var path = this[i];
|
|
|
|
|
|
|
|
if (donePaths.indexOf(i) === -1) {
|
|
|
|
|
|
|
|
if (this.closed) {
|
|
|
|
for (var j = 0; j < path.length; j ++) {
|
|
|
|
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();
|
|
|
|
if (minLength === false || length < minLength) {
|
|
|
|
minPath = path;
|
|
|
|
minLength = length;
|
|
|
|
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;
|
|
|
|
pathIndex = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
donePaths.push(pathIndex);
|
|
|
|
start = new THREE.Vector2(point.X, point.Y);
|
|
|
|
optimizedPaths.push(minPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
return optimizedPaths;
|
|
|
|
};
|
2015-06-09 21:58:22 +02:00
|
|
|
D3D.Paths.prototype.areas = function () {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-06-09 21:58:22 +02:00
|
|
|
|
|
|
|
var areas = [];
|
|
|
|
|
|
|
|
for (var i = 0; i < this.length; i ++) {
|
|
|
|
var shape = this[i];
|
|
|
|
|
2015-06-10 18:25:49 +02:00
|
|
|
var area = Math.abs(ClipperLib.Clipper.Area(shape));
|
|
|
|
areas.push(area);
|
2015-06-09 21:58:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return areas;
|
|
|
|
};
|
2015-05-13 12:12:15 +02:00
|
|
|
D3D.Paths.prototype.tresholdArea = function (minArea) {
|
2015-05-15 11:14:44 +02:00
|
|
|
//code not tested yet
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
for (var i = 0; i < this.length; i ++) {
|
|
|
|
var shape = this[i];
|
|
|
|
|
|
|
|
var area = ClipperLib.Clipper.Area(shape);
|
|
|
|
|
|
|
|
if (area < minArea) {
|
|
|
|
this.splice(i, 1);
|
|
|
|
i --;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return areas;
|
|
|
|
};
|
|
|
|
D3D.Paths.prototype.join = function (path) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
for (var i = 0; i < path.length; i ++) {
|
|
|
|
this.push(path[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
D3D.Paths.prototype.clone = function () {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-05-13 17:37:52 +02:00
|
|
|
return new D3D.Paths(ClipperLib.JS.Clone(this), this.closed);
|
|
|
|
};
|
|
|
|
D3D.Paths.prototype.bounds = function () {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
2015-05-13 17:37:52 +02:00
|
|
|
return ClipperLib.Clipper.GetBounds(this);
|
|
|
|
};
|
2015-06-10 18:25:49 +02:00
|
|
|
D3D.Paths.prototype.boundSize = function () {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-06-10 18:25:49 +02:00
|
|
|
|
|
|
|
var bounds = this.bounds();
|
|
|
|
|
|
|
|
var width = bounds.right - bounds.left;
|
|
|
|
var height = bounds.top - bounds.bottom;
|
|
|
|
|
|
|
|
return width * height;
|
|
|
|
};
|
2015-05-13 12:12:15 +02:00
|
|
|
D3D.Paths.prototype.draw = function (context, color) {
|
2015-06-11 10:28:21 +02:00
|
|
|
'use strict';
|
2015-05-13 12:12:15 +02:00
|
|
|
|
|
|
|
context.strokeStyle = color;
|
|
|
|
for (var i = 0; i < this.length; i ++) {
|
|
|
|
var shape = this[i];
|
|
|
|
|
2015-05-15 15:07:47 +02:00
|
|
|
//var point = shape[0];
|
|
|
|
//context.fillText(i, point.X*2, point.Y*2);
|
2015-05-15 11:14:44 +02:00
|
|
|
|
2015-05-13 12:12:15 +02:00
|
|
|
context.beginPath();
|
|
|
|
var length = this.closed ? (shape.length + 1) : shape.length;
|
|
|
|
for (var j = 0; j < length; j ++) {
|
|
|
|
var point = shape[j % shape.length];
|
|
|
|
|
|
|
|
context.lineTo(point.X*2, point.Y*2);
|
|
|
|
}
|
|
|
|
context.stroke();
|
|
|
|
}
|
|
|
|
};
|