@emasoft/svg-matrix 1.0.5 → 1.0.7
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/README.md +391 -385
- package/bin/svg-matrix.js +1000 -0
- package/package.json +30 -2
- package/scripts/bootstrap_repo.sh +99 -0
- package/scripts/postinstall.js +252 -0
- package/src/browser-verify.js +463 -0
- package/src/clip-path-resolver.js +760 -0
- package/src/geometry-to-path.js +348 -0
- package/src/index.js +427 -6
- package/src/logger.js +302 -0
- package/src/marker-resolver.js +1006 -0
- package/src/mask-resolver.js +1407 -0
- package/src/mesh-gradient.js +1215 -0
- package/src/pattern-resolver.js +844 -0
- package/src/polygon-clip.js +1491 -0
- package/src/svg-flatten.js +1264 -105
- package/src/text-to-path.js +820 -0
- package/src/transforms2d.js +493 -37
- package/src/transforms3d.js +418 -47
- package/src/use-symbol-resolver.js +1126 -0
- package/samples/preserveAspectRatio_SVG.svg +0 -63
- package/samples/test.svg +0 -39
package/README.md
CHANGED
|
@@ -1,529 +1,535 @@
|
|
|
1
1
|
# @emasoft/svg-matrix
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
High-precision matrix, vector, and SVG transformation library for JavaScript. Built on decimal.js for 80-digit precision arithmetic.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
- **SVG transform flattening**: parse transform attributes, build CTMs, flatten nested hierarchies
|
|
11
|
-
- **Linear algebra**: LU/QR decomposition, determinant, inverse, solve, matrix exponential
|
|
12
|
-
- **10^77 times better precision** than JavaScript floats for round-trip transforms
|
|
13
|
-
- Works in **Node.js** and **browsers** (via CDN)
|
|
7
|
+
**Linear Algebra**
|
|
8
|
+
- Matrix and Vector classes with full linear algebra operations
|
|
9
|
+
- LU/QR decomposition, determinant, inverse, solve, matrix exponential
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
**Affine Transforms**
|
|
12
|
+
- 2D (3x3 homogeneous): translation, rotation, scale, skew, reflection
|
|
13
|
+
- 3D (4x4 homogeneous): translation, rotation (X/Y/Z/arbitrary axis), scale
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
**SVG Processing**
|
|
16
|
+
- Transform attribute parsing and CTM (Current Transform Matrix) building
|
|
17
|
+
- viewBox, preserveAspectRatio, nested viewports, unit resolution
|
|
18
|
+
- Shape-to-path conversion (circle, ellipse, rect, line, polygon, polyline)
|
|
19
|
+
- Path parsing, normalization (absolute, cubics), transformation
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
**SVG Element Resolution**
|
|
22
|
+
- ClipPath flattening to polygons
|
|
23
|
+
- Mask resolution (luminance and alpha)
|
|
24
|
+
- Pattern tiling expansion
|
|
25
|
+
- Use/symbol inlining with proper transforms
|
|
26
|
+
- Marker positioning and orientation
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
**Advanced**
|
|
29
|
+
- Polygon boolean operations (intersection, union, difference, convex hull)
|
|
30
|
+
- SVG 2.0 mesh gradient parsing and rasterization
|
|
31
|
+
- Text-to-path conversion with font support
|
|
32
|
+
- Browser verification against Chrome's native W3C SVG2 implementation
|
|
26
33
|
|
|
27
|
-
|
|
34
|
+
## Requirements
|
|
28
35
|
|
|
29
|
-
|
|
36
|
+
- **Node.js 24.0.0** or higher (ES modules, modern JavaScript features)
|
|
37
|
+
- **Playwright** (optional) - for browser verification features
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
<script type="module">
|
|
33
|
-
import { Decimal, Matrix, Vector, Transforms2D, Transforms3D, SVGFlatten } from 'https://esm.sh/@emasoft/svg-matrix';
|
|
39
|
+
## Precision
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
| Scenario | Float Error | This Library | Improvement |
|
|
42
|
+
|----------|-------------|--------------|-------------|
|
|
43
|
+
| GIS/CAD coordinates (1e6+ scale) | 1.69e-7 | 0 | 10^93x |
|
|
44
|
+
| 6-level SVG hierarchy | 1.14e-13 | 1e-77 | 10^64x |
|
|
45
|
+
| 1000 round-trip transforms | 5.41e-14 | 0 | 10^86x |
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
const M = Transforms2D.translation(2, 3)
|
|
39
|
-
.mul(Transforms2D.rotate(Math.PI / 4))
|
|
40
|
-
.mul(Transforms2D.scale(1.5));
|
|
47
|
+
**When precision matters:** GIS, CAD, scientific visualization, deep transform hierarchies, accumulated operations.
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
**When floats suffice:** Simple transforms, small coordinates, visual applications where sub-pixel errors are imperceptible.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
node test/benchmark-precision.js
|
|
46
53
|
```
|
|
47
54
|
|
|
48
|
-
|
|
55
|
+
## Installation
|
|
49
56
|
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
import { Matrix, Vector } from 'https://cdn.skypack.dev/@emasoft/svg-matrix';
|
|
53
|
-
</script>
|
|
57
|
+
```bash
|
|
58
|
+
npm install @emasoft/svg-matrix
|
|
54
59
|
```
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
```js
|
|
62
|
+
import { Matrix, Vector, Transforms2D, Transforms3D, SVGFlatten } from '@emasoft/svg-matrix';
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### CDN
|
|
57
66
|
|
|
58
67
|
```html
|
|
59
|
-
<script type="importmap">
|
|
60
|
-
{
|
|
61
|
-
"imports": {
|
|
62
|
-
"@emasoft/svg-matrix": "https://esm.sh/@emasoft/svg-matrix"
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
</script>
|
|
66
68
|
<script type="module">
|
|
67
|
-
import { Matrix, Vector, Transforms2D } from '
|
|
69
|
+
import { Matrix, Vector, Transforms2D } from 'https://esm.sh/@emasoft/svg-matrix';
|
|
68
70
|
</script>
|
|
69
71
|
```
|
|
70
72
|
|
|
71
|
-
##
|
|
73
|
+
## CLI
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
The library includes a command-line interface for batch processing SVG files.
|
|
74
76
|
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
+
```bash
|
|
78
|
+
# Process single file
|
|
79
|
+
svg-matrix flatten input.svg -o output.svg
|
|
77
80
|
|
|
78
|
-
|
|
81
|
+
# Batch process folder
|
|
82
|
+
svg-matrix flatten ./svgs/ -o ./output/
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
# Process files from list
|
|
85
|
+
svg-matrix flatten --list files.txt -o ./output/
|
|
86
|
+
|
|
87
|
+
# Convert shapes to paths
|
|
88
|
+
svg-matrix convert input.svg -o output.svg
|
|
89
|
+
|
|
90
|
+
# Normalize paths to cubic Beziers
|
|
91
|
+
svg-matrix normalize input.svg -o output.svg
|
|
82
92
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
console.log('Scale:', v.scale(2).toNumberArray()); // [2, 4, 6]
|
|
86
|
-
console.log('Dot:', v.dot(w).toString()); // 32
|
|
87
|
-
console.log('Cross:', v.cross(w).toNumberArray()); // [-3, 6, -3]
|
|
93
|
+
# Show SVG file info
|
|
94
|
+
svg-matrix info input.svg
|
|
88
95
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
# Show help
|
|
97
|
+
svg-matrix help
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### CLI Options
|
|
101
|
+
|
|
102
|
+
| Option | Description |
|
|
103
|
+
|--------|-------------|
|
|
104
|
+
| `-o, --output <path>` | Output file or directory |
|
|
105
|
+
| `-l, --list <file>` | Read input files from text file |
|
|
106
|
+
| `-r, --recursive` | Process directories recursively |
|
|
107
|
+
| `-p, --precision <n>` | Decimal precision (default: 6) |
|
|
108
|
+
| `-f, --force` | Overwrite existing files |
|
|
109
|
+
| `-n, --dry-run` | Show what would be done |
|
|
110
|
+
| `-q, --quiet` | Suppress all output except errors |
|
|
111
|
+
| `-v, --verbose` | Enable verbose/debug output |
|
|
112
|
+
| `--log-file <path>` | Write log to file |
|
|
93
113
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
### File List Format
|
|
115
|
+
|
|
116
|
+
Create a text file with one path per line:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
# This is a comment
|
|
120
|
+
./folder1/file1.svg
|
|
121
|
+
./folder2/file2.svg
|
|
122
|
+
./entire-folder/
|
|
97
123
|
```
|
|
98
124
|
|
|
99
|
-
|
|
125
|
+
## Quick Start
|
|
100
126
|
|
|
101
127
|
```js
|
|
102
|
-
import { Matrix } from '@emasoft/svg-matrix';
|
|
128
|
+
import { Decimal, Matrix, Vector, Transforms2D, SVGFlatten } from '@emasoft/svg-matrix';
|
|
103
129
|
|
|
104
|
-
|
|
105
|
-
|
|
130
|
+
Decimal.set({ precision: 80 });
|
|
131
|
+
|
|
132
|
+
// Compose transforms (right-to-left: scale first, then rotate, then translate)
|
|
133
|
+
const M = Transforms2D.translation(10, 20)
|
|
134
|
+
.mul(Transforms2D.rotate(Math.PI / 4))
|
|
135
|
+
.mul(Transforms2D.scale(2));
|
|
136
|
+
|
|
137
|
+
// Apply to point
|
|
138
|
+
const [x, y] = Transforms2D.applyTransform(M, 1, 0);
|
|
106
139
|
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
140
|
+
// Round-trip with inverse
|
|
141
|
+
const [xBack, yBack] = Transforms2D.applyTransform(M.inverse(), x, y);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## API Reference
|
|
112
147
|
|
|
113
|
-
|
|
114
|
-
const inv = A.inverse();
|
|
115
|
-
console.log('Inverse:', inv.toNumberArray());
|
|
148
|
+
### Linear Algebra
|
|
116
149
|
|
|
117
|
-
|
|
118
|
-
const x = A.solve([1, 1]);
|
|
119
|
-
console.log('Solution:', x.toNumberArray());
|
|
150
|
+
#### Vector
|
|
120
151
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
152
|
+
```js
|
|
153
|
+
import { Vector } from '@emasoft/svg-matrix';
|
|
154
|
+
|
|
155
|
+
const v = Vector.from([1, 2, 3]);
|
|
156
|
+
const w = Vector.from([4, 5, 6]);
|
|
124
157
|
|
|
125
|
-
//
|
|
126
|
-
|
|
158
|
+
v.add(w) // Element-wise addition
|
|
159
|
+
v.sub(w) // Element-wise subtraction
|
|
160
|
+
v.scale(2) // Scalar multiplication
|
|
161
|
+
v.dot(w) // Dot product → Decimal
|
|
162
|
+
v.cross(w) // Cross product (3D)
|
|
163
|
+
v.norm() // Euclidean length
|
|
164
|
+
v.normalize() // Unit vector
|
|
165
|
+
v.angleBetween(w) // Angle in radians
|
|
166
|
+
v.projectOnto(w) // Vector projection
|
|
167
|
+
v.orthogonal() // Perpendicular vector
|
|
168
|
+
v.distance(w) // Euclidean distance
|
|
169
|
+
v.toNumberArray() // [1, 2, 3]
|
|
127
170
|
```
|
|
128
171
|
|
|
129
|
-
|
|
172
|
+
#### Matrix
|
|
130
173
|
|
|
131
174
|
```js
|
|
132
|
-
import {
|
|
175
|
+
import { Matrix } from '@emasoft/svg-matrix';
|
|
133
176
|
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
177
|
+
const A = Matrix.from([[1, 2], [3, 4]]);
|
|
178
|
+
const I = Matrix.identity(3);
|
|
179
|
+
const Z = Matrix.zeros(2, 3);
|
|
180
|
+
|
|
181
|
+
A.add(B) // Element-wise addition
|
|
182
|
+
A.sub(B) // Element-wise subtraction
|
|
183
|
+
A.mul(B) // Matrix multiplication
|
|
184
|
+
A.transpose() // Transpose
|
|
185
|
+
A.trace() // Sum of diagonal
|
|
186
|
+
A.determinant() // Determinant
|
|
187
|
+
A.inverse() // Matrix inverse
|
|
188
|
+
A.solve([1, 1]) // Solve Ax = b
|
|
189
|
+
A.lu() // { L, U, P } decomposition
|
|
190
|
+
A.qr() // { Q, R } decomposition
|
|
191
|
+
A.exp() // Matrix exponential
|
|
192
|
+
A.applyToVector(v) // Matrix-vector product
|
|
193
|
+
```
|
|
139
194
|
|
|
140
|
-
|
|
141
|
-
const Rp = Transforms2D.rotateAroundPoint(Math.PI / 2, 5, 5);
|
|
195
|
+
### Transforms
|
|
142
196
|
|
|
143
|
-
|
|
144
|
-
const Sk = Transforms2D.skew(0.5, 0);
|
|
145
|
-
const St = Transforms2D.stretchAlongAxis(1, 0, 2); // stretch 2x along X
|
|
197
|
+
#### 2D (3x3 matrices)
|
|
146
198
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const Ry = Transforms2D.reflectY(); // flip X
|
|
150
|
-
const Ro = Transforms2D.reflectOrigin(); // flip both
|
|
199
|
+
```js
|
|
200
|
+
import { Transforms2D } from '@emasoft/svg-matrix';
|
|
151
201
|
|
|
152
|
-
|
|
153
|
-
|
|
202
|
+
Transforms2D.translation(tx, ty)
|
|
203
|
+
Transforms2D.scale(sx, sy) // sy defaults to sx
|
|
204
|
+
Transforms2D.rotate(theta) // radians
|
|
205
|
+
Transforms2D.rotateAroundPoint(theta, px, py)
|
|
206
|
+
Transforms2D.skew(ax, ay)
|
|
207
|
+
Transforms2D.stretchAlongAxis(ux, uy, k)
|
|
208
|
+
Transforms2D.reflectX() // flip across X axis
|
|
209
|
+
Transforms2D.reflectY() // flip across Y axis
|
|
210
|
+
Transforms2D.reflectOrigin()
|
|
154
211
|
|
|
155
212
|
// Apply to point
|
|
156
|
-
const [x, y] = Transforms2D.applyTransform(
|
|
157
|
-
|
|
158
|
-
// Inverse transform
|
|
159
|
-
const Minv = M.inverse();
|
|
160
|
-
const [xBack, yBack] = Transforms2D.applyTransform(Minv, x, y);
|
|
213
|
+
const [x, y] = Transforms2D.applyTransform(matrix, px, py);
|
|
161
214
|
```
|
|
162
215
|
|
|
163
|
-
|
|
216
|
+
#### 3D (4x4 matrices)
|
|
164
217
|
|
|
165
218
|
```js
|
|
166
219
|
import { Transforms3D } from '@emasoft/svg-matrix';
|
|
167
220
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
221
|
+
Transforms3D.translation(tx, ty, tz)
|
|
222
|
+
Transforms3D.scale(sx, sy, sz)
|
|
223
|
+
Transforms3D.rotateX(theta)
|
|
224
|
+
Transforms3D.rotateY(theta)
|
|
225
|
+
Transforms3D.rotateZ(theta)
|
|
226
|
+
Transforms3D.rotateAroundAxis(ux, uy, uz, theta)
|
|
227
|
+
Transforms3D.rotateAroundPoint(ux, uy, uz, theta, px, py, pz)
|
|
228
|
+
Transforms3D.reflectXY() // flip Z
|
|
229
|
+
Transforms3D.reflectXZ() // flip Y
|
|
230
|
+
Transforms3D.reflectYZ() // flip X
|
|
231
|
+
|
|
232
|
+
const [x, y, z] = Transforms3D.applyTransform(matrix, px, py, pz);
|
|
233
|
+
```
|
|
171
234
|
|
|
172
|
-
|
|
173
|
-
const Rx = Transforms3D.rotateX(Math.PI / 2);
|
|
174
|
-
const Ry = Transforms3D.rotateY(Math.PI / 4);
|
|
175
|
-
const Rz = Transforms3D.rotateZ(Math.PI / 6);
|
|
235
|
+
### SVG Processing
|
|
176
236
|
|
|
177
|
-
|
|
178
|
-
const Raxis = Transforms3D.rotateAroundAxis(1, 1, 0, Math.PI / 3);
|
|
237
|
+
#### SVGFlatten - Transform Parsing & CTM
|
|
179
238
|
|
|
180
|
-
|
|
181
|
-
|
|
239
|
+
```js
|
|
240
|
+
import { SVGFlatten } from '@emasoft/svg-matrix';
|
|
182
241
|
|
|
183
|
-
//
|
|
184
|
-
const
|
|
185
|
-
const RfXZ = Transforms3D.reflectXZ(); // flip Y
|
|
186
|
-
const RfYZ = Transforms3D.reflectYZ(); // flip X
|
|
187
|
-
const RfO = Transforms3D.reflectOrigin(); // flip all
|
|
242
|
+
// Parse transform attributes
|
|
243
|
+
const m = SVGFlatten.parseTransformAttribute('translate(50,50) rotate(45) scale(2)');
|
|
188
244
|
|
|
189
|
-
//
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
245
|
+
// Build CTM from transform stack
|
|
246
|
+
const ctm = SVGFlatten.buildCTM([
|
|
247
|
+
'scale(1.5)',
|
|
248
|
+
'translate(-13.6, -10.2)',
|
|
249
|
+
'rotate(15)',
|
|
250
|
+
'matrix(0.716, 0, 0, 1.397, 0, 0)'
|
|
251
|
+
]);
|
|
193
252
|
|
|
194
|
-
|
|
253
|
+
// Apply to point
|
|
254
|
+
const { x, y } = SVGFlatten.applyToPoint(ctm, 10, 10);
|
|
195
255
|
|
|
196
|
-
|
|
256
|
+
// Transform path data
|
|
257
|
+
const transformed = SVGFlatten.transformPathData('M 100 100 L 200 200', ctm);
|
|
197
258
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
| `sub(v)` | Element-wise subtraction |
|
|
203
|
-
| `scale(s)` | Scalar multiplication |
|
|
204
|
-
| `negate()` | Negate all components |
|
|
205
|
-
| `dot(v)` | Dot product |
|
|
206
|
-
| `cross(v)` | Cross product (3D only) |
|
|
207
|
-
| `outer(v)` | Outer product (returns 2D array) |
|
|
208
|
-
| `norm()` | Euclidean length |
|
|
209
|
-
| `normalize()` | Unit vector |
|
|
210
|
-
| `angleBetween(v)` | Angle in radians |
|
|
211
|
-
| `projectOnto(v)` | Vector projection |
|
|
212
|
-
| `orthogonal()` | Perpendicular vector |
|
|
213
|
-
| `distance(v)` | Euclidean distance |
|
|
214
|
-
| `equals(v, tol?)` | Equality check with optional tolerance |
|
|
215
|
-
| `toArray()` | Get Decimal array |
|
|
216
|
-
| `toNumberArray()` | Get number array |
|
|
217
|
-
| `toStringArray()` | Get string array |
|
|
218
|
-
|
|
219
|
-
### Matrix
|
|
220
|
-
|
|
221
|
-
| Method | Description |
|
|
222
|
-
|--------|-------------|
|
|
223
|
-
| `Matrix.from(arr)` | Create from 2D array |
|
|
224
|
-
| `Matrix.zeros(r, c)` | Zero matrix |
|
|
225
|
-
| `Matrix.identity(n)` | Identity matrix |
|
|
226
|
-
| `add(M)` | Element-wise addition |
|
|
227
|
-
| `sub(M)` | Element-wise subtraction |
|
|
228
|
-
| `mul(M)` | Matrix multiplication |
|
|
229
|
-
| `div(s)` | Scalar division |
|
|
230
|
-
| `negate()` | Negate all elements |
|
|
231
|
-
| `transpose()` | Transpose |
|
|
232
|
-
| `trace()` | Sum of diagonal |
|
|
233
|
-
| `determinant()` | Determinant |
|
|
234
|
-
| `inverse()` | Matrix inverse |
|
|
235
|
-
| `solve(b)` | Solve Ax = b |
|
|
236
|
-
| `lu()` | LU decomposition |
|
|
237
|
-
| `qr()` | QR decomposition |
|
|
238
|
-
| `exp()` | Matrix exponential |
|
|
239
|
-
| `applyToVector(v)` | Matrix-vector product |
|
|
240
|
-
| `equals(M, tol?)` | Equality check |
|
|
241
|
-
| `isSquare()` | Check if square |
|
|
242
|
-
| `toNumberArray()` | Get number 2D array |
|
|
243
|
-
| `toArrayOfStrings()` | Get string 2D array |
|
|
244
|
-
|
|
245
|
-
### Transforms2D
|
|
246
|
-
|
|
247
|
-
| Function | Description |
|
|
248
|
-
|----------|-------------|
|
|
249
|
-
| `translation(tx, ty)` | Translation matrix |
|
|
250
|
-
| `scale(sx, sy?)` | Scale matrix (uniform if sy omitted) |
|
|
251
|
-
| `rotate(theta)` | Rotation matrix (radians) |
|
|
252
|
-
| `rotateAroundPoint(theta, px, py)` | Rotation around point |
|
|
253
|
-
| `skew(ax, ay)` | Skew/shear matrix |
|
|
254
|
-
| `stretchAlongAxis(ux, uy, k)` | Stretch along direction |
|
|
255
|
-
| `reflectX()` | Reflect across X axis |
|
|
256
|
-
| `reflectY()` | Reflect across Y axis |
|
|
257
|
-
| `reflectOrigin()` | Reflect through origin |
|
|
258
|
-
| `applyTransform(M, x, y)` | Apply matrix to point |
|
|
259
|
-
|
|
260
|
-
### Transforms3D
|
|
261
|
-
|
|
262
|
-
| Function | Description |
|
|
263
|
-
|----------|-------------|
|
|
264
|
-
| `translation(tx, ty, tz)` | Translation matrix |
|
|
265
|
-
| `scale(sx, sy?, sz?)` | Scale matrix |
|
|
266
|
-
| `rotateX(theta)` | Rotation around X axis |
|
|
267
|
-
| `rotateY(theta)` | Rotation around Y axis |
|
|
268
|
-
| `rotateZ(theta)` | Rotation around Z axis |
|
|
269
|
-
| `rotateAroundAxis(ux, uy, uz, theta)` | Rotation around arbitrary axis |
|
|
270
|
-
| `rotateAroundPoint(ux, uy, uz, theta, px, py, pz)` | Rotation around point |
|
|
271
|
-
| `reflectXY()` | Reflect across XY plane |
|
|
272
|
-
| `reflectXZ()` | Reflect across XZ plane |
|
|
273
|
-
| `reflectYZ()` | Reflect across YZ plane |
|
|
274
|
-
| `reflectOrigin()` | Reflect through origin |
|
|
275
|
-
| `applyTransform(M, x, y, z)` | Apply matrix to point |
|
|
276
|
-
|
|
277
|
-
### SVGFlatten
|
|
278
|
-
|
|
279
|
-
| Function | Description |
|
|
280
|
-
|----------|-------------|
|
|
281
|
-
| **viewBox & Viewport** | |
|
|
282
|
-
| `parseViewBox(str)` | Parse viewBox attribute "minX minY width height" |
|
|
283
|
-
| `parsePreserveAspectRatio(str)` | Parse preserveAspectRatio (align, meet/slice) |
|
|
284
|
-
| `computeViewBoxTransform(vb, vpW, vpH, par)` | Compute viewBox to viewport matrix |
|
|
285
|
-
| `SVGViewport` class | Represents viewport with viewBox + preserveAspectRatio |
|
|
286
|
-
| `buildFullCTM(hierarchy)` | Build CTM from SVG/group/element hierarchy |
|
|
287
|
-
| **Units & Percentages** | |
|
|
288
|
-
| `resolveLength(value, ref, dpi?)` | Resolve px, %, em, pt, in, cm, mm, pc units |
|
|
289
|
-
| `resolvePercentages(x, y, vpW, vpH)` | Resolve x/y percentages to viewport |
|
|
290
|
-
| `normalizedDiagonal(w, h)` | Compute sqrt(w^2+h^2)/sqrt(2) for percentages |
|
|
291
|
-
| **Object Bounding Box** | |
|
|
292
|
-
| `objectBoundingBoxTransform(x, y, w, h)` | Transform for objectBoundingBox units |
|
|
293
|
-
| **Transform Parsing** | |
|
|
294
|
-
| `parseTransformFunction(func, args)` | Parse a single SVG transform function |
|
|
295
|
-
| `parseTransformAttribute(str)` | Parse a full SVG transform attribute string |
|
|
296
|
-
| `buildCTM(transformStack)` | Build CTM from array of transform strings |
|
|
297
|
-
| `applyToPoint(ctm, x, y)` | Apply CTM to a 2D point |
|
|
298
|
-
| `toSVGMatrix(ctm, precision?)` | Convert CTM back to SVG matrix() notation |
|
|
299
|
-
| `isIdentity(m, tolerance?)` | Check if matrix is effectively identity |
|
|
300
|
-
| `transformPathData(pathD, ctm)` | Transform path data coordinates |
|
|
301
|
-
| `PRECISION_INFO` | Object with precision comparison data |
|
|
302
|
-
|
|
303
|
-
## SVG Transform Flattening
|
|
304
|
-
|
|
305
|
-
The `SVGFlatten` module provides tools for parsing SVG transform attributes, building CTMs (Current Transform Matrices), and flattening nested transforms with arbitrary precision.
|
|
306
|
-
|
|
307
|
-
### Why Use SVGFlatten?
|
|
308
|
-
|
|
309
|
-
SVG elements can have deeply nested transforms through parent groups. When coordinates are transformed from local space to viewport and back using JavaScript's native 64-bit floats, precision is lost:
|
|
259
|
+
// viewBox handling
|
|
260
|
+
const viewBox = SVGFlatten.parseViewBox('0 0 100 100');
|
|
261
|
+
const par = SVGFlatten.parsePreserveAspectRatio('xMidYMid meet');
|
|
262
|
+
const vbTransform = SVGFlatten.computeViewBoxTransform(viewBox, 800, 600, par);
|
|
310
263
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
264
|
+
// Full CTM with viewBox + nested transforms
|
|
265
|
+
const fullCtm = SVGFlatten.buildFullCTM([
|
|
266
|
+
{ type: 'svg', width: 800, height: 600, viewBox: '0 0 400 300' },
|
|
267
|
+
{ type: 'g', transform: 'translate(50, 50)' },
|
|
268
|
+
{ type: 'g', transform: 'rotate(45)' },
|
|
269
|
+
{ type: 'element', transform: 'scale(2)' }
|
|
270
|
+
]);
|
|
315
271
|
|
|
316
|
-
|
|
272
|
+
// Unit resolution (px, %, em, pt, in, cm, mm, pc)
|
|
273
|
+
SVGFlatten.resolveLength('50%', 800); // → 400
|
|
274
|
+
SVGFlatten.resolveLength('1in', 800); // → 96
|
|
317
275
|
|
|
276
|
+
// objectBoundingBox transform
|
|
277
|
+
const bboxTransform = SVGFlatten.objectBoundingBoxTransform(100, 50, 200, 100);
|
|
318
278
|
```
|
|
319
|
-
Original coordinates: (10, 10)
|
|
320
|
-
After round-trip: (10.00000000000000000000000000000000000000,
|
|
321
|
-
9.999999999999999999999999999999999999999999999999999999999999999999999999999999998)
|
|
322
|
-
Round-trip error: X=0, Y=2e-79
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
**Improvement: 10^77 times better precision than JavaScript floats.**
|
|
326
279
|
|
|
327
|
-
|
|
280
|
+
#### GeometryToPath - Shape Conversion
|
|
328
281
|
|
|
329
282
|
```js
|
|
330
|
-
import {
|
|
283
|
+
import { GeometryToPath } from '@emasoft/svg-matrix';
|
|
284
|
+
|
|
285
|
+
// Shape to path
|
|
286
|
+
GeometryToPath.circleToPathData(cx, cy, r, precision)
|
|
287
|
+
GeometryToPath.ellipseToPathData(cx, cy, rx, ry, precision)
|
|
288
|
+
GeometryToPath.rectToPathData(x, y, w, h, rx, ry, useArcs, precision)
|
|
289
|
+
GeometryToPath.lineToPathData(x1, y1, x2, y2, precision)
|
|
290
|
+
GeometryToPath.polylineToPathData(points, precision)
|
|
291
|
+
GeometryToPath.polygonToPathData(points, precision)
|
|
292
|
+
GeometryToPath.convertElementToPath(element, precision)
|
|
293
|
+
|
|
294
|
+
// Path manipulation
|
|
295
|
+
GeometryToPath.parsePathData(pathData) // → [{command, args}]
|
|
296
|
+
GeometryToPath.pathArrayToString(commands) // → path string
|
|
297
|
+
GeometryToPath.pathToAbsolute(pathData) // relative → absolute
|
|
298
|
+
GeometryToPath.pathToCubics(pathData) // all → cubic Beziers
|
|
299
|
+
GeometryToPath.transformPathData(pathData, matrix, precision)
|
|
300
|
+
|
|
301
|
+
// Bezier kappa constant: 4*(sqrt(2)-1)/3
|
|
302
|
+
GeometryToPath.getKappa()
|
|
303
|
+
```
|
|
331
304
|
|
|
332
|
-
|
|
333
|
-
const m1 = SVGFlatten.parseTransformAttribute('translate(10, 20)');
|
|
334
|
-
const m2 = SVGFlatten.parseTransformAttribute('rotate(45)');
|
|
335
|
-
const m3 = SVGFlatten.parseTransformAttribute('scale(2, 0.5)');
|
|
336
|
-
const m4 = SVGFlatten.parseTransformAttribute('skewX(15)');
|
|
337
|
-
const m5 = SVGFlatten.parseTransformAttribute('matrix(0.866, 0.5, -0.5, 0.866, 0, 0)');
|
|
305
|
+
#### BrowserVerify - Chrome Verification
|
|
338
306
|
|
|
339
|
-
|
|
340
|
-
|
|
307
|
+
```js
|
|
308
|
+
import { BrowserVerify } from '@emasoft/svg-matrix';
|
|
309
|
+
|
|
310
|
+
// One-off verification
|
|
311
|
+
await BrowserVerify.verifyViewBox(800, 600, '0 0 400 300', 'xMidYMid meet');
|
|
312
|
+
await BrowserVerify.verifyTransform('rotate(45) translate(100, 50) scale(2)');
|
|
313
|
+
|
|
314
|
+
// Session-based verification
|
|
315
|
+
const verifier = new BrowserVerify.BrowserVerifier();
|
|
316
|
+
await verifier.init({ headless: true });
|
|
317
|
+
await verifier.verifyViewBoxTransform(800, 600, '0 0 100 100');
|
|
318
|
+
await verifier.verifyMatrix(ctm, { width: 100, height: 100, transform: '...' });
|
|
319
|
+
await verifier.verifyPointTransform(ctm, 10, 20, config);
|
|
320
|
+
await verifier.close();
|
|
321
|
+
|
|
322
|
+
// Standard test suite (28 tests including W3C issue #215 cases)
|
|
323
|
+
await BrowserVerify.runStandardTests({ verbose: true });
|
|
341
324
|
```
|
|
342
325
|
|
|
343
|
-
###
|
|
326
|
+
### Polygon Operations
|
|
344
327
|
|
|
345
|
-
|
|
346
|
-
import { Decimal, SVGFlatten } from '@emasoft/svg-matrix';
|
|
328
|
+
#### PolygonClip
|
|
347
329
|
|
|
348
|
-
|
|
330
|
+
```js
|
|
331
|
+
import { PolygonClip } from '@emasoft/svg-matrix';
|
|
349
332
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
// <g transform="scale(1.2, 0.8)"> <!-- g4 -->
|
|
356
|
-
// <path transform="matrix(...)"/> <!-- element -->
|
|
357
|
-
|
|
358
|
-
const transformStack = [
|
|
359
|
-
'scale(1.5)', // viewBox scaling
|
|
360
|
-
'translate(-13.613145,-10.209854)', // g1
|
|
361
|
-
'translate(-1144.8563,517.64642)', // g2
|
|
362
|
-
'rotate(15)', // g3
|
|
363
|
-
'scale(1.2, 0.8)', // g4
|
|
364
|
-
'matrix(0.71577068,0,0,1.3970955,0,0)' // element
|
|
333
|
+
const square = [
|
|
334
|
+
PolygonClip.point(0, 0),
|
|
335
|
+
PolygonClip.point(2, 0),
|
|
336
|
+
PolygonClip.point(2, 2),
|
|
337
|
+
PolygonClip.point(0, 2)
|
|
365
338
|
];
|
|
366
339
|
|
|
367
|
-
//
|
|
368
|
-
|
|
340
|
+
// Boolean operations
|
|
341
|
+
PolygonClip.polygonIntersection(poly1, poly2)
|
|
342
|
+
PolygonClip.polygonUnion(poly1, poly2)
|
|
343
|
+
PolygonClip.polygonDifference(poly1, poly2)
|
|
369
344
|
|
|
370
|
-
//
|
|
371
|
-
|
|
372
|
-
|
|
345
|
+
// Properties
|
|
346
|
+
PolygonClip.polygonArea(polygon)
|
|
347
|
+
PolygonClip.isCounterClockwise(polygon)
|
|
348
|
+
PolygonClip.isConvex(polygon)
|
|
349
|
+
PolygonClip.pointInPolygon(point, polygon) // 1=inside, 0=boundary, -1=outside
|
|
373
350
|
|
|
374
|
-
//
|
|
375
|
-
|
|
376
|
-
const recovered = SVGFlatten.applyToPoint(inverseCTM, viewport.x, viewport.y);
|
|
351
|
+
// Convex hull
|
|
352
|
+
PolygonClip.convexHull(points)
|
|
377
353
|
|
|
378
|
-
//
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
console.log('Round-trip error X:', errorX.toString()); // 0
|
|
382
|
-
console.log('Round-trip error Y:', errorY.toString()); // ~2e-79
|
|
354
|
+
// Bounding box
|
|
355
|
+
PolygonClip.boundingBox(polygon) // {minX, minY, maxX, maxY}
|
|
356
|
+
PolygonClip.bboxIntersects(bbox1, bbox2)
|
|
383
357
|
```
|
|
384
358
|
|
|
385
|
-
|
|
359
|
+
#### ClipPathResolver
|
|
386
360
|
|
|
387
361
|
```js
|
|
388
|
-
import {
|
|
362
|
+
import { ClipPathResolver } from '@emasoft/svg-matrix';
|
|
389
363
|
|
|
390
|
-
|
|
391
|
-
const
|
|
392
|
-
const
|
|
364
|
+
// Parse and resolve clipPath
|
|
365
|
+
const clipData = ClipPathResolver.parseClipPathElement(element);
|
|
366
|
+
const clipPolygon = ClipPathResolver.resolveClipPath(clipData, targetBBox);
|
|
393
367
|
|
|
394
|
-
|
|
395
|
-
|
|
368
|
+
// Shape to polygon
|
|
369
|
+
ClipPathResolver.shapeToPolygon({ type: 'circle', cx: 100, cy: 100, r: 50 }, { samples: 32 })
|
|
370
|
+
ClipPathResolver.pathToPolygon(pathData, { samples: 20 })
|
|
371
|
+
ClipPathResolver.polygonToPathData(polygon)
|
|
372
|
+
|
|
373
|
+
// Apply clipPath to element
|
|
374
|
+
ClipPathResolver.applyClipPath(elementData, clipPathData, targetBBox)
|
|
396
375
|
```
|
|
397
376
|
|
|
398
|
-
###
|
|
377
|
+
### SVG Element Resolution
|
|
399
378
|
|
|
400
|
-
|
|
379
|
+
#### UseSymbolResolver
|
|
401
380
|
|
|
402
381
|
```js
|
|
403
|
-
import {
|
|
382
|
+
import { UseSymbolResolver } from '@emasoft/svg-matrix';
|
|
404
383
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
384
|
+
const useData = UseSymbolResolver.parseUseElement(useElement);
|
|
385
|
+
const symbolData = UseSymbolResolver.parseSymbolElement(symbolElement);
|
|
386
|
+
const resolved = UseSymbolResolver.resolveUse(useData, svgDocument);
|
|
387
|
+
const flattened = UseSymbolResolver.flattenResolvedUse(resolved);
|
|
388
|
+
UseSymbolResolver.resolveAllUses(svgDocument)
|
|
410
389
|
```
|
|
411
390
|
|
|
412
|
-
|
|
391
|
+
#### MarkerResolver
|
|
413
392
|
|
|
414
|
-
|
|
393
|
+
```js
|
|
394
|
+
import { MarkerResolver } from '@emasoft/svg-matrix';
|
|
395
|
+
|
|
396
|
+
const markerData = MarkerResolver.parseMarkerElement(markerElement);
|
|
397
|
+
const vertices = MarkerResolver.getPathVertices(pathData);
|
|
398
|
+
const transform = MarkerResolver.getMarkerTransform(markerData, vertex, angle, strokeWidth);
|
|
399
|
+
const instances = MarkerResolver.resolveMarkers(pathD, { 'marker-start': m1, 'marker-end': m2, strokeWidth: 2 });
|
|
400
|
+
MarkerResolver.markersToPathData(instances)
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
#### PatternResolver
|
|
415
404
|
|
|
416
405
|
```js
|
|
417
|
-
import {
|
|
406
|
+
import { PatternResolver } from '@emasoft/svg-matrix';
|
|
418
407
|
|
|
419
|
-
|
|
408
|
+
const patternData = PatternResolver.parsePatternElement(patternElement);
|
|
409
|
+
const tiles = PatternResolver.resolvePattern(patternData, targetBBox);
|
|
410
|
+
PatternResolver.applyPattern(elementData, patternData, targetBBox)
|
|
411
|
+
PatternResolver.patternToClipPath(patternData, targetBBox)
|
|
412
|
+
PatternResolver.patternToPathData(patternData, targetBBox)
|
|
413
|
+
```
|
|
420
414
|
|
|
421
|
-
|
|
422
|
-
const viewBox = SVGFlatten.parseViewBox('0 0 100 100');
|
|
423
|
-
const par = SVGFlatten.parsePreserveAspectRatio('xMidYMid meet');
|
|
415
|
+
#### MaskResolver
|
|
424
416
|
|
|
425
|
-
|
|
426
|
-
|
|
417
|
+
```js
|
|
418
|
+
import { MaskResolver } from '@emasoft/svg-matrix';
|
|
427
419
|
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
|
|
420
|
+
const maskData = MaskResolver.parseMaskElement(maskElement);
|
|
421
|
+
const maskPolygon = MaskResolver.resolveMask(maskData, targetBBox);
|
|
422
|
+
MaskResolver.applyMask(elementPolygon, maskData, targetBBox)
|
|
423
|
+
MaskResolver.colorToLuminance({ r, g, b }) // sRGB luminance
|
|
431
424
|
```
|
|
432
425
|
|
|
433
|
-
###
|
|
426
|
+
### Advanced Features
|
|
434
427
|
|
|
435
|
-
|
|
428
|
+
#### MeshGradient (SVG 2.0)
|
|
436
429
|
|
|
437
430
|
```js
|
|
438
|
-
import {
|
|
431
|
+
import { MeshGradient } from '@emasoft/svg-matrix';
|
|
439
432
|
|
|
440
|
-
|
|
433
|
+
// Parse mesh gradient
|
|
434
|
+
const meshDef = MeshGradient.parseMeshGradientElement(element);
|
|
435
|
+
const meshData = MeshGradient.parseMeshGradient(meshDef);
|
|
441
436
|
|
|
442
|
-
//
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
{ type: 'g', transform: 'translate(50, 50)' },
|
|
446
|
-
{ type: 'g', transform: 'rotate(45)' },
|
|
447
|
-
{ type: 'element', transform: 'scale(2)' }
|
|
448
|
-
];
|
|
437
|
+
// Coons patch evaluation
|
|
438
|
+
const patch = new MeshGradient.CoonsPatch(topEdge, rightEdge, bottomEdge, leftEdge, cornerColors);
|
|
439
|
+
const { point, color } = patch.evaluate(u, v);
|
|
449
440
|
|
|
450
|
-
|
|
451
|
-
const
|
|
452
|
-
const viewport = SVGFlatten.applyToPoint(ctm, local.x, local.y);
|
|
441
|
+
// Rasterize to ImageData
|
|
442
|
+
const imageData = MeshGradient.rasterizeMeshGradient(meshData, width, height);
|
|
453
443
|
|
|
454
|
-
//
|
|
455
|
-
const
|
|
456
|
-
const recovered = SVGFlatten.applyToPoint(inverse, viewport.x, viewport.y);
|
|
457
|
-
// Error: X=2e-78, Y=2e-78
|
|
458
|
-
```
|
|
444
|
+
// Convert to polygons for vector export
|
|
445
|
+
const polygons = MeshGradient.meshGradientToPolygons(meshData, { subdivisions: 16 });
|
|
459
446
|
|
|
460
|
-
|
|
447
|
+
// Clip and export
|
|
448
|
+
const clipped = MeshGradient.clipMeshGradient(meshData, clipPolygon, { subdivisions: 32 });
|
|
449
|
+
const svgPaths = MeshGradient.clippedMeshToSVG(clipped);
|
|
450
|
+
```
|
|
461
451
|
|
|
462
|
-
|
|
452
|
+
#### TextToPath
|
|
463
453
|
|
|
464
454
|
```js
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
455
|
+
import { TextToPath } from '@emasoft/svg-matrix';
|
|
456
|
+
import opentype from 'opentype.js';
|
|
457
|
+
|
|
458
|
+
const font = await opentype.load('font.ttf');
|
|
459
|
+
|
|
460
|
+
// Convert text to path
|
|
461
|
+
const pathData = TextToPath.textToPath("Hello", {
|
|
462
|
+
x: 100, y: 100,
|
|
463
|
+
fontSize: 24,
|
|
464
|
+
font: font,
|
|
465
|
+
textAnchor: TextToPath.TextAnchor.MIDDLE,
|
|
466
|
+
dominantBaseline: TextToPath.DominantBaseline.MIDDLE
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Parse text element
|
|
470
|
+
const textData = TextToPath.parseTextElement(textElement);
|
|
471
|
+
const result = TextToPath.textElementToPath(textData, { font });
|
|
472
|
+
|
|
473
|
+
// Measure text
|
|
474
|
+
const metrics = TextToPath.measureText("Hello", { fontSize: "20px" }, font);
|
|
475
|
+
const bbox = TextToPath.getTextBBox(textData);
|
|
474
476
|
```
|
|
475
477
|
|
|
476
|
-
###
|
|
478
|
+
### Convenience Functions
|
|
477
479
|
|
|
478
|
-
|
|
480
|
+
Direct exports for common operations:
|
|
479
481
|
|
|
480
482
|
```js
|
|
481
|
-
import {
|
|
483
|
+
import {
|
|
484
|
+
// Transforms
|
|
485
|
+
translate2D, rotate2D, scale2D, transform2D,
|
|
486
|
+
translate3D, scale3D, transform3D,
|
|
482
487
|
|
|
483
|
-
|
|
488
|
+
// Shape conversion
|
|
489
|
+
circleToPath, ellipseToPath, rectToPath, lineToPath,
|
|
490
|
+
polygonToPath, polylineToPath,
|
|
484
491
|
|
|
485
|
-
//
|
|
486
|
-
|
|
487
|
-
|
|
492
|
+
// Path manipulation
|
|
493
|
+
parsePath, pathToString, pathToAbsolute, pathToCubics, transformPath,
|
|
494
|
+
elementToPath,
|
|
488
495
|
|
|
489
|
-
//
|
|
490
|
-
|
|
491
|
-
SVGFlatten.resolveLength('2.54cm', viewportWidth);// -> 96 (1 inch)
|
|
492
|
-
SVGFlatten.resolveLength('72pt', viewportWidth); // -> 96 (1 inch)
|
|
493
|
-
SVGFlatten.resolveLength('6pc', viewportWidth); // -> 96 (1 inch)
|
|
496
|
+
// Matrix/Vector creation
|
|
497
|
+
identity, zeros, vec, mat,
|
|
494
498
|
|
|
495
|
-
//
|
|
496
|
-
|
|
499
|
+
// Precision control
|
|
500
|
+
setPrecision, getPrecision,
|
|
497
501
|
|
|
498
|
-
//
|
|
499
|
-
|
|
502
|
+
// Constants
|
|
503
|
+
getKappa
|
|
504
|
+
} from '@emasoft/svg-matrix';
|
|
500
505
|
```
|
|
501
506
|
|
|
502
|
-
###
|
|
507
|
+
### Logging
|
|
503
508
|
|
|
504
|
-
|
|
509
|
+
Control library logging output:
|
|
505
510
|
|
|
506
511
|
```js
|
|
507
|
-
|
|
508
|
-
const bboxTransform = SVGFlatten.objectBoundingBoxTransform(100, 50, 200, 100);
|
|
512
|
+
import { Logger, LogLevel, setLogLevel, enableFileLogging } from '@emasoft/svg-matrix';
|
|
509
513
|
|
|
510
|
-
//
|
|
511
|
-
|
|
512
|
-
// (0.5, 0.5) in bbox -> (200, 100) in user space (center)
|
|
513
|
-
```
|
|
514
|
+
// Suppress all logging
|
|
515
|
+
setLogLevel(LogLevel.SILENT);
|
|
514
516
|
|
|
515
|
-
|
|
517
|
+
// Enable only errors
|
|
518
|
+
setLogLevel(LogLevel.ERROR);
|
|
516
519
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
import { Decimal, SVGFlatten } from 'https://esm.sh/@emasoft/svg-matrix';
|
|
520
|
+
// Enable warnings and errors (default)
|
|
521
|
+
setLogLevel(LogLevel.WARN);
|
|
520
522
|
|
|
521
|
-
|
|
523
|
+
// Enable all logging including debug
|
|
524
|
+
setLogLevel(LogLevel.DEBUG);
|
|
522
525
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
526
|
+
// Write logs to file
|
|
527
|
+
enableFileLogging('/path/to/log.txt');
|
|
528
|
+
|
|
529
|
+
// Direct Logger access
|
|
530
|
+
Logger.level = LogLevel.INFO;
|
|
531
|
+
Logger.warn('Custom warning');
|
|
532
|
+
Logger.debug('Debug info');
|
|
527
533
|
```
|
|
528
534
|
|
|
529
535
|
## License
|