From 274ccd3e12541794802d9b30ecee4ab1e64e5776 Mon Sep 17 00:00:00 2001 From: Baku Hashimoto Date: Tue, 28 Nov 2023 15:35:26 +0900 Subject: [PATCH] Declare Path.transform and Path.toSVG and their tests --- src/Path.test.ts | 20 +++++++++++++++++ src/Path.ts | 57 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 src/Path.test.ts diff --git a/src/Path.test.ts b/src/Path.test.ts new file mode 100644 index 0000000..2d7fcd4 --- /dev/null +++ b/src/Path.test.ts @@ -0,0 +1,20 @@ +import '../jest.setup' + +import {Path} from './Path' + +describe('Path', () => { + it('should compute `toSVG` correctly', () => { + const path: Path = [ + ['M', [0, 0]], + ['L', [1, 1]], + ['L', [2, 2]], + ['Q', [3, 3], [4, 4]], + ['A', [1, 1], 1, false, true, [5, 5]], + ['Z'], + ] + + expect(Path.toSVG(path)).toEqual( + 'M 0,0 L 1,1 L 2,2 Q 3,3 4,4 A 1,1 1 0 1 5,5 Z' + ) + }) +}) diff --git a/src/Path.ts b/src/Path.ts index 57273d5..452b621 100644 --- a/src/Path.ts +++ b/src/Path.ts @@ -1,4 +1,6 @@ -import {vec2} from 'linearly' +import {mat2d, vec2} from 'linearly' + +import {toFixedSimple} from './utils' // Line commands export type CommandM = ['M', end: vec2] @@ -37,4 +39,55 @@ export type Command = export type Path = Command[] -export namespace Path {} +export namespace Path { + export function transform(path: Path, matrix: mat2d): Path { + return path.map(([command, ...points]) => { + switch (command) { + case 'M': + case 'L': + case 'Q': + case 'T': + case 'C': + case 'S': + return [ + command, + ...(points as vec2[]).map(p => vec2.transformMat2d(p, matrix)), + ] + case 'H': + return [ + command, + vec2.transformMat2d([points[0] as number, 0], matrix)[0], + ] + case 'V': + return [ + command, + vec2.transformMat2d([0, points[0] as number], matrix)[0], + ] + case 'A': + throw new Error('Not implemented') + case 'Z': + return ['Z'] + } + }) as Path + } + + export function toSVG(path: Path, fractionDigits = 2): string { + return path + .map(([command, ...ps]) => { + const strs = ps.map(p => { + if (typeof p === 'number') { + return toFixedSimple(p, fractionDigits) + } else if (typeof p === 'boolean') { + return p ? '1' : '0' + } else { + const x = toFixedSimple(p[0], fractionDigits) + const y = toFixedSimple(p[1], fractionDigits) + return `${x},${y}` + } + }) + + return [command, ...strs].join(' ') + }) + .join(' ') + } +}