Skip to content

Commit

Permalink
feat(geom): add centerOfWeight(), centroidOfBounds(), update centroid()
Browse files Browse the repository at this point in the history
- update centroid() impls for complex poly & poly, moved to centerOfWeight()
- update centroid() to support more types
- update pkg exports
  • Loading branch information
postspectacular committed Jun 19, 2024
1 parent a4dec6b commit 9048aac
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 32 deletions.
6 changes: 6 additions & 0 deletions packages/geom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,15 @@
"./bpatch": {
"default": "./bpatch.js"
},
"./center-of-weight": {
"default": "./center-of-weight.js"
},
"./center": {
"default": "./center.js"
},
"./centroid-of-bounds": {
"default": "./centroid-of-bounds.js"
},
"./centroid": {
"default": "./centroid.js"
},
Expand Down
50 changes: 50 additions & 0 deletions packages/geom/src/center-of-weight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { Maybe } from "@thi.ng/api";
import type { MultiFn1O } from "@thi.ng/defmulti";
import { DEFAULT, defmulti } from "@thi.ng/defmulti/defmulti";
import {
centerOfWeight2,
complexCenterOfWeight2,
} from "@thi.ng/geom-poly-utils/center-of-weight";
import type { Vec } from "@thi.ng/vectors";
import type { IShape } from "./api.js";
import type { ComplexPolygon } from "./api/complex-polygon.js";
import type { Polygon } from "./api/polygon.js";
import { centroid } from "./centroid.js";
import { __dispatch } from "./internal/dispatch.js";

