thi-ng/umbrella

View on GitHub
packages/geom-clip-poly/src/index.ts

Summary

Maintainability
A
1 hr
Test Coverage
import { intersectLineLine } from "@thi.ng/geom-isec/line-line";
import { centroid } from "@thi.ng/geom-poly-utils/centroid";
import { EPS } from "@thi.ng/math/api";
import type { ReadonlyVec, Vec } from "@thi.ng/vectors";
import { corner2 } from "@thi.ng/vectors/clockwise";

/**
 * Extended version of Sutherland-Hodgeman convex polygon clipping supporting
 * any convex boundary polygon (not only rects). Returns new array of clipped
 * vertices.
 *
 * https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
 *
 * @param pts - subject poly vertices
 * @param bounds - clipping boundary vertices
 * @param bc - pre-computed boundary centroid
 * @param eps - edge classification tolerance
 */
export const sutherlandHodgeman = (
    pts: ReadonlyVec[],
    bounds: ReadonlyVec[],
    bc?: ReadonlyVec,
    eps = EPS
) => {
    bc = bc || centroid(bounds);
    for (let ne = bounds.length, j = ne - 1, i = 0; i < ne; j = i, i++) {
        const clipped: Vec[] = [];
        const ca = bounds[j];
        const cb = bounds[i];
        const sign = corner2(ca, cb, bc, eps);
        for (let np = pts.length, k = np - 1, l = 0; l < np; k = l, l++) {
            const p = pts[k];
            const q = pts[l];
            const cqsign = corner2(ca, cb, q, eps);
            if (corner2(ca, cb, p, eps) === sign) {
                clipped.push(
                    cqsign !== sign
                        ? intersectLineLine(ca, cb, p, q).isec![0]
                        : q
                );
            } else if (cqsign === sign) {
                clipped.push(intersectLineLine(ca, cb, p, q).isec![0], q);
            }
        }
        if (clipped.length < 2) {
            return [];
        }
        pts = clipped;
    }
    return pts;
};