Skip to content

Commit

Permalink
feat(geom): update asPath(), add AsPathOpts
Browse files Browse the repository at this point in the history
BREAKING CHANGE: update asPath() args, add AsPathOpts as 2nd arg

- add option for using only linear path segments (no cubics)
- update impls for complexpoly & other polygon types
  • Loading branch information
postspectacular committed May 4, 2024
1 parent e3c9f20 commit ef0ebdf
Showing 1 changed file with 92 additions and 52 deletions.
144 changes: 92 additions & 52 deletions packages/geom/src/as-path.ts
@@ -1,81 +1,121 @@
import type { MultiFn1O } from "@thi.ng/defmulti";
import type { MultiFn2O } from "@thi.ng/defmulti";
import { DEFAULT, defmulti } from "@thi.ng/defmulti/defmulti";
import type { Attribs, IShape, PathSegment } from "@thi.ng/geom-api";
import type { Attribs, CubicOpts, IShape, PathSegment } from "@thi.ng/geom-api";
import type { ReadonlyVec } from "@thi.ng/vectors";
import { copy } from "@thi.ng/vectors/copy";
import type { Arc } from "./api/arc.js";
import type { ComplexPolygon } from "./api/complex-polygon.js";
import { Line } from "./api/line.js";
import { Path } from "./api/path.js";
import type { Polygon } from "./api/polygon.js";
import type { Polyline } from "./api/polyline.js";
import type { Rect } from "./api/rect.js";
import { asCubic } from "./as-cubic.js";
import { asPolygon } from "./as-polygon.js";
import { asPolyline } from "./as-polyline.js";
import { __copyAttribs } from "./internal/copy.js";
import { __dispatch } from "./internal/dispatch.js";
import { pathFromCubics } from "./path.js";

export interface AsPathOpts extends CubicOpts {
/**
* If true (default: false), creates path consisting of linear segments
* only.
*/
linear: boolean;
}

/**
* Converts given shape into a {@link Path} (by default via {@link asCubic} and
* {@link pathFromCubics}).
*
* @remarks
* The following shape types will be converted into paths consisting only of linear segments (NOT cubic beziers):
*
* - {@link ComplexPolygon}
* - {@link Polygon}
* - {@link Polyline}
* - {@link Quad}
* - {@link Rect}
* - {@link Triangle}
* If {@link AsPathOpts.linear} is enabled the shape will be converted into a
* path consisting only of linear segments (NOT cubic beziers). As an interim
* step this will involve a conversion via {@link asPolygon} or
* {@link asPolyline} with default opts.
*
* @param src
* @param attribs
*/
export const asPath: MultiFn1O<IShape, Attribs, IShape> = defmulti<
any,
Attribs | undefined,
IShape
>(
__dispatch,
{ quad: "poly", tri: "poly" },
{
[DEFAULT]: (src: IShape, attribs?: Attribs) =>
pathFromCubics(asCubic(src), attribs || __copyAttribs(src)),
export const asPath: ((
shape: IShape,
opts?: Partial<AsPathOpts>,
attribs?: Attribs
) => Path) &
MultiFn2O<IShape, Partial<AsPathOpts> | undefined, Attribs, Path> =
defmulti<any, Partial<AsPathOpts> | undefined, Attribs | undefined, Path>(
__dispatch,
{
line: "polyline",
quad: "poly",
tri: "poly",
},
{
[DEFAULT]: (
src: IShape,
opts?: Partial<AsPathOpts>,
attribs?: Attribs
) =>
opts?.linear
? asPath(
asPolygon(src)[0],
opts,
attribs || __copyAttribs(src)
)
: __defaultImpl(src, opts, attribs),

complexpoly: ($: ComplexPolygon, attribs) =>
new Path(
__lineSegments($.boundary.points, true),
$.children.map((c) => __lineSegments(c.points, true)),
attribs || __copyAttribs($)
),
arc: ($: Arc, opts, attribs) =>
opts?.linear
? asPath(
asPolyline($)[0],
opts,
attribs || __copyAttribs($)
)
: __defaultImpl($, opts, attribs),

poly: ($: Polygon, attribs) =>
new Path(
__lineSegments($.points, true),
[],
attribs || __copyAttribs($)
),
complexpoly: ($: ComplexPolygon, opts, attribs) => {
attribs = attribs || __copyAttribs($);
if (opts?.linear) {
return new Path(
__lineSegments($.boundary.points, true),
$.children.map((c) => __lineSegments(c.points, true)),
attribs
);
}
const res = pathFromCubics(asCubic($.boundary, opts), attribs);
for (let child of $.children) {
res.addSubPaths(
pathFromCubics(asCubic(child, opts)).segments
);
}
return res;
},

polyline: ($: Polyline, attribs) =>
new Path(
__lineSegments($.points, false),
[],
attribs || __copyAttribs($)
),
poly: ($: Polygon, opts, attribs) =>
opts?.linear
? new Path(
__lineSegments($.points, true),
[],
attribs || __copyAttribs($)
)
: __defaultImpl($, opts, attribs),

rect: ($: Rect, attribs) => {
const max = $.max();
return new Path(
__lineSegments(
[$.pos, [max[0], $.pos[1]], max, [$.pos[0], max[1]]],
false
),
[],
attribs || __copyAttribs($)
);
},
}
);
polyline: ($: Polyline, opts, attribs) =>
opts?.linear
? new Path(
__lineSegments($.points, false),
[],
attribs || __copyAttribs($)
)
: __defaultImpl($, opts, attribs),
}
);

const __defaultImpl = (
src: IShape,
opts?: Partial<CubicOpts>,
attribs?: Attribs
) => pathFromCubics(asCubic(src, opts), attribs || __copyAttribs(src));

const __lineSegments = (points: ReadonlyVec[], closed: boolean) => {
if (!points.length) return [];
Expand Down

0 comments on commit ef0ebdf

Please sign in to comment.