sketchmark 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +188 -0
  2. package/bin/sketchmark.cjs +2008 -0
  3. package/dist/src/builders/index.d.ts +74 -0
  4. package/dist/src/builders/index.js +230 -0
  5. package/dist/src/compounds.d.ts +13 -0
  6. package/dist/src/compounds.js +118 -0
  7. package/dist/src/deck.d.ts +4 -0
  8. package/dist/src/deck.js +91 -0
  9. package/dist/src/diagnostics.d.ts +5 -0
  10. package/dist/src/diagnostics.js +113 -0
  11. package/dist/src/export/index.d.ts +8 -0
  12. package/dist/src/export/index.js +15 -0
  13. package/dist/src/index.d.ts +19 -0
  14. package/dist/src/index.js +35 -0
  15. package/dist/src/kernel.d.ts +8 -0
  16. package/dist/src/kernel.js +68 -0
  17. package/dist/src/normalize.d.ts +6 -0
  18. package/dist/src/normalize.js +191 -0
  19. package/dist/src/patch.d.ts +5 -0
  20. package/dist/src/patch.js +72 -0
  21. package/dist/src/path-sampling.d.ts +3 -0
  22. package/dist/src/path-sampling.js +275 -0
  23. package/dist/src/player/index.d.ts +68 -0
  24. package/dist/src/player/index.js +600 -0
  25. package/dist/src/project.d.ts +11 -0
  26. package/dist/src/project.js +107 -0
  27. package/dist/src/render/html.d.ts +2 -0
  28. package/dist/src/render/html.js +13 -0
  29. package/dist/src/render/raw-three.d.ts +7 -0
  30. package/dist/src/render/raw-three.js +17 -0
  31. package/dist/src/render/svg.d.ts +3 -0
  32. package/dist/src/render/svg.js +277 -0
  33. package/dist/src/render/three-html.d.ts +2 -0
  34. package/dist/src/render/three-html.js +303 -0
  35. package/dist/src/render/three-preview-svg.d.ts +3 -0
  36. package/dist/src/render/three-preview-svg.js +102 -0
  37. package/dist/src/scenes.d.ts +4 -0
  38. package/dist/src/scenes.js +25 -0
  39. package/dist/src/schema.d.ts +2 -0
  40. package/dist/src/schema.js +403 -0
  41. package/dist/src/sequences.d.ts +43 -0
  42. package/dist/src/sequences.js +109 -0
  43. package/dist/src/shapes/builtins.d.ts +2 -0
  44. package/dist/src/shapes/builtins.js +429 -0
  45. package/dist/src/shapes/common.d.ts +9 -0
  46. package/dist/src/shapes/common.js +75 -0
  47. package/dist/src/shapes/geometry.d.ts +22 -0
  48. package/dist/src/shapes/geometry.js +166 -0
  49. package/dist/src/shapes/index.d.ts +2 -0
  50. package/dist/src/shapes/index.js +18 -0
  51. package/dist/src/shapes/registry.d.ts +9 -0
  52. package/dist/src/shapes/registry.js +35 -0
  53. package/dist/src/shapes/types.d.ts +34 -0
  54. package/dist/src/shapes/types.js +2 -0
  55. package/dist/src/types.d.ts +439 -0
  56. package/dist/src/types.js +2 -0
  57. package/dist/src/utils.d.ts +25 -0
  58. package/dist/src/utils.js +157 -0
  59. package/dist/src/validate.d.ts +2 -0
  60. package/dist/src/validate.js +434 -0
  61. package/dist/tests/run.d.ts +1 -0
  62. package/dist/tests/run.js +651 -0
  63. package/package.json +52 -0
  64. package/schema/visual.schema.json +930 -0
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pointOnPath = pointOnPath;
4
+ exports.samplePath = samplePath;
5
+ function pointOnPath(d, progress) {
6
+ const points = samplePath(d);
7
+ if (points.length === 0)
8
+ return undefined;
9
+ if (points.length === 1)
10
+ return points[0];
11
+ const clamped = clamp(progress, 0, 1);
12
+ const lengths = [];
13
+ let total = 0;
14
+ for (let index = 1; index < points.length; index += 1) {
15
+ const length = distance(points[index - 1], points[index]);
16
+ lengths.push(length);
17
+ total += length;
18
+ }
19
+ if (total <= 0)
20
+ return points[0];
21
+ const target = clamped * total;
22
+ let cursor = 0;
23
+ for (let index = 1; index < points.length; index += 1) {
24
+ const segment = lengths[index - 1];
25
+ if (cursor + segment >= target) {
26
+ const local = segment <= 0 ? 0 : (target - cursor) / segment;
27
+ return lerpPoint(points[index - 1], points[index], local);
28
+ }
29
+ cursor += segment;
30
+ }
31
+ return points[points.length - 1];
32
+ }
33
+ function samplePath(d) {
34
+ const tokens = tokenizePath(d);
35
+ const points = [];
36
+ let index = 0;
37
+ let command = "";
38
+ let current = [0, 0];
39
+ let subpathStart = [0, 0];
40
+ let lastCubicControl;
41
+ let lastQuadraticControl;
42
+ while (index < tokens.length) {
43
+ const token = tokens[index];
44
+ if (typeof token === "string") {
45
+ command = token;
46
+ index += 1;
47
+ }
48
+ if (!command)
49
+ break;
50
+ const relative = command === command.toLowerCase();
51
+ const upper = command.toUpperCase();
52
+ const nextPoint = () => {
53
+ const x = readNumber(tokens, index);
54
+ const y = readNumber(tokens, index + 1);
55
+ if (x === undefined || y === undefined)
56
+ return undefined;
57
+ index += 2;
58
+ return relative ? [current[0] + x, current[1] + y] : [x, y];
59
+ };
60
+ if (upper === "M") {
61
+ const point = nextPoint();
62
+ if (!point)
63
+ break;
64
+ current = point;
65
+ subpathStart = point;
66
+ points.push(current);
67
+ command = relative ? "l" : "L";
68
+ lastCubicControl = undefined;
69
+ lastQuadraticControl = undefined;
70
+ continue;
71
+ }
72
+ if (upper === "L") {
73
+ const point = nextPoint();
74
+ if (!point)
75
+ break;
76
+ current = point;
77
+ points.push(current);
78
+ lastCubicControl = undefined;
79
+ lastQuadraticControl = undefined;
80
+ continue;
81
+ }
82
+ if (upper === "H") {
83
+ const value = readNumber(tokens, index);
84
+ if (value === undefined)
85
+ break;
86
+ index += 1;
87
+ current = [relative ? current[0] + value : value, current[1]];
88
+ points.push(current);
89
+ lastCubicControl = undefined;
90
+ lastQuadraticControl = undefined;
91
+ continue;
92
+ }
93
+ if (upper === "V") {
94
+ const value = readNumber(tokens, index);
95
+ if (value === undefined)
96
+ break;
97
+ index += 1;
98
+ current = [current[0], relative ? current[1] + value : value];
99
+ points.push(current);
100
+ lastCubicControl = undefined;
101
+ lastQuadraticControl = undefined;
102
+ continue;
103
+ }
104
+ if (upper === "C") {
105
+ const start = current;
106
+ const c1 = nextPoint();
107
+ const c2 = nextPoint();
108
+ const end = nextPoint();
109
+ if (!c1 || !c2 || !end)
110
+ break;
111
+ for (let step = 1; step <= 24; step += 1)
112
+ points.push(cubicPoint(start, c1, c2, end, step / 24));
113
+ current = end;
114
+ lastCubicControl = c2;
115
+ lastQuadraticControl = undefined;
116
+ continue;
117
+ }
118
+ if (upper === "S") {
119
+ const start = current;
120
+ const reflected = lastCubicControl ? [current[0] * 2 - lastCubicControl[0], current[1] * 2 - lastCubicControl[1]] : current;
121
+ const c2 = nextPoint();
122
+ const end = nextPoint();
123
+ if (!c2 || !end)
124
+ break;
125
+ for (let step = 1; step <= 24; step += 1)
126
+ points.push(cubicPoint(start, reflected, c2, end, step / 24));
127
+ current = end;
128
+ lastCubicControl = c2;
129
+ lastQuadraticControl = undefined;
130
+ continue;
131
+ }
132
+ if (upper === "Q") {
133
+ const start = current;
134
+ const c = nextPoint();
135
+ const end = nextPoint();
136
+ if (!c || !end)
137
+ break;
138
+ for (let step = 1; step <= 20; step += 1)
139
+ points.push(quadraticPoint(start, c, end, step / 20));
140
+ current = end;
141
+ lastCubicControl = undefined;
142
+ lastQuadraticControl = c;
143
+ continue;
144
+ }
145
+ if (upper === "T") {
146
+ const start = current;
147
+ const reflected = lastQuadraticControl ? [current[0] * 2 - lastQuadraticControl[0], current[1] * 2 - lastQuadraticControl[1]] : current;
148
+ const end = nextPoint();
149
+ if (!end)
150
+ break;
151
+ for (let step = 1; step <= 20; step += 1)
152
+ points.push(quadraticPoint(start, reflected, end, step / 20));
153
+ current = end;
154
+ lastCubicControl = undefined;
155
+ lastQuadraticControl = reflected;
156
+ continue;
157
+ }
158
+ if (upper === "A") {
159
+ const rx = readNumber(tokens, index);
160
+ const ry = readNumber(tokens, index + 1);
161
+ const rotation = readNumber(tokens, index + 2);
162
+ const largeArcFlag = readNumber(tokens, index + 3);
163
+ const sweepFlag = readNumber(tokens, index + 4);
164
+ const rawEndX = readNumber(tokens, index + 5);
165
+ const rawEndY = readNumber(tokens, index + 6);
166
+ if (rx === undefined || ry === undefined || rotation === undefined || largeArcFlag === undefined || sweepFlag === undefined || rawEndX === undefined || rawEndY === undefined)
167
+ break;
168
+ index += 7;
169
+ const end = relative ? [current[0] + rawEndX, current[1] + rawEndY] : [rawEndX, rawEndY];
170
+ const arcPoints = sampleSvgArc(current, Math.abs(rx), Math.abs(ry), rotation, Boolean(largeArcFlag), Boolean(sweepFlag), end);
171
+ points.push(...arcPoints);
172
+ current = end;
173
+ lastCubicControl = undefined;
174
+ lastQuadraticControl = undefined;
175
+ continue;
176
+ }
177
+ if (upper === "Z") {
178
+ current = subpathStart;
179
+ points.push(current);
180
+ lastCubicControl = undefined;
181
+ lastQuadraticControl = undefined;
182
+ continue;
183
+ }
184
+ break;
185
+ }
186
+ return points;
187
+ }
188
+ function sampleSvgArc(start, rxInput, ryInput, rotation, largeArc, sweep, end) {
189
+ if (rxInput === 0 || ryInput === 0)
190
+ return [end];
191
+ if (nearPoint(start, end))
192
+ return [end];
193
+ let rx = rxInput;
194
+ let ry = ryInput;
195
+ const phi = (rotation * Math.PI) / 180;
196
+ const cosPhi = Math.cos(phi);
197
+ const sinPhi = Math.sin(phi);
198
+ const dx2 = (start[0] - end[0]) / 2;
199
+ const dy2 = (start[1] - end[1]) / 2;
200
+ const x1p = cosPhi * dx2 + sinPhi * dy2;
201
+ const y1p = -sinPhi * dx2 + cosPhi * dy2;
202
+ const radiusScale = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);
203
+ if (radiusScale > 1) {
204
+ const factor = Math.sqrt(radiusScale);
205
+ rx *= factor;
206
+ ry *= factor;
207
+ }
208
+ const rx2 = rx * rx;
209
+ const ry2 = ry * ry;
210
+ const x1p2 = x1p * x1p;
211
+ const y1p2 = y1p * y1p;
212
+ const sign = largeArc === sweep ? -1 : 1;
213
+ const numerator = Math.max(0, rx2 * ry2 - rx2 * y1p2 - ry2 * x1p2);
214
+ const denominator = Math.max(0.000001, rx2 * y1p2 + ry2 * x1p2);
215
+ const coef = sign * Math.sqrt(numerator / denominator);
216
+ const cxp = (coef * rx * y1p) / ry;
217
+ const cyp = (coef * -ry * x1p) / rx;
218
+ const cx = cosPhi * cxp - sinPhi * cyp + (start[0] + end[0]) / 2;
219
+ const cy = sinPhi * cxp + cosPhi * cyp + (start[1] + end[1]) / 2;
220
+ const vectorAngle = (ux, uy, vx, vy) => {
221
+ const dot = ux * vx + uy * vy;
222
+ const length = Math.max(0.000001, Math.hypot(ux, uy) * Math.hypot(vx, vy));
223
+ const signValue = ux * vy - uy * vx < 0 ? -1 : 1;
224
+ return signValue * Math.acos(clamp(dot / length, -1, 1));
225
+ };
226
+ const theta1 = vectorAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
227
+ let delta = vectorAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry);
228
+ if (!sweep && delta > 0)
229
+ delta -= Math.PI * 2;
230
+ if (sweep && delta < 0)
231
+ delta += Math.PI * 2;
232
+ const steps = Math.max(8, Math.ceil(Math.abs(delta) / (Math.PI / 12)));
233
+ const out = [];
234
+ for (let step = 1; step <= steps; step += 1) {
235
+ const theta = theta1 + delta * (step / steps);
236
+ const x = cosPhi * rx * Math.cos(theta) - sinPhi * ry * Math.sin(theta) + cx;
237
+ const y = sinPhi * rx * Math.cos(theta) + cosPhi * ry * Math.sin(theta) + cy;
238
+ out.push([x, y]);
239
+ }
240
+ return out;
241
+ }
242
+ function tokenizePath(d) {
243
+ const matches = d.match(/[AaCcHhLlMmQqSsTtVvZz]|[-+]?(?:\d*\.\d+|\d+)(?:e[-+]?\d+)?/g) ?? [];
244
+ return matches.map((token) => (/^[A-Za-z]$/.test(token) ? token : Number(token)));
245
+ }
246
+ function readNumber(tokens, index) {
247
+ const value = tokens[index];
248
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
249
+ }
250
+ function distance(a, b) {
251
+ return Math.hypot(b[0] - a[0], b[1] - a[1]);
252
+ }
253
+ function lerpPoint(a, b, t) {
254
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
255
+ }
256
+ function cubicPoint(a, b, c, d, t) {
257
+ const mt = 1 - t;
258
+ return [
259
+ mt * mt * mt * a[0] + 3 * mt * mt * t * b[0] + 3 * mt * t * t * c[0] + t * t * t * d[0],
260
+ mt * mt * mt * a[1] + 3 * mt * mt * t * b[1] + 3 * mt * t * t * c[1] + t * t * t * d[1]
261
+ ];
262
+ }
263
+ function quadraticPoint(a, b, c, t) {
264
+ const mt = 1 - t;
265
+ return [
266
+ mt * mt * a[0] + 2 * mt * t * b[0] + t * t * c[0],
267
+ mt * mt * a[1] + 2 * mt * t * b[1] + t * t * c[1]
268
+ ];
269
+ }
270
+ function nearPoint(left, right) {
271
+ return Math.abs(left[0] - right[0]) < 0.000001 && Math.abs(left[1] - right[1]) < 0.000001;
272
+ }
273
+ function clamp(value, min, max) {
274
+ return Math.max(min, Math.min(max, value));
275
+ }
@@ -0,0 +1,68 @@
1
+ import type { VisualDocument } from "../types";
2
+ import { documentForDeckStep } from "../deck";
3
+ export { documentForDeckStep };
4
+ export type { VisualDocument } from "../types";
5
+ export type BrowserExportFormat = "svg" | "png" | "jpg" | "html" | "json" | "mp4";
6
+ export type BrowserExportExtension = BrowserExportFormat | "webm";
7
+ export interface BrowserExportOptions {
8
+ title?: string;
9
+ filename?: string;
10
+ time?: number;
11
+ document?: VisualDocument;
12
+ sourceDocument?: VisualDocument;
13
+ }
14
+ export interface BrowserExportResult {
15
+ blob: Blob;
16
+ filename: string;
17
+ mimeType: string;
18
+ extension: BrowserExportExtension;
19
+ fallback?: "webm";
20
+ }
21
+ export interface VisualPlayerOptions {
22
+ document: VisualDocument;
23
+ autoplay?: boolean;
24
+ loop?: boolean;
25
+ onFrame?: (state: VisualPlayerState) => void;
26
+ onError?: (error: Error) => void;
27
+ }
28
+ export interface VisualPlayerState {
29
+ time: number;
30
+ duration: number;
31
+ playing: boolean;
32
+ }
33
+ export declare class VisualPlayer {
34
+ readonly ready: Promise<VisualPlayer>;
35
+ private root;
36
+ private document;
37
+ private loop;
38
+ private onFrame?;
39
+ private onError?;
40
+ private raf;
41
+ private startedAt;
42
+ private startedTime;
43
+ private currentTime;
44
+ private playing;
45
+ constructor(root: HTMLElement, options: VisualPlayerOptions);
46
+ play(): void;
47
+ pause(): void;
48
+ seek(time: number): void;
49
+ render(time?: number): void;
50
+ setDocument(document: VisualDocument): void;
51
+ getDocument(): VisualDocument;
52
+ getState(): VisualPlayerState;
53
+ toSvg(time?: number): string;
54
+ downloadSvg(filename?: string, time?: number): void;
55
+ exportBrowser(format: BrowserExportFormat, options?: BrowserExportOptions): Promise<BrowserExportResult>;
56
+ download(format: BrowserExportFormat, options?: BrowserExportOptions): Promise<BrowserExportResult>;
57
+ destroy(): void;
58
+ private tick;
59
+ private duration;
60
+ private emit;
61
+ private handleError;
62
+ }
63
+ export declare function createVisualPlayer(root: HTMLElement, options: VisualPlayerOptions): VisualPlayer;
64
+ export declare class SketchmarkPlayer extends VisualPlayer {
65
+ }
66
+ export declare function createSketchmarkPlayer(root: HTMLElement, options: VisualPlayerOptions): SketchmarkPlayer;
67
+ export declare function exportVisualFromBrowser(player: VisualPlayer, format: BrowserExportFormat, options?: BrowserExportOptions): Promise<BrowserExportResult>;
68
+ export declare function downloadBrowserExport(result: BrowserExportResult): void;