@countertype/clipper2-ts
Version:
TypeScript port of Clipper2 polygon clipping and offsetting library
131 lines • 4.92 kB
JavaScript
;
/*******************************************************************************
* Author : Angus Johnson *
* Date : 10 October 2024 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : Minkowski Sum and Difference *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Minkowski = void 0;
const Core_1 = require("./Core");
const Engine_1 = require("./Engine");
var Minkowski;
(function (Minkowski) {
function minkowskiInternal(pattern, path, isSum, isClosed) {
const delta = isClosed ? 0 : 1;
const patLen = pattern.length;
const pathLen = path.length;
const tmp = [];
for (const pathPt of path) {
const path2 = [];
if (isSum) {
for (const basePt of pattern) {
path2.push(Core_1.Point64Utils.add(pathPt, basePt));
}
}
else {
for (const basePt of pattern) {
path2.push(Core_1.Point64Utils.subtract(pathPt, basePt));
}
}
tmp.push(path2);
}
const result = [];
let g = isClosed ? pathLen - 1 : 0;
let h = patLen - 1;
for (let i = delta; i < pathLen; i++) {
for (let j = 0; j < patLen; j++) {
const quad = [
tmp[g][h],
tmp[i][h],
tmp[i][j],
tmp[g][j]
];
if (!isPositive(quad)) {
result.push(reversePath(quad));
}
else {
result.push(quad);
}
h = j;
}
g = i;
}
return result;
}
function sum(pattern, path, isClosed) {
return union(minkowskiInternal(pattern, path, true, isClosed), Core_1.FillRule.NonZero);
}
Minkowski.sum = sum;
function sumD(pattern, path, isClosed, decimalPlaces = 2) {
const scale = Math.pow(10, decimalPlaces);
const tmp = union(minkowskiInternal(scalePath64(pattern, scale), scalePath64(path, scale), true, isClosed), Core_1.FillRule.NonZero);
return scalePathsD(tmp, 1 / scale);
}
Minkowski.sumD = sumD;
function diff(pattern, path, isClosed) {
return union(minkowskiInternal(pattern, path, false, isClosed), Core_1.FillRule.NonZero);
}
Minkowski.diff = diff;
function diffD(pattern, path, isClosed, decimalPlaces = 2) {
const scale = Math.pow(10, decimalPlaces);
const tmp = union(minkowskiInternal(scalePath64(pattern, scale), scalePath64(path, scale), false, isClosed), Core_1.FillRule.NonZero);
return scalePathsD(tmp, 1 / scale);
}
Minkowski.diffD = diffD;
// Helper functions (these would typically be imported from the main Clipper class)
function isPositive(path) {
return area(path) >= 0;
}
function area(path) {
// https://en.wikipedia.org/wiki/Shoelace_formula
let a = 0.0;
const cnt = path.length;
if (cnt < 3)
return 0.0;
let prevPt = path[cnt - 1];
for (const pt of path) {
a += (prevPt.y + pt.y) * (prevPt.x - pt.x);
prevPt = pt;
}
return a * 0.5;
}
function reversePath(path) {
return [...path].reverse();
}
function scalePath64(path, scale) {
const result = [];
for (const pt of path) {
result.push({
x: Core_1.InternalClipper.roundToEven(pt.x * scale),
y: Core_1.InternalClipper.roundToEven(pt.y * scale)
});
}
return result;
}
function scalePathsD(paths, scale) {
const result = [];
for (const path of paths) {
const pathD = [];
for (const pt of path) {
pathD.push({
x: pt.x * scale,
y: pt.y * scale
});
}
result.push(pathD);
}
return result;
}
// Local union implementation to avoid circular dependency
function union(paths, fillRule) {
const solution = [];
const c = new Engine_1.Clipper64();
c.addPaths(paths, Core_1.PathType.Subject);
c.execute(Core_1.ClipType.Union, fillRule, solution);
return solution;
}
})(Minkowski || (exports.Minkowski = Minkowski = {}));
//# sourceMappingURL=Minkowski.js.map