|
1 |
| -import type { MultiFn1O } from "@thi.ng/defmulti"; |
| 1 | +import type { MultiFn2O } from "@thi.ng/defmulti"; |
2 | 2 | import { DEFAULT, defmulti } from "@thi.ng/defmulti/defmulti";
|
3 |
| -import type { Attribs, IShape, PathSegment } from "@thi.ng/geom-api"; |
| 3 | +import type { Attribs, CubicOpts, IShape, PathSegment } from "@thi.ng/geom-api"; |
4 | 4 | import type { ReadonlyVec } from "@thi.ng/vectors";
|
5 | 5 | import { copy } from "@thi.ng/vectors/copy";
|
| 6 | +import type { Arc } from "./api/arc.js"; |
6 | 7 | import type { ComplexPolygon } from "./api/complex-polygon.js";
|
7 | 8 | import { Line } from "./api/line.js";
|
8 | 9 | import { Path } from "./api/path.js";
|
9 | 10 | import type { Polygon } from "./api/polygon.js";
|
10 | 11 | import type { Polyline } from "./api/polyline.js";
|
11 |
| -import type { Rect } from "./api/rect.js"; |
12 | 12 | import { asCubic } from "./as-cubic.js";
|
| 13 | +import { asPolygon } from "./as-polygon.js"; |
| 14 | +import { asPolyline } from "./as-polyline.js"; |
13 | 15 | import { __copyAttribs } from "./internal/copy.js";
|
14 | 16 | import { __dispatch } from "./internal/dispatch.js";
|
15 | 17 | import { pathFromCubics } from "./path.js";
|
16 | 18 |
|
| 19 | +export interface AsPathOpts extends CubicOpts { |
| 20 | + /** |
| 21 | + * If true (default: false), creates path consisting of linear segments |
| 22 | + * only. |
| 23 | + */ |
| 24 | + linear: boolean; |
| 25 | +} |
| 26 | + |
17 | 27 | /**
|
18 | 28 | * Converts given shape into a {@link Path} (by default via {@link asCubic} and
|
19 | 29 | * {@link pathFromCubics}).
|
20 | 30 | *
|
21 | 31 | * @remarks
|
22 |
| - * The following shape types will be converted into paths consisting only of linear segments (NOT cubic beziers): |
23 |
| - * |
24 |
| - * - {@link ComplexPolygon} |
25 |
| - * - {@link Polygon} |
26 |
| - * - {@link Polyline} |
27 |
| - * - {@link Quad} |
28 |
| - * - {@link Rect} |
29 |
| - * - {@link Triangle} |
| 32 | + * If {@link AsPathOpts.linear} is enabled the shape will be converted into a |
| 33 | + * path consisting only of linear segments (NOT cubic beziers). As an interim |
| 34 | + * step this will involve a conversion via {@link asPolygon} or |
| 35 | + * {@link asPolyline} with default opts. |
30 | 36 | *
|
31 | 37 | * @param src
|
32 | 38 | * @param attribs
|
33 | 39 | */
|
34 |
| -export const asPath: MultiFn1O<IShape, Attribs, IShape> = defmulti< |
35 |
| - any, |
36 |
| - Attribs | undefined, |
37 |
| - IShape |
38 |
| ->( |
39 |
| - __dispatch, |
40 |
| - { quad: "poly", tri: "poly" }, |
41 |
| - { |
42 |
| - [DEFAULT]: (src: IShape, attribs?: Attribs) => |
43 |
| - pathFromCubics(asCubic(src), attribs || __copyAttribs(src)), |
| 40 | +export const asPath: (( |
| 41 | + shape: IShape, |
| 42 | + opts?: Partial<AsPathOpts>, |
| 43 | + attribs?: Attribs |
| 44 | +) => Path) & |
| 45 | + MultiFn2O<IShape, Partial<AsPathOpts> | undefined, Attribs, Path> = |
| 46 | + defmulti<any, Partial<AsPathOpts> | undefined, Attribs | undefined, Path>( |
| 47 | + __dispatch, |
| 48 | + { |
| 49 | + line: "polyline", |
| 50 | + quad: "poly", |
| 51 | + tri: "poly", |
| 52 | + }, |
| 53 | + { |
| 54 | + [DEFAULT]: ( |
| 55 | + src: IShape, |
| 56 | + opts?: Partial<AsPathOpts>, |
| 57 | + attribs?: Attribs |
| 58 | + ) => |
| 59 | + opts?.linear |
| 60 | + ? asPath( |
| 61 | + asPolygon(src)[0], |
| 62 | + opts, |
| 63 | + attribs || __copyAttribs(src) |
| 64 | + ) |
| 65 | + : __defaultImpl(src, opts, attribs), |
44 | 66 |
|
45 |
| - complexpoly: ($: ComplexPolygon, attribs) => |
46 |
| - new Path( |
47 |
| - __lineSegments($.boundary.points, true), |
48 |
| - $.children.map((c) => __lineSegments(c.points, true)), |
49 |
| - attribs || __copyAttribs($) |
50 |
| - ), |
| 67 | + arc: ($: Arc, opts, attribs) => |
| 68 | + opts?.linear |
| 69 | + ? asPath( |
| 70 | + asPolyline($)[0], |
| 71 | + opts, |
| 72 | + attribs || __copyAttribs($) |
| 73 | + ) |
| 74 | + : __defaultImpl($, opts, attribs), |
51 | 75 |
|
52 |
| - poly: ($: Polygon, attribs) => |
53 |
| - new Path( |
54 |
| - __lineSegments($.points, true), |
55 |
| - [], |
56 |
| - attribs || __copyAttribs($) |
57 |
| - ), |
| 76 | + complexpoly: ($: ComplexPolygon, opts, attribs) => { |
| 77 | + attribs = attribs || __copyAttribs($); |
| 78 | + if (opts?.linear) { |
| 79 | + return new Path( |
| 80 | + __lineSegments($.boundary.points, true), |
| 81 | + $.children.map((c) => __lineSegments(c.points, true)), |
| 82 | + attribs |
| 83 | + ); |
| 84 | + } |
| 85 | + const res = pathFromCubics(asCubic($.boundary, opts), attribs); |
| 86 | + for (let child of $.children) { |
| 87 | + res.addSubPaths( |
| 88 | + pathFromCubics(asCubic(child, opts)).segments |
| 89 | + ); |
| 90 | + } |
| 91 | + return res; |
| 92 | + }, |
58 | 93 |
|
59 |
| - polyline: ($: Polyline, attribs) => |
60 |
| - new Path( |
61 |
| - __lineSegments($.points, false), |
62 |
| - [], |
63 |
| - attribs || __copyAttribs($) |
64 |
| - ), |
| 94 | + poly: ($: Polygon, opts, attribs) => |
| 95 | + opts?.linear |
| 96 | + ? new Path( |
| 97 | + __lineSegments($.points, true), |
| 98 | + [], |
| 99 | + attribs || __copyAttribs($) |
| 100 | + ) |
| 101 | + : __defaultImpl($, opts, attribs), |
65 | 102 |
|
66 |
| - rect: ($: Rect, attribs) => { |
67 |
| - const max = $.max(); |
68 |
| - return new Path( |
69 |
| - __lineSegments( |
70 |
| - [$.pos, [max[0], $.pos[1]], max, [$.pos[0], max[1]]], |
71 |
| - false |
72 |
| - ), |
73 |
| - [], |
74 |
| - attribs || __copyAttribs($) |
75 |
| - ); |
76 |
| - }, |
77 |
| - } |
78 |
| -); |
| 103 | + polyline: ($: Polyline, opts, attribs) => |
| 104 | + opts?.linear |
| 105 | + ? new Path( |
| 106 | + __lineSegments($.points, false), |
| 107 | + [], |
| 108 | + attribs || __copyAttribs($) |
| 109 | + ) |
| 110 | + : __defaultImpl($, opts, attribs), |
| 111 | + } |
| 112 | + ); |
| 113 | + |
| 114 | +const __defaultImpl = ( |
| 115 | + src: IShape, |
| 116 | + opts?: Partial<CubicOpts>, |
| 117 | + attribs?: Attribs |
| 118 | +) => pathFromCubics(asCubic(src, opts), attribs || __copyAttribs(src)); |
79 | 119 |
|
80 | 120 | const __lineSegments = (points: ReadonlyVec[], closed: boolean) => {
|
81 | 121 | if (!points.length) return [];
|
|
0 commit comments