Skip to content

Commit

Permalink
feat(geom): initial import ComplexPolygon & impls (#464)
Browse files Browse the repository at this point in the history
- add complexPolygon() factory fn
- add bounds() & centroid() impls
  • Loading branch information
postspectacular committed May 2, 2024
1 parent 2235202 commit ded007c
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 1 deletion.
51 changes: 51 additions & 0 deletions packages/geom/src/api/complex-polygon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { ensureArray } from "@thi.ng/arrays/ensure-array";
import type { Attribs, IHiccupShape } from "@thi.ng/geom-api";
import { __copyAttribs } from "../internal/copy.js";
import { Polygon } from "./polygon.js";

export class ComplexPolygon implements IHiccupShape {
children: Polygon[];

constructor(
public boundary: Polygon = new Polygon(),
children?: Iterable<Polygon>,
public attribs?: Attribs
) {
this.children = children ? ensureArray(children) : [];
}

get type() {
return "complexpoly";
}

addChild(poly: Polygon) {
this.children.push(poly);
}

copy(): ComplexPolygon {
return new ComplexPolygon(
this.boundary.copy(),
this.children.map((h) => h.copy()),
__copyAttribs(this)
);
}

withAttribs(attribs: Attribs): ComplexPolygon {
return new ComplexPolygon(this.boundary, this.children, attribs);
}

toHiccup() {
const segments: any[] = [];
const $hiccupSegments = ({ points }: Polygon) => {
if (!points.length) return;
segments.push(["M", points[0]]);
for (let i = 1, n = points.length; i < n; i++) {
segments.push(["L", points[i]]);
}
segments.push(["Z"]);
};
$hiccupSegments(this.boundary);
for (let c of this.children) $hiccupSegments(c);
return ["path", this.attribs, segments];
}
}
6 changes: 6 additions & 0 deletions packages/geom/src/bounds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { subN2 } from "@thi.ng/vectors/subn";
import { aabbFromMinMaxWithMargin } from "./aabb.js";
import type { Arc } from "./api/arc.js";
import type { Circle } from "./api/circle.js";
import type { ComplexPolygon } from "./api/complex-polygon.js";
import type { Cubic } from "./api/cubic.js";
import type { Ellipse } from "./api/ellipse.js";
import type { Group } from "./api/group.js";
Expand Down Expand Up @@ -87,6 +88,11 @@ export const bounds: MultiFn1O<IShape, number, Maybe<AABBLike>> = defmulti<
mulN2(null, [2, 2], $.r + margin)
),

complexpoly: ($: ComplexPolygon, margin = 0) => {
const res = __collBounds([$.boundary, ...$.children], bounds);
return res ? new Rect(...res).offset(margin) : undefined;
},

cubic: ({ points }: Cubic, margin = 0) =>
rectFromMinMaxWithMargin(
...cubicBounds(points[0], points[1], points[2], points[3]),
Expand Down
29 changes: 28 additions & 1 deletion packages/geom/src/centroid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@ import { centroid as _centroid } from "@thi.ng/geom-poly-utils/centroid";
import type { Vec } from "@thi.ng/vectors";
import { add } from "@thi.ng/vectors/add";
import { addmN } from "@thi.ng/vectors/addmn";
import { maddN } from "@thi.ng/vectors/maddn";
import { divN2 } from "@thi.ng/vectors/divn";
import { maddN, maddN2 } from "@thi.ng/vectors/maddn";
import { mixN } from "@thi.ng/vectors/mixn";
import { msubN2 } from "@thi.ng/vectors/msubn";
import { mulN } from "@thi.ng/vectors/muln";
import { set } from "@thi.ng/vectors/set";
import type { Circle } from "./api/circle.js";
import type { ComplexPolygon } from "./api/complex-polygon.js";
import type { Group } from "./api/group.js";
import type { Line } from "./api/line.js";
import type { Plane } from "./api/plane.js";
import type { Polygon } from "./api/polygon.js";
import type { Triangle } from "./api/triangle.js";
import { area } from "./area.js";
import { bounds } from "./bounds.js";
import { __dispatch } from "./internal/dispatch.js";

Expand Down Expand Up @@ -69,6 +73,29 @@ export const centroid: MultiFn1O<IShape, Vec, Maybe<Vec>> = defmulti<
{
circle: ($: Circle, out?) => set(out || [], $.pos),

// https://math.stackexchange.com/a/623849
// ((Aout * Cout) - (Ain * Cin)) / (Aout - Ain)
complexpoly: ($: ComplexPolygon, out?) => {
const outerArea = Math.abs(area($.boundary));
let innerArea = 0;
let innerCentroid = [0, 0];
for (let child of $.children) {
const a = Math.abs(area(child));
innerArea += a;
maddN2(innerCentroid, centroid(child)!, a, innerCentroid);
}
return divN2(
null,
msubN2(
out || [],
centroid($.boundary)!,
outerArea,
innerCentroid
),
outerArea - innerArea
);
},

group: ($: Group, out?) => {
const b = bounds($);
return b ? centroid(b, out) : undefined;
Expand Down
9 changes: 9 additions & 0 deletions packages/geom/src/complex-polygon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Attribs } from "@thi.ng/geom-api";
import { ComplexPolygon } from "./api/complex-polygon.js";
import { Polygon } from "./api/polygon.js";

export const complexPolygon = (
boundary?: Polygon,
children?: Iterable<Polygon>,
attribs?: Attribs
) => new ComplexPolygon(boundary, children, attribs);
2 changes: 2 additions & 0 deletions packages/geom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from "./api/apc.js";
export * from "./api/arc.js";
export * from "./api/bpatch.js";
export * from "./api/circle.js";
export * from "./api/complex-polygon.js";
export * from "./api/cubic.js";
export * from "./api/ellipse.js";
export * from "./api/group.js";
Expand All @@ -25,6 +26,7 @@ export * from "./aabb.js";
export * from "./arc.js";
export * from "./bpatch.js";
export * from "./circle.js";
export * from "./complex-polygon.js";
export * from "./cubic.js";
export * from "./ellipse.js";
export * from "./group.js";
Expand Down

0 comments on commit ded007c

Please sign in to comment.