diff --git a/packages/geom/src/internal/path.ts b/packages/geom/src/internal/path.ts new file mode 100644 index 0000000000..b8f545b2b6 --- /dev/null +++ b/packages/geom/src/internal/path.ts @@ -0,0 +1,70 @@ +import type { Maybe } from "@thi.ng/api"; +import { map } from "@thi.ng/transducers/map"; +import { mapcat } from "@thi.ng/transducers/mapcat"; +import type { ReadonlyVec } from "@thi.ng/vectors"; +import { equals } from "@thi.ng/vectors/equals"; +import type { + Attribs, + PathSegment2, + PathSegment3, + SegmentType2, +} from "../api.js"; +import type { Cubic } from "../api/cubic.js"; +import type { Cubic3 } from "../api/cubic3.js"; +import type { Path } from "../api/path.js"; +import type { Path3 } from "../api/path3.js"; +import { asCubic } from "../as-cubic.js"; +import { __copySegment } from "./copy.js"; + +interface PathType { + 2: { path: Path; ctor: typeof Path; cubic: Cubic; seg: PathSegment2 }; + 3: { path: Path3; ctor: typeof Path3; cubic: Cubic3; seg: PathSegment3 }; +} + +export const __pathFromCubics = ( + ctor: PathType[T]["ctor"], + cubics: PathType[T]["cubic"][], + attribs?: Attribs +): PathType[T]["path"] => { + let subPaths: PathType[T]["seg"][][] = []; + let curr: PathType[T]["seg"][]; + let lastP: Maybe; + const $beginPath = (c: PathType[T]["cubic"]) => { + curr = [{ type: "m", point: c.points[0] }]; + subPaths.push(curr); + }; + for (let c of cubics) { + if (!(lastP && equals(lastP, c.points[0]))) $beginPath(c); + curr!.push({ type: "c", geo: c }); + lastP = c.points[3]; + } + const path = new ctor( + subPaths[0], + subPaths.slice(1), + attribs || cubics[0].attribs + ); + return path; +}; + +export const __normalizedPath = ( + ctor: PathType[T]["ctor"], + path: PathType[T]["path"], + only?: SegmentType2[] +): PathType[T]["path"] => { + const $normalize = (segments: PathType[T]["seg"][]) => [ + ...mapcat((s) => { + if (s.geo && (!only || only.includes(s.type))) { + return map( + (c) => { type: "c", geo: c }, + asCubic(s.geo) + ); + } + return [__copySegment(s)]; + }, segments), + ]; + return new ctor( + $normalize(path.segments), + path.subPaths.map($normalize), + path.attribs + ); +}; diff --git a/packages/geom/src/path.ts b/packages/geom/src/path.ts index 29f2cb7ae2..6f585ce621 100644 --- a/packages/geom/src/path.ts +++ b/packages/geom/src/path.ts @@ -1,14 +1,9 @@ -import type { Maybe } from "@thi.ng/api"; import { isNumber } from "@thi.ng/checks/is-number"; +import type { Vec } from "@thi.ng/vectors"; import type { Attribs, PathSegment2, SegmentType2 } from "./api.js"; -import { map } from "@thi.ng/transducers/map"; -import { mapcat } from "@thi.ng/transducers/mapcat"; -import type { ReadonlyVec, Vec } from "@thi.ng/vectors"; -import { equals2 } from "@thi.ng/vectors/equals"; import type { Cubic } from "./api/cubic.js"; import { Path } from "./api/path.js"; -import { asCubic } from "./as-cubic.js"; -import { __copySegment } from "./internal/copy.js"; +import { __normalizedPath, __pathFromCubics } from "./internal/path.js"; import { PathBuilder } from "./path-builder.js"; /** @@ -34,7 +29,7 @@ export const path = ( * `attribs`. * * @remarks - * If no `attribs` are given, those from the first curve will be used. + * If no `attribs` are given, those from the first curve will be used (if any). * * For each successive curve segment, if the start point of the current curve is * not the same as the last point of the previous curve, a new sub path will be @@ -45,26 +40,8 @@ export const path = ( * @param cubics * @param attribs */ -export const pathFromCubics = (cubics: Cubic[], attribs?: Attribs) => { - let subPaths: PathSegment2[][] = []; - let curr: PathSegment2[]; - let lastP: Maybe; - const $beginPath = (c: Cubic) => { - curr = [{ type: "m", point: c.points[0] }]; - subPaths.push(curr); - }; - for (let c of cubics) { - if (!(lastP && equals2(lastP, c.points[0]))) $beginPath(c); - curr!.push({ type: "c", geo: c }); - lastP = c.points[3]; - } - const path = new Path( - subPaths[0], - subPaths.slice(1), - attribs || cubics[0].attribs - ); - return path; -}; +export const pathFromCubics = (cubics: Cubic[], attribs?: Attribs) => + __pathFromCubics<2>(Path, cubics, attribs); /** * Converts given path into a new one with segments converted to {@link Cubic} @@ -80,25 +57,7 @@ export const pathFromCubics = (cubics: Cubic[], attribs?: Attribs) => { export const normalizedPath = ( path: Path, only?: Extract[] -) => { - const $normalize = (segments: PathSegment2[]) => [ - ...mapcat((s) => { - s.type; - if (s.geo && (!only || only.includes(s.type))) { - return map( - (c) => ({ type: "c", geo: c }), - asCubic(s.geo) - ); - } - return [__copySegment(s)]; - }, segments), - ]; - return new Path( - $normalize(path.segments), - path.subPaths.map($normalize), - path.attribs - ); -}; +) => __normalizedPath<2>(Path, path, only); /** * Creates a new rounded rect {@link Path}, using the given corner radius or diff --git a/packages/geom/src/path3.ts b/packages/geom/src/path3.ts index eee29fbcfe..99bbe93b3f 100644 --- a/packages/geom/src/path3.ts +++ b/packages/geom/src/path3.ts @@ -1,13 +1,7 @@ -import type { Maybe } from "@thi.ng/api"; import type { Attribs, PathSegment3, SegmentType3 } from "./api.js"; -import { map } from "@thi.ng/transducers/map"; -import { mapcat } from "@thi.ng/transducers/mapcat"; -import type { ReadonlyVec } from "@thi.ng/vectors"; -import { equals3 } from "@thi.ng/vectors/equals"; import type { Cubic3 } from "./api/cubic3.js"; import { Path3 } from "./api/path3.js"; -import { asCubic } from "./as-cubic.js"; -import { __copySegment } from "./internal/copy.js"; +import { __normalizedPath, __pathFromCubics } from "./internal/path.js"; /** * Creates a new {@link Path3} instance, optional with given `segments`, @@ -32,7 +26,7 @@ export const path3 = ( * `attribs`. * * @remarks - * If no `attribs` are given, those from the first curve will be used. + * If no `attribs` are given, those from the first curve will be used (if any). * * For each successive curve segment, if the start point of the current curve is * not the same as the last point of the previous curve, a new sub path will be @@ -43,26 +37,8 @@ export const path3 = ( * @param cubics * @param attribs */ -export const pathFromCubics3 = (cubics: Cubic3[], attribs?: Attribs) => { - let subPaths: PathSegment3[][] = []; - let curr: PathSegment3[]; - let lastP: Maybe; - const $beginPath = (c: Cubic3) => { - curr = [{ type: "m", point: c.points[0] }]; - subPaths.push(curr); - }; - for (let c of cubics) { - if (!(lastP && equals3(lastP, c.points[0]))) $beginPath(c); - curr!.push({ type: "c", geo: c }); - lastP = c.points[3]; - } - const path = new Path3( - subPaths[0], - subPaths.slice(1), - attribs || cubics[0].attribs - ); - return path; -}; +export const pathFromCubics3 = (cubics: Cubic3[], attribs?: Attribs) => + __pathFromCubics<3>(Path3, cubics, attribs); /** * Converts given path into a new one with segments converted to {@link Cubic3} @@ -78,21 +54,4 @@ export const pathFromCubics3 = (cubics: Cubic3[], attribs?: Attribs) => { export const normalizedPath3 = ( path: Path3, only?: Extract[] -) => { - const $normalize = (segments: PathSegment3[]) => [ - ...mapcat((s) => { - if (s.geo && (!only || only.includes(s.type))) { - return map( - (c) => ({ type: "c", geo: c }), - asCubic(s.geo) - ); - } - return [__copySegment(s)]; - }, segments), - ]; - return new Path3( - $normalize(path.segments), - path.subPaths.map($normalize), - path.attribs - ); -}; +) => __normalizedPath<3>(Path3, path, only);