/**
* Attempts to compute the center of weight (CoW) for given `shape`, otherwise
* returns `undefined`.
*
* @remarks
* The actual CoW is only available for these types:
*
* - {@link ComplexPolygon}
* - {@link Polygon}
*
* For all other shapes, the normal {@link centroid} calculation will be used.
* Also see {@link centroidOfBounds}.
*
* @param shape
* @param out
*/
export const centerOfWeight: MultiFn1O<IShape, Vec, Maybe<Vec>> = defmulti<
any,
Maybe<Vec>,
Maybe<Vec>
>(
__dispatch,
{},
{
[DEFAULT]: ($: IShape, out?: Vec) => centroid($, out),

complexpoly: ($: ComplexPolygon, out?) =>
complexCenterOfWeight2(
$.boundary.points,
$.children.map((c) => c.points),
out
),

poly: ($: Polygon, out) => centerOfWeight2($.points, out),
}
);
4 changes: 2 additions & 2 deletions packages/geom/src/center.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { Maybe } from "@thi.ng/api";
import type { MultiFn2 } from "@thi.ng/defmulti";
import { DEFAULT, defmulti } from "@thi.ng/defmulti/defmulti";
import type { IShape } from "./api.js";
import { ZERO2, ZERO3, type ReadonlyVec } from "@thi.ng/vectors/api";
import { set2, set3 } from "@thi.ng/vectors/set";
import { submN } from "@thi.ng/vectors/submn";
import type { IShape } from "./api.js";
import { Arc } from "./api/arc.js";
import { Circle } from "./api/circle.js";
import { Ellipse } from "./api/ellipse.js";
Expand All @@ -23,7 +23,7 @@ export type CenterFn = {

/**
* Returns copy of given shape centered around optionally provided point `p`
* (default: worldspace origin).
* (default: world space origin).
*
* @remarks
* Implemented for all shape types supported by {@link centroid} and
Expand Down
17 changes: 17 additions & 0 deletions packages/geom/src/centroid-of-bounds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Vec } from "@thi.ng/vectors";
import type { IShape } from "./api.js";
import { bounds } from "./bounds.js";
import { centroid } from "./centroid.js";

/**
* Attempts to compute the bounding box of given `shape` and if available,
* returns its centroid (or writes it to `out`, if given). Otherwise returns
* `undefined`.
*
* @param shape
* @param out
*/
export const centroidOfBounds = (shape: IShape, out?: Vec) => {
const b = bounds(shape);
return b ? centroid(b, out) : undefined;
};
54 changes: 30 additions & 24 deletions packages/geom/src/centroid.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
import type { Maybe } from "@thi.ng/api";
import type { MultiFn1O } from "@thi.ng/defmulti";
import { defmulti } from "@thi.ng/defmulti/defmulti";
import type { AABBLike, IShape, PCLike } from "./api.js";
import {
centerOfWeight2,
complexCenterOfWeight2,
} from "@thi.ng/geom-poly-utils/center-of-weight";
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 { addmN, addmN2 } from "@thi.ng/vectors/addmn";
import { maddN } from "@thi.ng/vectors/maddn";
import { mixN } from "@thi.ng/vectors/mixn";
import { mulN } from "@thi.ng/vectors/muln";
import { set } from "@thi.ng/vectors/set";
import type { AABBLike, IShape, PCLike } from "./api.js";
import type { Circle } from "./api/circle.js";
import type { ComplexPolygon } from "./api/complex-polygon.js";
import type { Group } from "./api/group.js";
Expand All @@ -25,10 +20,13 @@ import { bounds } from "./bounds.js";
import { __dispatch } from "./internal/dispatch.js";

/**
* Computes (possibly weighted) centroid of given shape, writes result in
* optionally provided output vector (or creates new one if omitted).
* Computes the unweighted centroid of given shape, writes result in optionally
* provided output vector (or creates new one if omitted).
*
* @remarks
* For vertex based shapes, this computes the simple mean position of the
* vertices. Also see {@link centroidOfBounds}, {@link centerOfWeight}.
*
* Currently implemented for:
*
* - {@link AABB}
Expand All @@ -43,12 +41,18 @@ import { __dispatch } from "./internal/dispatch.js";
* - {@link Line}
* - {@link Path}
* - {@link Polygon}
* - {@link Polygon3}
* - {@link Polyline}
* - {@link Polyline3}
* - {@link Points}
* - {@link Points3}
* - {@link Quad}
* - {@link Quad3}
* - {@link Quadratic}
* - {@link Text} - (no way to compute size, only position & any margin)
* - {@link Quadratic3}
* - {@link Text} (returns position, not considering body or alignment)
* - {@link Triangle}
* - {@link Triangle3}
*
* @param shape
* @param out
Expand All @@ -61,27 +65,33 @@ export const centroid: MultiFn1O<IShape, Vec, Maybe<Vec>> = defmulti<
__dispatch,
{
arc: "circle",
aabb: "rect",
bpatch: "points",
ellipse: "circle",
cubic: "points",
cubic3: "points",
line3: "line",
path: "group",
points3: "points",
poly: "points",
poly3: "points",
polyline: "points",
quad: "poly",
polyline3: "points",
quad: "points",
quad3: "points",
quadratic: "points",
quadratic3: "points",
rect: "aabb",
sphere: "circle",
text: "circle",
tri3: "tri",
},
{
abbb: ($: AABBLike, out?) => maddN(out || [], $.size, 0.5, $.pos),

circle: ($: Circle, out?) => set(out || [], $.pos),

complexpoly: ($: ComplexPolygon, out?) =>
complexCenterOfWeight2(
$.boundary.points,
$.children.map((c) => c.points),
out
),
_centroid($.boundary.points, out),

extra: () => undefined,

Expand All @@ -91,17 +101,13 @@ export const centroid: MultiFn1O<IShape, Vec, Maybe<Vec>> = defmulti<
},

line: ({ points }: Line, out?) =>
mixN(out || [], points[0], points[1], 0.5),
addmN2(out || [], points[0], points[1], 0.5),

points: ($: PCLike, out?) => _centroid($.points, out),

plane: ($: Plane, out?) => mulN(out || [], $.normal, $.w),

poly: ($: Polygon, out?) => centerOfWeight2($.points, out),

rect: ($: AABBLike, out?) => maddN(out || [], $.size, 0.5, $.pos),

tri: ({ points }: Triangle, out?) =>
addmN(null, add(out || [], points[0], points[1]), points[2], 1 / 3),
tri: ({ points: [a, b, c] }: Triangle, out?) =>
addmN(null, add(out || [], a, b), c, 1 / 3),
}
);
12 changes: 6 additions & 6 deletions packages/geom/src/fit-into-bounds.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { Maybe } from "@thi.ng/api";
import type { IShape, IShape2, IShape3 } from "./api.js";
import { minNonZero2, minNonZero3 } from "@thi.ng/math/interval";
import { safeDiv } from "@thi.ng/math/safe-div";
import type { MatOpNV, MatOpV } from "@thi.ng/matrices";
import { concat } from "@thi.ng/matrices/concat";
import { scale23, scale44 } from "@thi.ng/matrices/scale";
import { translation23, translation44 } from "@thi.ng/matrices/translation";
import type { ReadonlyVec, Vec } from "@thi.ng/vectors";
import { neg } from "@thi.ng/vectors/neg";
import { mulN2, mulN3 } from "@thi.ng/vectors/muln";
import type { IShape, IShape2, IShape3 } from "./api.js";
import type { AABB } from "./api/aabb.js";
import type { Arc } from "./api/arc.js";
import type { Circle } from "./api/circle.js";
Expand Down Expand Up @@ -61,7 +61,7 @@ export function fitIntoBounds2(shape: IShape2, dest: Rect) {
translation23,
scale23,
shape,
neg(null, c),
mulN2(null, c, -1),
centroid(dest)!,
minNonZero2(
safeDiv(dest.size[0], src.size[0]),
Expand All @@ -80,7 +80,7 @@ export const fitIntoBounds3 = <T extends IShape3>(
shape: T,
dest: AABB
): Maybe<T> => {
const src = <AABB>bounds(shape);
const src = bounds(shape);
if (!src) return;
const c = centroid(src);
if (!c) return;
Expand All @@ -89,7 +89,7 @@ export const fitIntoBounds3 = <T extends IShape3>(
translation44,
scale44,
shape,
neg(null, c),
mulN3(null, c, -1),
centroid(dest)!,
minNonZero3(
safeDiv(dest.size[0], src.size[0]),
Expand Down Expand Up @@ -128,7 +128,7 @@ export const fitAllIntoBounds2 = (shapes: IShape2[], dest: Rect) => {
translation23,
scale23,
s,
neg(null, c1),
mulN2(null, c1, -1),
c2,
smat
)
Expand Down

0 comments on commit 9048aac

Please sign in to comment.