Skip to content

Commit

Permalink
refactor(grid-iterators): update clipped shape iters
Browse files Browse the repository at this point in the history
- better pre-clipping for circle/line
- intern intersection/clipping helpers
  • Loading branch information
postspectacular committed Nov 2, 2021
1 parent 1b80de4 commit dc065ed
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 33 deletions.
4 changes: 2 additions & 2 deletions packages/grid-iterators/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@
"./circle": {
"import": "./circle.js"
},
"./clipped": {
"import": "./clipped.js"
"./clipping": {
"import": "./clipping.js"
},
"./column-ends": {
"import": "./column-ends.js"
Expand Down
13 changes: 9 additions & 4 deletions packages/grid-iterators/src/circle.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { clipped } from "./clipped.js";
import { asInt } from "@thi.ng/api/typedarray";
import { clipped, intersectRectCircle } from "./clipping.js";
import { hline } from "./hvline.js";
import { asInt } from "./utils.js";

export function* circle(cx: number, cy: number, r: number, fill = true) {
[cx, cy, r] = asInt(cx, cy, r);
Expand Down Expand Up @@ -49,7 +49,8 @@ export function* circle(cx: number, cy: number, r: number, fill = true) {

/**
* Version of {@link circle} yielding only coordinates in rect defined by
* `left,top`..`right,bottom`.
* `left,top`..`right,bottom`. Returns undefined if circle lies completely
* outside given clip rectangle.
*
* @param cx
* @param cy
Expand All @@ -69,4 +70,8 @@ export const circleClipped = (
right: number,
bottom: number,
fill = true
) => clipped(circle(cx, cy, r, fill), left, top, right, bottom);
) => {
return intersectRectCircle(left, top, right, bottom, cx, cy, r)
? clipped(circle(cx, cy, r, fill), left, top, right, bottom)
: undefined;
};
22 changes: 0 additions & 22 deletions packages/grid-iterators/src/clipped.ts

This file was deleted.

101 changes: 101 additions & 0 deletions packages/grid-iterators/src/clipping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import type { FnN3, FnU2, FnU7, FnU8, Tuple } from "@thi.ng/api";

/**
* Filters points from `src` iterable to remove any falling outside the rect
* defined by `left,top`..`right,bottom`.
*
* @param src
* @param left
* @param top
* @param right
* @param bottom
*/
export function* clipped(
src: Iterable<number[]>,
left: number,
top: number,
right: number,
bottom: number
) {
for (let p of src) {
if (p[0] >= left && p[0] < right && p[1] >= top && p[1] < bottom)
yield p;
}
}

/** @internal */
const axis: FnN3 = (a, b, c) =>
(a < b ? a - b : a > b + c ? a - b - c : 0) ** 2;

/**
* Based on {@link @thi.ng/geom-isec}, but inlined to avoid dependency.
*
* @param x
* @param y
* @param w
* @param h
* @param cx
* @param cy
* @param r
*
* @internal
*/
export const intersectRectCircle: FnU7<number, boolean> = (
x,
y,
w,
h,
cx,
cy,
r
) => axis(cx, x, w) + axis(cy, y, h) <= r * r;

/**
* Based on {@link @thi.ng/geom-clip-line#liangBarsky2Raw}, but with diff return type.
*
* @param ax
* @param ay
* @param bx
* @param by
* @param minx
* @param miny
* @param maxx
* @param maxy
*
* @internal
*/
export const liangBarsky: FnU8<number, Tuple<number, 4> | undefined> = (
ax,
ay,
bx,
by,
minx,
miny,
maxx,
maxy
) => {
const dx = bx - ax;
const dy = by - ay;
let alpha = 0;
let beta = 1;
const clip: FnU2<number, boolean> = (p, q) => {
if (p < 0) {
const r = q / p;
if (r > beta) return false;
r > alpha && (alpha = r);
} else if (p > 0) {
const r = q / p;
if (r < alpha) return false;
r < beta && (beta = r);
} else if (q < 0) {
return false;
}
return true;
};
return clip(-dx, ax - minx) &&
clip(dx, maxx - ax) &&
clip(-dy, ay - miny) &&
clip(dy, maxy - ay)
? [alpha * dx + ax, alpha * dy + ay, beta * dx + ax, beta * dy + ay]
: undefined;
};
2 changes: 1 addition & 1 deletion packages/grid-iterators/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export * from "./circle.js";
export * from "./clipped.js";
export * from "./clipping.js";
export * from "./column-ends.js";
export * from "./columns.js";
export * from "./diagonal.js";
Expand Down
12 changes: 8 additions & 4 deletions packages/grid-iterators/src/line.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { clipped } from "./clipped.js";
import { asInt } from "./utils.js";
import { asInt } from "@thi.ng/api/typedarray";
import { liangBarsky } from "./clipping";

export function* line(ax: number, ay: number, bx: number, by: number) {
[ax, ay, bx, by] = asInt(ax, ay, bx, by);
Expand All @@ -26,7 +26,8 @@ export function* line(ax: number, ay: number, bx: number, by: number) {

/**
* Version of {@link line} yielding only coordinates in rect defined by
* `left,top`..`right,bottom`.
* `left,top`..`right,bottom`. Returns undefined if circle lies completely
* outside given clip rectangle.
*
* @param x1
* @param y1
Expand All @@ -46,4 +47,7 @@ export const lineClipped = (
top: number,
right: number,
bottom: number
) => clipped(line(x1, y1, x2, y2), left, top, right, bottom);
) => {
const res = liangBarsky(x1, y1, x2, y2, left, top, right, bottom);
return res ? line(res[0], res[1], res[2], res[3]) : undefined;
};

0 comments on commit dc065ed

Please sign in to comment.