From 372db851665c9fa8e56f00e9181b0e42b288d6a8 Mon Sep 17 00:00:00 2001 From: Karsten Schmidt Date: Mon, 9 Jan 2023 14:05:47 +0100 Subject: [PATCH] feat(geom-clip-line): add clipPolylineWith() --- packages/geom-clip-line/package.json | 3 + packages/geom-clip-line/src/clip-with.ts | 75 ++++++++++++++++++++++++ packages/geom-clip-line/src/index.ts | 1 + 3 files changed, 79 insertions(+) create mode 100644 packages/geom-clip-line/src/clip-with.ts diff --git a/packages/geom-clip-line/package.json b/packages/geom-clip-line/package.json index f00fb51f5a..31d788b8f4 100644 --- a/packages/geom-clip-line/package.json +++ b/packages/geom-clip-line/package.json @@ -73,6 +73,9 @@ "./clip-poly": { "default": "./clip-poly.js" }, + "./clip-with": { + "default": "./clip-with.js" + }, "./liang-barsky": { "default": "./liang-barsky.js" } diff --git a/packages/geom-clip-line/src/clip-with.ts b/packages/geom-clip-line/src/clip-with.ts new file mode 100644 index 0000000000..dd60b8262e --- /dev/null +++ b/packages/geom-clip-line/src/clip-with.ts @@ -0,0 +1,75 @@ +import type { Predicate2 } from "@thi.ng/api"; +import type { ReadonlyVec } from "@thi.ng/vectors"; + +/** + * Segments given polyline based on given predicate and `step` size. Returns + * array of segmented vertex chunks. + * + * @remarks + * Vertices are processed pairwise using given `step` size (default: 1). As long + * as the predicate is true, vertices are collected into the current chunk. If + * the predicate returns false, the current chunk is terminated and depending on + * `keepLast`, the current vertex (i.e. for which the predicate failed) is still + * added or not. If `keepLast` is false and the current chunk only included one + * other vertex, that entire chunk will be discarded. + * + * @example + * ```ts + * const pts = [[0, 0], [1, 0], [1, 1], [2, 1], [3, 1], [4, 0], [5, 0]]; + * + * // isolate horizontal chunks + * clipPolylineWith((a, b) => a[1] == b[1], pts) + * // [ + * // [[0, 0], [1, 0]], + * // [[1, 1], [2, 1], [3, 1]], + * // [[4, 0], [5, 0]] + * // ] + * + * // isolate sloped chunks + * clipPolylineWith((a, b) => a[1] != b[1], pts) + * // [ + * // [[1, 0], [1, 1]], + * // [[3, 1], [4, 0]] + * // ] + * ``` + * + * @param pred + * @param pts + * @param step + * @param keepLast + */ +export const clipPolylineWith = ( + pred: Predicate2, + pts: ReadonlyVec[], + step = 1, + keepLast = true +) => { + const segs: ReadonlyVec[][] = []; + let last = -1; + + const $terminate = (i: number) => { + if (last !== -1) { + if (keepLast) { + segs[segs.length - 1].push(pts[i]); + } else if (segs[segs.length - 1].length < 2) { + segs.pop(); + } + } + last = -1; + }; + + for (let i = 0, n = pts.length - step; i < n; i++) { + if (pred(pts[i], pts[i + step])) { + if (last === -1) { + segs.push([pts[i]]); + last = i; + } else { + segs[segs.length - 1].push(pts[i]); + } + } else { + $terminate(i); + } + } + $terminate(pts.length - step); + return segs; +}; diff --git a/packages/geom-clip-line/src/index.ts b/packages/geom-clip-line/src/index.ts index 11d2fbcf12..76e2fc672c 100644 --- a/packages/geom-clip-line/src/index.ts +++ b/packages/geom-clip-line/src/index.ts @@ -1,2 +1,3 @@ export * from "./clip-poly.js"; +export * from "./clip-with.js"; export * from "./liang-barsky.js";