@zseven-w/pen-renderer 0.6.0 → 0.7.1
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.
- package/LICENSE +21 -0
- package/README.md +153 -27
- package/package.json +23 -9
- package/src/__tests__/document-flattener.test.ts +166 -90
- package/src/__tests__/font-manager.test.ts +65 -0
- package/src/__tests__/image-loader.test.ts +136 -0
- package/src/__tests__/paint-utils.test.ts +61 -0
- package/src/__tests__/render-node-thumbnail.test.ts +312 -0
- package/src/document-flattener.ts +222 -159
- package/src/font-manager.ts +221 -190
- package/src/image-loader.ts +138 -51
- package/src/index.ts +18 -17
- package/src/init.ts +50 -21
- package/src/node-renderer.ts +957 -386
- package/src/paint-utils.ts +99 -74
- package/src/path-utils.ts +235 -115
- package/src/render-node-thumbnail.ts +155 -0
- package/src/renderer.ts +196 -175
- package/src/spatial-index.ts +139 -27
- package/src/text-renderer.ts +360 -302
- package/src/types.ts +18 -22
- package/src/viewport.ts +28 -29
package/src/types.ts
CHANGED
|
@@ -1,40 +1,36 @@
|
|
|
1
|
-
import type { PenNode } from '@zseven-w/pen-types'
|
|
1
|
+
import type { PenNode } from '@zseven-w/pen-types';
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
node: PenNode
|
|
5
|
-
absX: number
|
|
6
|
-
absY: number
|
|
7
|
-
absW: number
|
|
8
|
-
absH: number
|
|
9
|
-
clipRect?: { x: number; y: number; w: number; h: number; rx: number }
|
|
10
|
-
}
|
|
3
|
+
export type { ViewportState } from '@zseven-w/pen-types';
|
|
11
4
|
|
|
12
|
-
export interface
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
export interface RenderNode {
|
|
6
|
+
node: PenNode;
|
|
7
|
+
absX: number;
|
|
8
|
+
absY: number;
|
|
9
|
+
absW: number;
|
|
10
|
+
absH: number;
|
|
11
|
+
clipRect?: { x: number; y: number; w: number; h: number; rx: number };
|
|
16
12
|
}
|
|
17
13
|
|
|
18
14
|
/** Injectable icon lookup function for resolving icon names to SVG path data. */
|
|
19
15
|
export interface IconLookupFn {
|
|
20
|
-
(name: string): { d: string; iconId: string; style: 'stroke' | 'fill' } | null
|
|
16
|
+
(name: string): { d: string; iconId: string; style: 'stroke' | 'fill' } | null;
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
export interface PenRendererOptions {
|
|
24
20
|
/** URL pattern for CanvasKit WASM files. Default: '/canvaskit/' */
|
|
25
|
-
canvasKitPath?: string | ((file: string) => string)
|
|
21
|
+
canvasKitPath?: string | ((file: string) => string);
|
|
26
22
|
/** Base URL for bundled font files. Default: '/fonts/' */
|
|
27
|
-
fontBasePath?: string
|
|
23
|
+
fontBasePath?: string;
|
|
28
24
|
/** Custom Google Fonts CSS endpoint. Default: 'https://fonts.googleapis.com/css2' */
|
|
29
|
-
googleFontsCssUrl?: string
|
|
25
|
+
googleFontsCssUrl?: string;
|
|
30
26
|
/** Icon lookup function. Default: null (icons render as fallback circle) */
|
|
31
|
-
iconLookup?: IconLookupFn
|
|
27
|
+
iconLookup?: IconLookupFn;
|
|
32
28
|
/** Theme variant to use for variable resolution. Default: first variant per axis */
|
|
33
|
-
themeVariant?: Record<string, string
|
|
29
|
+
themeVariant?: Record<string, string>;
|
|
34
30
|
/** Background color. Default: '#1a1a1a' */
|
|
35
|
-
backgroundColor?: string
|
|
31
|
+
backgroundColor?: string;
|
|
36
32
|
/** Device pixel ratio override. Default: window.devicePixelRatio */
|
|
37
|
-
devicePixelRatio?: number
|
|
33
|
+
devicePixelRatio?: number;
|
|
38
34
|
/** Default fonts to preload. Default: ['Inter', 'Noto Sans SC'] */
|
|
39
|
-
defaultFonts?: string[]
|
|
35
|
+
defaultFonts?: string[];
|
|
40
36
|
}
|
package/src/viewport.ts
CHANGED
|
@@ -1,48 +1,46 @@
|
|
|
1
|
-
import { MIN_ZOOM, MAX_ZOOM } from '@zseven-w/pen-core'
|
|
2
|
-
import type { ViewportState } from './types.js'
|
|
1
|
+
import { MIN_ZOOM, MAX_ZOOM } from '@zseven-w/pen-core';
|
|
2
|
+
import type { ViewportState } from './types.js';
|
|
3
3
|
|
|
4
|
-
export type { ViewportState } from './types.js'
|
|
4
|
+
export type { ViewportState } from './types.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Compute the 3x3 transform matrix for CanvasKit from viewport state.
|
|
8
8
|
* CanvasKit uses column-major [scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2]
|
|
9
9
|
*/
|
|
10
10
|
export function viewportMatrix(vp: ViewportState): number[] {
|
|
11
|
-
return [
|
|
12
|
-
vp.zoom, 0, vp.panX,
|
|
13
|
-
0, vp.zoom, vp.panY,
|
|
14
|
-
0, 0, 1,
|
|
15
|
-
]
|
|
11
|
+
return [vp.zoom, 0, vp.panX, 0, vp.zoom, vp.panY, 0, 0, 1];
|
|
16
12
|
}
|
|
17
13
|
|
|
18
14
|
/**
|
|
19
15
|
* Convert screen (client) coordinates to scene coordinates.
|
|
20
16
|
*/
|
|
21
17
|
export function screenToScene(
|
|
22
|
-
clientX: number,
|
|
18
|
+
clientX: number,
|
|
19
|
+
clientY: number,
|
|
23
20
|
canvasRect: DOMRect,
|
|
24
21
|
vp: ViewportState,
|
|
25
22
|
): { x: number; y: number } {
|
|
26
|
-
const sx = clientX - canvasRect.left
|
|
27
|
-
const sy = clientY - canvasRect.top
|
|
23
|
+
const sx = clientX - canvasRect.left;
|
|
24
|
+
const sy = clientY - canvasRect.top;
|
|
28
25
|
return {
|
|
29
26
|
x: (sx - vp.panX) / vp.zoom,
|
|
30
27
|
y: (sy - vp.panY) / vp.zoom,
|
|
31
|
-
}
|
|
28
|
+
};
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
/**
|
|
35
32
|
* Convert scene coordinates to screen coordinates.
|
|
36
33
|
*/
|
|
37
34
|
export function sceneToScreen(
|
|
38
|
-
sceneX: number,
|
|
35
|
+
sceneX: number,
|
|
36
|
+
sceneY: number,
|
|
39
37
|
canvasRect: DOMRect,
|
|
40
38
|
vp: ViewportState,
|
|
41
39
|
): { x: number; y: number } {
|
|
42
40
|
return {
|
|
43
41
|
x: sceneX * vp.zoom + vp.panX + canvasRect.left,
|
|
44
42
|
y: sceneY * vp.zoom + vp.panY + canvasRect.top,
|
|
45
|
-
}
|
|
43
|
+
};
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
/**
|
|
@@ -50,23 +48,24 @@ export function sceneToScreen(
|
|
|
50
48
|
*/
|
|
51
49
|
export function zoomToPoint(
|
|
52
50
|
vp: ViewportState,
|
|
53
|
-
screenX: number,
|
|
51
|
+
screenX: number,
|
|
52
|
+
screenY: number,
|
|
54
53
|
canvasRect: DOMRect,
|
|
55
54
|
newZoom: number,
|
|
56
55
|
): ViewportState {
|
|
57
|
-
const clampedZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, newZoom))
|
|
58
|
-
const sx = screenX - canvasRect.left
|
|
59
|
-
const sy = screenY - canvasRect.top
|
|
56
|
+
const clampedZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, newZoom));
|
|
57
|
+
const sx = screenX - canvasRect.left;
|
|
58
|
+
const sy = screenY - canvasRect.top;
|
|
60
59
|
|
|
61
60
|
// The scene point under the cursor should stay fixed
|
|
62
|
-
const sceneX = (sx - vp.panX) / vp.zoom
|
|
63
|
-
const sceneY = (sy - vp.panY) / vp.zoom
|
|
61
|
+
const sceneX = (sx - vp.panX) / vp.zoom;
|
|
62
|
+
const sceneY = (sy - vp.panY) / vp.zoom;
|
|
64
63
|
|
|
65
64
|
return {
|
|
66
65
|
zoom: clampedZoom,
|
|
67
66
|
panX: sx - sceneX * clampedZoom,
|
|
68
67
|
panY: sy - sceneY * clampedZoom,
|
|
69
|
-
}
|
|
68
|
+
};
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
/**
|
|
@@ -79,11 +78,11 @@ export function getViewportBounds(
|
|
|
79
78
|
margin = 0,
|
|
80
79
|
) {
|
|
81
80
|
return {
|
|
82
|
-
left:
|
|
83
|
-
top:
|
|
81
|
+
left: -vp.panX / vp.zoom - margin,
|
|
82
|
+
top: -vp.panY / vp.zoom - margin,
|
|
84
83
|
right: (-vp.panX + canvasWidth) / vp.zoom + margin,
|
|
85
84
|
bottom: (-vp.panY + canvasHeight) / vp.zoom + margin,
|
|
86
|
-
}
|
|
85
|
+
};
|
|
87
86
|
}
|
|
88
87
|
|
|
89
88
|
/**
|
|
@@ -94,9 +93,9 @@ export function isRectInViewport(
|
|
|
94
93
|
vpBounds: ReturnType<typeof getViewportBounds>,
|
|
95
94
|
): boolean {
|
|
96
95
|
return !(
|
|
97
|
-
rect.x + rect.w < vpBounds.left
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)
|
|
96
|
+
rect.x + rect.w < vpBounds.left ||
|
|
97
|
+
rect.x > vpBounds.right ||
|
|
98
|
+
rect.y + rect.h < vpBounds.top ||
|
|
99
|
+
rect.y > vpBounds.bottom
|
|
100
|
+
);
|
|
102
101
|
}
|