UNPKG

@countertype/clipper2-ts

Version:

TypeScript port of Clipper2 polygon clipping and offsetting library

1,134 lines 40.4 kB
"use strict"; /******************************************************************************* * Author : Angus Johnson * * Date : 5 March 2025 * * Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2025 * * Purpose : This module contains simple functions that will likely cover * * most polygon boolean and offsetting needs, while also avoiding * * the inherent complexities of the other modules. * * License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ Object.defineProperty(exports, "__esModule", { value: true }); exports.Clipper = void 0; const Core_1 = require("./Core"); const Engine_1 = require("./Engine"); const Offset_1 = require("./Offset"); const RectClip_1 = require("./RectClip"); const Minkowski_1 = require("./Minkowski"); var Clipper; (function (Clipper) { // Constants Clipper.invalidRect64 = Core_1.InvalidRect64; Clipper.invalidRectD = Core_1.InvalidRectD; // Boolean operations function intersect(subject, clip, fillRule) { return booleanOp(Core_1.ClipType.Intersection, subject, clip, fillRule); } Clipper.intersect = intersect; function intersectD(subject, clip, fillRule, precision = 2) { return booleanOpD(Core_1.ClipType.Intersection, subject, clip, fillRule, precision); } Clipper.intersectD = intersectD; function union(subject, clipOrFillRule, fillRule) { if (typeof clipOrFillRule === 'number') { // First overload: union(subject, fillRule) return booleanOp(Core_1.ClipType.Union, subject, null, clipOrFillRule); } else { // Second overload: union(subject, clip, fillRule) return booleanOp(Core_1.ClipType.Union, subject, clipOrFillRule, fillRule); } } Clipper.union = union; function unionD(subject, clipOrFillRule, fillRuleOrPrecision, precision) { if (typeof clipOrFillRule === 'number') { // First overload: unionD(subject, fillRule) return booleanOpD(Core_1.ClipType.Union, subject, null, clipOrFillRule); } else { // Second overload: unionD(subject, clip, fillRule, precision) return booleanOpD(Core_1.ClipType.Union, subject, clipOrFillRule, fillRuleOrPrecision, precision || 2); } } Clipper.unionD = unionD; function difference(subject, clip, fillRule) { return booleanOp(Core_1.ClipType.Difference, subject, clip, fillRule); } Clipper.difference = difference; function differenceD(subject, clip, fillRule, precision = 2) { return booleanOpD(Core_1.ClipType.Difference, subject, clip, fillRule, precision); } Clipper.differenceD = differenceD; function xor(subject, clip, fillRule) { return booleanOp(Core_1.ClipType.Xor, subject, clip, fillRule); } Clipper.xor = xor; function xorD(subject, clip, fillRule, precision = 2) { return booleanOpD(Core_1.ClipType.Xor, subject, clip, fillRule, precision); } Clipper.xorD = xorD; function booleanOp(clipType, subject, clip, fillRule) { const solution = []; if (subject === null) return solution; const c = new Engine_1.Clipper64(); c.addPaths(subject, Core_1.PathType.Subject); if (clip !== null) { c.addPaths(clip, Core_1.PathType.Clip); } c.execute(clipType, fillRule, solution); return solution; } Clipper.booleanOp = booleanOp; function booleanOpWithPolyTree(clipType, subject, clip, polytree, fillRule) { if (subject === null) return; const c = new Engine_1.Clipper64(); c.addPaths(subject, Core_1.PathType.Subject); if (clip !== null) { c.addPaths(clip, Core_1.PathType.Clip); } c.execute(clipType, fillRule, polytree); } Clipper.booleanOpWithPolyTree = booleanOpWithPolyTree; function booleanOpD(clipType, subject, clip, fillRule, precision = 2) { const solution = []; const c = new Engine_1.ClipperD(precision); c.addSubjectPaths(subject); if (clip !== null) { c.addClipPaths(clip); } c.execute(clipType, fillRule, solution); return solution; } Clipper.booleanOpD = booleanOpD; function booleanOpDWithPolyTree(clipType, subject, clip, polytree, fillRule, precision = 2) { if (subject === null) return; const c = new Engine_1.ClipperD(precision); c.addSubjectPaths(subject); if (clip !== null) { c.addClipPaths(clip); } c.execute(clipType, fillRule, polytree); } Clipper.booleanOpDWithPolyTree = booleanOpDWithPolyTree; function inflatePaths(paths, delta, joinType, endType, miterLimit = 2.0, arcTolerance = 0.0) { const co = new Offset_1.ClipperOffset(miterLimit, arcTolerance); co.addPaths(paths, joinType, endType); const solution = []; co.execute(delta, solution); return solution; } Clipper.inflatePaths = inflatePaths; function inflatePathsD(paths, delta, joinType, endType, miterLimit = 2.0, precision = 2, arcTolerance = 0.0) { Core_1.InternalClipper.checkPrecision(precision); const scale = Math.pow(10, precision); const tmp = scalePaths64(paths, scale); const co = new Offset_1.ClipperOffset(miterLimit, scale * arcTolerance); co.addPaths(tmp, joinType, endType); const solution = []; co.execute(delta * scale, solution); // reuse solution to receive (scaled) solution return scalePathsD(solution, 1 / scale); } Clipper.inflatePathsD = inflatePathsD; function rectClip(rect, pathsOrPath, precision) { if ('left' in rect && typeof rect.left === 'number' && Number.isInteger(rect.left)) { // Rect64 case const rect64 = rect; if (Core_1.Rect64Utils.isEmpty(rect64)) return []; if (Array.isArray(pathsOrPath[0])) { // Paths64 const paths = pathsOrPath; if (paths.length === 0) return []; const rc = new RectClip_1.RectClip64(rect64); return rc.execute(paths); } else { // Path64 const path = pathsOrPath; if (path.length === 0) return []; const tmp = [path]; return rectClip(rect64, tmp); } } else { // RectD case const rectD = rect; const prec = precision || 2; Core_1.InternalClipper.checkPrecision(prec); if (Core_1.RectDUtils.isEmpty(rectD)) return []; const scale = Math.pow(10, prec); const r = scaleRect(rectD, scale); if (Array.isArray(pathsOrPath[0])) { // PathsD const paths = pathsOrPath; if (paths.length === 0) return []; const tmpPath = scalePaths64(paths, scale); const rc = new RectClip_1.RectClip64(r); const result = rc.execute(tmpPath); return scalePathsD(result, 1 / scale); } else { // PathD const path = pathsOrPath; if (path.length === 0) return []; const tmp = [path]; return rectClip(rectD, tmp, prec); } } } Clipper.rectClip = rectClip; function rectClipLines(rect, pathsOrPath, precision) { if ('left' in rect && typeof rect.left === 'number' && Number.isInteger(rect.left)) { // Rect64 case const rect64 = rect; if (Core_1.Rect64Utils.isEmpty(rect64)) return []; if (Array.isArray(pathsOrPath[0])) { // Paths64 const paths = pathsOrPath; if (paths.length === 0) return []; const rc = new RectClip_1.RectClipLines64(rect64); return rc.execute(paths); } else { // Path64 const path = pathsOrPath; if (path.length === 0) return []; const tmp = [path]; return rectClipLines(rect64, tmp); } } else { // RectD case const rectD = rect; const prec = precision || 2; Core_1.InternalClipper.checkPrecision(prec); if (Core_1.RectDUtils.isEmpty(rectD)) return []; const scale = Math.pow(10, prec); const r = scaleRect(rectD, scale); if (Array.isArray(pathsOrPath[0])) { // PathsD const paths = pathsOrPath; if (paths.length === 0) return []; const tmpPath = scalePaths64(paths, scale); const rc = new RectClip_1.RectClipLines64(r); const result = rc.execute(tmpPath); return scalePathsD(result, 1 / scale); } else { // PathD const path = pathsOrPath; if (path.length === 0) return []; const tmp = [path]; return rectClipLines(rectD, tmp, prec); } } } Clipper.rectClipLines = rectClipLines; function minkowskiSum(pattern, path, isClosed) { return Minkowski_1.Minkowski.sum(pattern, path, isClosed); } Clipper.minkowskiSum = minkowskiSum; function minkowskiSumD(pattern, path, isClosed) { return Minkowski_1.Minkowski.sumD(pattern, path, isClosed); } Clipper.minkowskiSumD = minkowskiSumD; function minkowskiDiff(pattern, path, isClosed) { return Minkowski_1.Minkowski.diff(pattern, path, isClosed); } Clipper.minkowskiDiff = minkowskiDiff; function minkowskiDiffD(pattern, path, isClosed) { return Minkowski_1.Minkowski.diffD(pattern, path, isClosed); } Clipper.minkowskiDiffD = minkowskiDiffD; 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; } Clipper.area = area; function areaPaths(paths) { let a = 0.0; for (const path of paths) { a += area(path); } return a; } Clipper.areaPaths = areaPaths; function areaD(path) { 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; } Clipper.areaD = areaD; function areaPathsD(paths) { let a = 0.0; for (const path of paths) { a += areaD(path); } return a; } Clipper.areaPathsD = areaPathsD; function isPositive(poly) { return area(poly) >= 0; } Clipper.isPositive = isPositive; function isPositiveD(poly) { return areaD(poly) >= 0; } Clipper.isPositiveD = isPositiveD; function path64ToString(path) { let result = ""; for (const pt of path) { result += Core_1.Point64Utils.toString(pt); } return result + '\n'; } Clipper.path64ToString = path64ToString; function paths64ToString(paths) { let result = ""; for (const path of paths) { result += path64ToString(path); } return result; } Clipper.paths64ToString = paths64ToString; function pathDToString(path, precision = 2) { let result = ""; for (const pt of path) { result += Core_1.PointDUtils.toString(pt, precision); } return result + '\n'; } Clipper.pathDToString = pathDToString; function pathsDToString(paths, precision = 2) { let result = ""; for (const path of paths) { result += pathDToString(path, precision); } return result; } Clipper.pathsDToString = pathsDToString; function offsetPath(path, dx, dy) { const result = []; for (const pt of path) { result.push({ x: pt.x + dx, y: pt.y + dy }); } return result; } Clipper.offsetPath = offsetPath; function scalePoint64(pt, scale) { return { x: Math.round(pt.x * scale), y: Math.round(pt.y * scale) }; } Clipper.scalePoint64 = scalePoint64; function scalePointD(pt, scale) { return { x: pt.x * scale, y: pt.y * scale }; } Clipper.scalePointD = scalePointD; function scaleRect(rec, scale) { return { left: Math.round(rec.left * scale), top: Math.round(rec.top * scale), right: Math.round(rec.right * scale), bottom: Math.round(rec.bottom * scale) }; } Clipper.scaleRect = scaleRect; function scalePath(path, scale) { if (Core_1.InternalClipper.isAlmostZero(scale - 1)) return path; const result = []; for (const pt of path) { result.push({ x: Math.round(pt.x * scale), y: Math.round(pt.y * scale) }); } return result; } Clipper.scalePath = scalePath; function scalePaths(paths, scale) { if (Core_1.InternalClipper.isAlmostZero(scale - 1)) return paths; const result = []; for (const path of paths) { result.push(scalePath(path, scale)); } return result; } Clipper.scalePaths = scalePaths; function scalePathD(path, scale) { if (Core_1.InternalClipper.isAlmostZero(scale - 1)) return path; const result = []; for (const pt of path) { result.push(Core_1.PointDUtils.scale(pt, scale)); } return result; } Clipper.scalePathD = scalePathD; function scalePathsD(paths, scale) { if (Core_1.InternalClipper.isAlmostZero(scale - 1)) return paths; const result = []; for (const path of paths) { result.push(scalePathD(path, scale)); } return result; } Clipper.scalePathsD = scalePathsD; // Unlike ScalePath, both ScalePath64 & ScalePathD also involve type conversion function scalePath64(path, scale) { const result = []; for (const pt of path) { result.push({ x: Math.round(pt.x * scale), y: Math.round(pt.y * scale) }); } return result; } Clipper.scalePath64 = scalePath64; function scalePaths64(paths, scale) { const result = []; for (const path of paths) { result.push(scalePath64(path, scale)); } return result; } Clipper.scalePaths64 = scalePaths64; function scalePathDFromInt(path, scale) { const result = []; for (const pt of path) { result.push({ x: pt.x * scale, y: pt.y * scale }); } return result; } Clipper.scalePathDFromInt = scalePathDFromInt; function scalePathsDFromInt(paths, scale) { const result = []; for (const path of paths) { result.push(scalePathDFromInt(path, scale)); } return result; } Clipper.scalePathsDFromInt = scalePathsDFromInt; // The static functions Path64 and PathD convert path types without scaling function path64FromD(path) { const result = []; for (const pt of path) { result.push(Core_1.Point64Utils.fromPointD(pt)); } return result; } Clipper.path64FromD = path64FromD; function paths64FromD(paths) { const result = []; for (const path of paths) { result.push(path64FromD(path)); } return result; } Clipper.paths64FromD = paths64FromD; function pathsD(paths) { const result = []; for (const path of paths) { result.push(pathD(path)); } return result; } Clipper.pathsD = pathsD; function pathD(path) { const result = []; for (const pt of path) { result.push(Core_1.PointDUtils.fromPoint64(pt)); } return result; } Clipper.pathD = pathD; function translatePath(path, dx, dy) { const result = []; for (const pt of path) { result.push({ x: pt.x + dx, y: pt.y + dy }); } return result; } Clipper.translatePath = translatePath; function translatePaths(paths, dx, dy) { const result = []; for (const path of paths) { result.push(offsetPath(path, dx, dy)); } return result; } Clipper.translatePaths = translatePaths; function translatePathD(path, dx, dy) { const result = []; for (const pt of path) { result.push({ x: pt.x + dx, y: pt.y + dy }); } return result; } Clipper.translatePathD = translatePathD; function translatePathsD(paths, dx, dy) { const result = []; for (const path of paths) { result.push(translatePathD(path, dx, dy)); } return result; } Clipper.translatePathsD = translatePathsD; function reversePath(path) { return [...path].reverse(); } Clipper.reversePath = reversePath; function reversePathD(path) { return [...path].reverse(); } Clipper.reversePathD = reversePathD; function reversePaths(paths) { const result = []; for (const path of paths) { result.push(reversePath(path)); } return result; } Clipper.reversePaths = reversePaths; function reversePathsD(paths) { const result = []; for (const path of paths) { result.push(reversePathD(path)); } return result; } Clipper.reversePathsD = reversePathsD; function getBounds(path) { return Core_1.InternalClipper.getBounds(path); } Clipper.getBounds = getBounds; function getBoundsPaths(paths) { const result = Core_1.Rect64Utils.createInvalid(); for (const path of paths) { for (const pt of path) { if (pt.x < result.left) result.left = pt.x; if (pt.x > result.right) result.right = pt.x; if (pt.y < result.top) result.top = pt.y; if (pt.y > result.bottom) result.bottom = pt.y; } } return result.left === Number.MAX_SAFE_INTEGER ? { left: 0, top: 0, right: 0, bottom: 0 } : result; } Clipper.getBoundsPaths = getBoundsPaths; function getBoundsD(path) { const result = Core_1.RectDUtils.createInvalid(); for (const pt of path) { if (pt.x < result.left) result.left = pt.x; if (pt.x > result.right) result.right = pt.x; if (pt.y < result.top) result.top = pt.y; if (pt.y > result.bottom) result.bottom = pt.y; } return Math.abs(result.left - Number.MAX_VALUE) < Core_1.InternalClipper.floatingPointTolerance ? { left: 0, top: 0, right: 0, bottom: 0 } : result; } Clipper.getBoundsD = getBoundsD; function getBoundsPathsD(paths) { const result = Core_1.RectDUtils.createInvalid(); for (const path of paths) { for (const pt of path) { if (pt.x < result.left) result.left = pt.x; if (pt.x > result.right) result.right = pt.x; if (pt.y < result.top) result.top = pt.y; if (pt.y > result.bottom) result.bottom = pt.y; } } return Math.abs(result.left - Number.MAX_VALUE) < Core_1.InternalClipper.floatingPointTolerance ? { left: 0, top: 0, right: 0, bottom: 0 } : result; } Clipper.getBoundsPathsD = getBoundsPathsD; function makePath(arr) { const len = Math.floor(arr.length / 2); const p = []; for (let i = 0; i < len; i++) { p.push({ x: arr[i * 2], y: arr[i * 2 + 1] }); } return p; } Clipper.makePath = makePath; function makePathD(arr) { const len = Math.floor(arr.length / 2); const p = []; for (let i = 0; i < len; i++) { p.push({ x: arr[i * 2], y: arr[i * 2 + 1] }); } return p; } Clipper.makePathD = makePathD; function sqr(val) { return val * val; } Clipper.sqr = sqr; function distanceSqr(pt1, pt2) { return sqr(pt1.x - pt2.x) + sqr(pt1.y - pt2.y); } Clipper.distanceSqr = distanceSqr; function midPoint(pt1, pt2) { return { x: Math.round((pt1.x + pt2.x) / 2), y: Math.round((pt1.y + pt2.y) / 2) }; } Clipper.midPoint = midPoint; function midPointD(pt1, pt2) { return { x: (pt1.x + pt2.x) / 2, y: (pt1.y + pt2.y) / 2 }; } Clipper.midPointD = midPointD; function inflateRect(rec, dx, dy) { rec.left -= dx; rec.right += dx; rec.top -= dy; rec.bottom += dy; } Clipper.inflateRect = inflateRect; function inflateRectD(rec, dx, dy) { rec.left -= dx; rec.right += dx; rec.top -= dy; rec.bottom += dy; } Clipper.inflateRectD = inflateRectD; function pointsNearEqual(pt1, pt2, distanceSqrd) { return sqr(pt1.x - pt2.x) + sqr(pt1.y - pt2.y) < distanceSqrd; } Clipper.pointsNearEqual = pointsNearEqual; function stripNearDuplicates(path, minEdgeLenSqrd, isClosedPath) { const cnt = path.length; const result = []; if (cnt === 0) return result; let lastPt = path[0]; result.push(lastPt); for (let i = 1; i < cnt; i++) { if (!pointsNearEqual(lastPt, path[i], minEdgeLenSqrd)) { lastPt = path[i]; result.push(lastPt); } } if (isClosedPath && pointsNearEqual(lastPt, result[0], minEdgeLenSqrd)) { result.pop(); } return result; } Clipper.stripNearDuplicates = stripNearDuplicates; function stripDuplicates(path, isClosedPath) { const cnt = path.length; const result = []; if (cnt === 0) return result; let lastPt = path[0]; result.push(lastPt); for (let i = 1; i < cnt; i++) { if (!Core_1.Point64Utils.equals(lastPt, path[i])) { lastPt = path[i]; result.push(lastPt); } } if (isClosedPath && Core_1.Point64Utils.equals(lastPt, result[0])) { result.pop(); } return result; } Clipper.stripDuplicates = stripDuplicates; function addPolyNodeToPaths(polyPath, paths) { if (polyPath.poly && polyPath.poly.length > 0) { paths.push(polyPath.poly); } for (let i = 0; i < polyPath.count; i++) { addPolyNodeToPaths(polyPath.child(i), paths); } } function polyTreeToPaths64(polyTree) { const result = []; for (let i = 0; i < polyTree.count; i++) { addPolyNodeToPaths(polyTree.child(i), result); } return result; } Clipper.polyTreeToPaths64 = polyTreeToPaths64; function addPolyNodeToPathsD(polyPath, paths) { if (polyPath.poly && polyPath.poly.length > 0) { paths.push(polyPath.poly); } for (let i = 0; i < polyPath.count; i++) { addPolyNodeToPathsD(polyPath.child(i), paths); } } Clipper.addPolyNodeToPathsD = addPolyNodeToPathsD; function polyTreeToPathsD(polyTree) { const result = []; for (let i = 0; i < polyTree.count; i++) { addPolyNodeToPathsD(polyTree.child(i), result); } return result; } Clipper.polyTreeToPathsD = polyTreeToPathsD; function perpendicDistFromLineSqrd(pt, line1, line2) { const a = pt.x - line1.x; const b = pt.y - line1.y; const c = line2.x - line1.x; const d = line2.y - line1.y; if (c === 0 && d === 0) return 0; return sqr(a * d - c * b) / (c * c + d * d); } Clipper.perpendicDistFromLineSqrd = perpendicDistFromLineSqrd; function perpendicDistFromLineSqrd64(pt, line1, line2) { const a = pt.x - line1.x; const b = pt.y - line1.y; const c = line2.x - line1.x; const d = line2.y - line1.y; if (c === 0 && d === 0) return 0; return sqr(a * d - c * b) / (c * c + d * d); } Clipper.perpendicDistFromLineSqrd64 = perpendicDistFromLineSqrd64; function rdp(path, begin, end, epsSqrd, flags) { while (true) { let idx = 0; let maxD = 0; while (end > begin && Core_1.Point64Utils.equals(path[begin], path[end])) flags[end--] = false; for (let i = begin + 1; i < end; ++i) { // PerpendicDistFromLineSqrd - avoids expensive Sqrt() const d = perpendicDistFromLineSqrd64(path[i], path[begin], path[end]); if (d <= maxD) continue; maxD = d; idx = i; } if (maxD <= epsSqrd) return; flags[idx] = true; if (idx > begin + 1) rdp(path, begin, idx, epsSqrd, flags); if (idx < end - 1) { begin = idx; continue; } break; } } function ramerDouglasPeucker(path, epsilon) { const len = path.length; if (len < 5) return path; const flags = new Array(len).fill(false); flags[0] = true; flags[len - 1] = true; rdp(path, 0, len - 1, sqr(epsilon), flags); const result = []; for (let i = 0; i < len; ++i) { if (flags[i]) result.push(path[i]); } return result; } Clipper.ramerDouglasPeucker = ramerDouglasPeucker; function ramerDouglasPeuckerPaths(paths, epsilon) { const result = []; for (const path of paths) { result.push(ramerDouglasPeucker(path, epsilon)); } return result; } Clipper.ramerDouglasPeuckerPaths = ramerDouglasPeuckerPaths; function rdpD(path, begin, end, epsSqrd, flags) { while (true) { let idx = 0; let maxD = 0; while (end > begin && Core_1.PointDUtils.equals(path[begin], path[end])) flags[end--] = false; for (let i = begin + 1; i < end; ++i) { // PerpendicDistFromLineSqrd - avoids expensive Sqrt() const d = perpendicDistFromLineSqrd(path[i], path[begin], path[end]); if (d <= maxD) continue; maxD = d; idx = i; } if (maxD <= epsSqrd) return; flags[idx] = true; if (idx > begin + 1) rdpD(path, begin, idx, epsSqrd, flags); if (idx < end - 1) { begin = idx; continue; } break; } } function ramerDouglasPeuckerD(path, epsilon) { const len = path.length; if (len < 5) return path; const flags = new Array(len).fill(false); flags[0] = true; flags[len - 1] = true; rdpD(path, 0, len - 1, sqr(epsilon), flags); const result = []; for (let i = 0; i < len; ++i) { if (flags[i]) result.push(path[i]); } return result; } Clipper.ramerDouglasPeuckerD = ramerDouglasPeuckerD; function ramerDouglasPeuckerPathsD(paths, epsilon) { const result = []; for (const path of paths) { result.push(ramerDouglasPeuckerD(path, epsilon)); } return result; } Clipper.ramerDouglasPeuckerPathsD = ramerDouglasPeuckerPathsD; function getNext(current, high, flags) { ++current; while (current <= high && flags[current]) ++current; if (current <= high) return current; current = 0; while (flags[current]) ++current; return current; } function getPrior(current, high, flags) { if (current === 0) current = high; else --current; while (current > 0 && flags[current]) --current; if (!flags[current]) return current; current = high; while (flags[current]) --current; return current; } function simplifyPath(path, epsilon, isClosedPath = true) { const len = path.length; const high = len - 1; const epsSqr = sqr(epsilon); if (len < 4) return path; const flags = new Array(len).fill(false); const dsq = new Array(len).fill(0); let curr = 0; if (isClosedPath) { dsq[0] = perpendicDistFromLineSqrd64(path[0], path[high], path[1]); dsq[high] = perpendicDistFromLineSqrd64(path[high], path[0], path[high - 1]); } else { dsq[0] = Number.MAX_VALUE; dsq[high] = Number.MAX_VALUE; } for (let i = 1; i < high; ++i) { dsq[i] = perpendicDistFromLineSqrd64(path[i], path[i - 1], path[i + 1]); } while (true) { if (dsq[curr] > epsSqr) { const start = curr; do { curr = getNext(curr, high, flags); } while (curr !== start && dsq[curr] > epsSqr); if (curr === start) break; } const prev = getPrior(curr, high, flags); const next = getNext(curr, high, flags); if (next === prev) break; let prior2; if (dsq[next] < dsq[curr]) { prior2 = prev; const newPrev = curr; curr = next; const newNext = getNext(next, high, flags); flags[curr] = true; curr = newNext; const nextNext = getNext(newNext, high, flags); if (isClosedPath || ((curr !== high) && (curr !== 0))) { dsq[curr] = perpendicDistFromLineSqrd64(path[curr], path[newPrev], path[nextNext]); } if (isClosedPath || ((newPrev !== 0) && (newPrev !== high))) { dsq[newPrev] = perpendicDistFromLineSqrd64(path[newPrev], path[prior2], path[curr]); } } else { prior2 = getPrior(prev, high, flags); flags[curr] = true; curr = next; const nextNext = getNext(next, high, flags); if (isClosedPath || ((curr !== high) && (curr !== 0))) { dsq[curr] = perpendicDistFromLineSqrd64(path[curr], path[prev], path[nextNext]); } if (isClosedPath || ((prev !== 0) && (prev !== high))) { dsq[prev] = perpendicDistFromLineSqrd64(path[prev], path[prior2], path[curr]); } } } const result = []; for (let i = 0; i < len; i++) { if (!flags[i]) result.push(path[i]); } return result; } Clipper.simplifyPath = simplifyPath; function simplifyPaths(paths, epsilon, isClosedPaths = true) { const result = []; for (const path of paths) { result.push(simplifyPath(path, epsilon, isClosedPaths)); } return result; } Clipper.simplifyPaths = simplifyPaths; function simplifyPathD(path, epsilon, isClosedPath = true) { const len = path.length; const high = len - 1; const epsSqr = sqr(epsilon); if (len < 4) return path; const flags = new Array(len).fill(false); const dsq = new Array(len).fill(0); let curr = 0; if (isClosedPath) { dsq[0] = perpendicDistFromLineSqrd(path[0], path[high], path[1]); dsq[high] = perpendicDistFromLineSqrd(path[high], path[0], path[high - 1]); } else { dsq[0] = Number.MAX_VALUE; dsq[high] = Number.MAX_VALUE; } for (let i = 1; i < high; ++i) { dsq[i] = perpendicDistFromLineSqrd(path[i], path[i - 1], path[i + 1]); } while (true) { if (dsq[curr] > epsSqr) { const start = curr; do { curr = getNext(curr, high, flags); } while (curr !== start && dsq[curr] > epsSqr); if (curr === start) break; } const prev = getPrior(curr, high, flags); const next = getNext(curr, high, flags); if (next === prev) break; let prior2; if (dsq[next] < dsq[curr]) { prior2 = prev; const newPrev = curr; curr = next; const newNext = getNext(next, high, flags); flags[curr] = true; curr = newNext; const nextNext = getNext(newNext, high, flags); if (isClosedPath || ((curr !== high) && (curr !== 0))) { dsq[curr] = perpendicDistFromLineSqrd(path[curr], path[newPrev], path[nextNext]); } if (isClosedPath || ((newPrev !== 0) && (newPrev !== high))) { dsq[newPrev] = perpendicDistFromLineSqrd(path[newPrev], path[prior2], path[curr]); } } else { prior2 = getPrior(prev, high, flags); flags[curr] = true; curr = next; const nextNext = getNext(next, high, flags); if (isClosedPath || ((curr !== high) && (curr !== 0))) { dsq[curr] = perpendicDistFromLineSqrd(path[curr], path[prev], path[nextNext]); } if (isClosedPath || ((prev !== 0) && (prev !== high))) { dsq[prev] = perpendicDistFromLineSqrd(path[prev], path[prior2], path[curr]); } } } const result = []; for (let i = 0; i < len; i++) { if (!flags[i]) result.push(path[i]); } return result; } Clipper.simplifyPathD = simplifyPathD; function simplifyPathsD(paths, epsilon, isClosedPath = true) { const result = []; for (const path of paths) { result.push(simplifyPathD(path, epsilon, isClosedPath)); } return result; } Clipper.simplifyPathsD = simplifyPathsD; function trimCollinear(path, isOpen = false) { let len = path.length; let i = 0; if (!isOpen) { while (i < len - 1 && Core_1.InternalClipper.isCollinear(path[len - 1], path[i], path[i + 1])) i++; while (i < len - 1 && Core_1.InternalClipper.isCollinear(path[len - 2], path[len - 1], path[i])) len--; } if (len - i < 3) { if (!isOpen || len < 2 || Core_1.Point64Utils.equals(path[0], path[1])) { return []; } return path; } const result = []; let last = path[i]; result.push(last); for (i++; i < len - 1; i++) { if (Core_1.InternalClipper.isCollinear(last, path[i], path[i + 1])) continue; last = path[i]; result.push(last); } if (isOpen) { result.push(path[len - 1]); } else if (!Core_1.InternalClipper.isCollinear(last, path[len - 1], result[0])) { result.push(path[len - 1]); } else { while (result.length > 2 && Core_1.InternalClipper.isCollinear(result[result.length - 1], result[result.length - 2], result[0])) { result.pop(); } if (result.length < 3) { result.length = 0; } } return result; } Clipper.trimCollinear = trimCollinear; function trimCollinearD(path, precision, isOpen = false) { Core_1.InternalClipper.checkPrecision(precision); const scale = Math.pow(10, precision); let p = scalePath64(path, scale); p = trimCollinear(p, isOpen); return scalePathDFromInt(p, 1 / scale); } Clipper.trimCollinearD = trimCollinearD; function pointInPolygon(pt, polygon) { return Core_1.InternalClipper.pointInPolygon(pt, polygon); } Clipper.pointInPolygon = pointInPolygon; function pointInPolygonD(pt, polygon, precision = 2) { Core_1.InternalClipper.checkPrecision(precision); const scale = Math.pow(10, precision); const p = Core_1.Point64Utils.fromPointD(Core_1.PointDUtils.scale(pt, scale)); const pathScaled = scalePath64(polygon, scale); return Core_1.InternalClipper.pointInPolygon(p, pathScaled); } Clipper.pointInPolygonD = pointInPolygonD; function ellipse(center, radiusX, radiusY = 0, steps = 0) { if (radiusX <= 0) return []; if (radiusY <= 0) radiusY = radiusX; if (steps <= 2) { steps = Math.ceil(Math.PI * Math.sqrt((radiusX + radiusY) / 2)); } const si = Math.sin(2 * Math.PI / steps); const co = Math.cos(2 * Math.PI / steps); let dx = co; let dy = si; const result = [{ x: Math.round(center.x + radiusX), y: center.y }]; for (let i = 1; i < steps; ++i) { result.push({ x: Math.round(center.x + radiusX * dx), y: Math.round(center.y + radiusY * dy) }); const x = dx * co - dy * si; dy = dy * co + dx * si; dx = x; } return result; } Clipper.ellipse = ellipse; function ellipseD(center, radiusX, radiusY = 0, steps = 0) { if (radiusX <= 0) return []; if (radiusY <= 0) radiusY = radiusX; if (steps <= 2) { steps = Math.ceil(Math.PI * Math.sqrt((radiusX + radiusY) / 2)); } const si = Math.sin(2 * Math.PI / steps); const co = Math.cos(2 * Math.PI / steps); let dx = co; let dy = si; const result = [{ x: center.x + radiusX, y: center.y }]; for (let i = 1; i < steps; ++i) { result.push({ x: center.x + radiusX * dx, y: center.y + radiusY * dy }); const x = dx * co - dy * si; dy = dy * co + dx * si; dx = x; } return result; } Clipper.ellipseD = ellipseD; })(Clipper || (exports.Clipper = Clipper = {})); //# sourceMappingURL=Clipper.js.map