@emasoft/svg-matrix 1.0.19 → 1.0.21

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 CHANGED
@@ -1,896 +1,393 @@
1
1
  # @emasoft/svg-matrix
2
2
 
3
- High-precision matrix, vector, and SVG transformation library for JavaScript. Built on decimal.js for 80-digit precision arithmetic.
3
+ <p align="center">
4
+ <img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='80' viewBox='0 0 600 80'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0%25' y1='0%25' x2='100%25' y2='0%25'%3E%3Cstop offset='0%25' stop-color='%23e0e0e0'/%3E%3Cstop offset='50%25' stop-color='%23404040'/%3E%3Cstop offset='100%25' stop-color='%23e0e0e0'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cg stroke='%23888' stroke-width='0.5' fill='none'%3E%3Ccircle cx='40' cy='40' r='25'/%3E%3Ccircle cx='40' cy='40' r='18'/%3E%3Cline x1='40' y1='15' x2='40' y2='65'/%3E%3Cline x1='15' y1='40' x2='65' y2='40'/%3E%3Cline x1='22' y1='22' x2='58' y2='58'/%3E%3Cline x1='58' y1='22' x2='22' y2='58'/%3E%3Crect x='100' y='20' width='40' height='40' transform='rotate(15 120 40)'/%3E%3Crect x='100' y='20' width='40' height='40' transform='rotate(30 120 40)'/%3E%3Crect x='100' y='20' width='40' height='40' transform='rotate(45 120 40)'/%3E%3Cpath d='M180 60 Q220 10 260 60' /%3E%3Ccircle cx='180' cy='60' r='3'/%3E%3Ccircle cx='220' cy='10' r='2'/%3E%3Ccircle cx='260' cy='60' r='3'/%3E%3Cline x1='180' y1='60' x2='220' y2='10' stroke-dasharray='4,2'/%3E%3Cline x1='220' y1='10' x2='260' y2='60' stroke-dasharray='4,2'/%3E%3Cpolygon points='320,15 360,40 320,65 340,40' /%3E%3Cline x1='320' y1='15' x2='360' y2='40'/%3E%3Cline x1='360' y1='40' x2='320' y2='65'/%3E%3Cpath d='M400 20 L440 20 L440 60 L400 60 Z M400 20 L440 60 M440 20 L400 60'/%3E%3Cg transform='translate(480 40)'%3E%3Ccircle r='25'/%3E%3Cpath d='M-25 0 A25 25 0 0 1 0 -25'/%3E%3Cpath d='M0 -25 A25 25 0 0 1 25 0'/%3E%3Cline x1='0' y1='-25' x2='0' y2='0'/%3E%3Cline x1='0' y1='0' x2='25' y2='0'/%3E%3Ctext x='-8' y='4' font-size='8' fill='%23666' font-family='monospace'%3E90%C2%B0%3C/text%3E%3C/g%3E%3Cline x1='540' y1='40' x2='600' y2='40'/%3E%3C/g%3E%3C/svg%3E" alt="Geometric precision illustration"/>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <strong>Arbitrary-precision mathematics for vectors, matrices, and SVG transformations</strong><br/>
9
+ <em>80 significant digits. Mathematically verified. Zero floating-point errors.</em>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="#part-1-core-math-library">Core Math</a> &#8226;
14
+ <a href="#part-2-svg-toolbox">SVG Toolbox</a> &#8226;
15
+ <a href="#installation">Install</a> &#8226;
16
+ <a href="API.md">API Reference</a>
17
+ </p>
4
18
 
5
- ## Features
6
-
7
- **Linear Algebra**
8
- - Matrix and Vector classes with full linear algebra operations
9
- - LU/QR decomposition, determinant, inverse, solve, matrix exponential
10
-
11
- **Affine Transforms**
12
- - 2D (3x3 homogeneous): translation, rotation, scale, skew, reflection
13
- - 3D (4x4 homogeneous): translation, rotation (X/Y/Z/arbitrary axis), scale
14
-
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
20
-
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
27
-
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
33
-
34
- ## Requirements
35
-
36
- - **Node.js 24.0.0** or higher (ES modules, modern JavaScript features)
37
- - **Playwright** (optional) - for browser verification features
38
-
39
- ## Precision
40
-
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 |
46
-
47
- **When precision matters:** GIS, CAD, scientific visualization, deep transform hierarchies, accumulated operations.
48
-
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
53
- ```
54
-
55
- ## Installation
56
-
57
- ### Node.js (Local Installation)
58
-
59
- **What does "local installation" mean?** It means downloading the library to your computer so you can use it in your Node.js projects. This is what most developers do.
60
-
61
- #### Step 1: Install the package
62
-
63
- Open your terminal and run:
64
-
65
- ```bash
66
- npm install @emasoft/svg-matrix
67
- ```
68
-
69
- This downloads the library into a folder called `node_modules` in your project.
70
-
71
- #### Upgrading to the Latest Version
72
-
73
- To update to the latest version:
19
+ ---
74
20
 
75
- ```bash
76
- npm update @emasoft/svg-matrix
77
- ```
21
+ ## What Is This?
78
22
 
79
- Or to install a specific version:
23
+ This package contains **two libraries** that work together:
80
24
 
81
- ```bash
82
- npm install @emasoft/svg-matrix@latest
83
- ```
25
+ | Library | Purpose | Precision |
26
+ |---------|---------|-----------|
27
+ | **Core Math** | Vectors, matrices, 2D/3D transforms | 80 digits (configurable to 10^9) |
28
+ | **SVG Toolbox** | Parse, transform, validate, optimize SVG files | 80 digits + visual verification |
84
29
 
85
- To check your current version:
30
+ **Think of it like this:**
86
31
 
87
- ```bash
88
- npm list @emasoft/svg-matrix
89
- ```
32
+ > **Core Math** is a calculator that never makes rounding errors.
33
+ > **SVG Toolbox** uses that calculator to work with SVG graphics perfectly.
90
34
 
91
- #### Step 2: Create a JavaScript file
35
+ ---
92
36
 
93
- Create a new file called `example.js` in your project folder.
37
+ <!-- Geometric divider: Golden ratio spiral construction -->
38
+ <p align="center">
39
+ <img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='20' viewBox='0 0 400 20'%3E%3Cg stroke='%23ccc' stroke-width='0.5' fill='none'%3E%3Cline x1='0' y1='10' x2='120' y2='10'/%3E%3Crect x='125' y='5' width='10' height='10'/%3E%3Crect x='137' y='5' width='6.18' height='6.18'/%3E%3Crect x='145' y='5' width='3.82' height='3.82'/%3E%3Crect x='150' y='5' width='2.36' height='2.36'/%3E%3Cpath d='M160 10 Q170 3 180 10 Q190 17 200 10 Q210 3 220 10 Q230 17 240 10'/%3E%3Crect x='250' y='7' width='2.36' height='2.36'/%3E%3Crect x='254' y='5' width='3.82' height='3.82'/%3E%3Crect x='259' y='5' width='6.18' height='6.18'/%3E%3Crect x='267' y='5' width='10' height='10'/%3E%3Cline x1='280' y1='10' x2='400' y2='10'/%3E%3C/g%3E%3C/svg%3E" alt=""/>
40
+ </p>
94
41
 
95
- #### Step 3: Import what you need
42
+ # Part 1: Core Math Library
96
43
 
97
- There are two ways to import the library:
44
+ **For:** Scientists, engineers, game developers, anyone who needs exact calculations.
98
45
 
99
- **Way 1: Pick only what you need (recommended)**
46
+ ## What Can It Do?
100
47
 
101
- This is like ordering specific items from a menu:
48
+ Imagine you want to rotate a spaceship in a game, or calculate where two laser beams cross. Normal JavaScript math has tiny errors that add up. This library has **zero errors** because it uses 80-digit precision.
102
49
 
103
50
  ```js
104
- // example.js
105
-
106
- // Import only the modules you need
107
- import { Matrix, Vector, Transforms2D } from '@emasoft/svg-matrix';
108
-
109
- // Now you can use them directly
110
- const v = Vector.from([1, 2, 3]);
111
- const rotation = Transforms2D.rotate(Math.PI / 4); // 45 degrees
112
-
113
- console.log('Vector:', v.toNumberArray());
114
- console.log('Rotation matrix created!');
51
+ // Normal JavaScript: 0.1 + 0.2 = 0.30000000000000004 (wrong!)
52
+ // svg-matrix: 0.1 + 0.2 = 0.3 (exactly right)
115
53
  ```
116
54
 
117
- **Way 2: Import everything at once**
55
+ ### Vectors (Arrows in Space)
118
56
 
119
- This is like getting the entire toolbox:
57
+ A vector is like an arrow pointing somewhere. You can add arrows, measure them, find angles between them.
120
58
 
121
59
  ```js
122
- // example.js
123
-
124
- // Import EVERYTHING as one big object called "SVGMatrix"
125
- import * as SVGMatrix from '@emasoft/svg-matrix';
126
-
127
- // Now use SVGMatrix.ModuleName to access each tool
128
- const v = SVGMatrix.Vector.from([1, 2, 3]);
129
- const m = SVGMatrix.Matrix.identity(3);
130
- const rotation = SVGMatrix.Transforms2D.rotate(Math.PI / 4);
131
- const pathData = SVGMatrix.GeometryToPath.circleToPathData(100, 100, 50);
132
-
133
- // See everything that's available:
134
- console.log('Available modules:', Object.keys(SVGMatrix));
135
- ```
60
+ import { Vector } from '@emasoft/svg-matrix';
136
61
 
137
- **What's inside the toolbox?** When you import everything, you get:
138
- - `SVGMatrix.Matrix` - For matrix math
139
- - `SVGMatrix.Vector` - For vector math
140
- - `SVGMatrix.Transforms2D` - For 2D rotations, translations, scaling
141
- - `SVGMatrix.Transforms3D` - For 3D transformations
142
- - `SVGMatrix.SVGFlatten` - For flattening SVG transforms
143
- - `SVGMatrix.GeometryToPath` - For converting shapes to paths
144
- - `SVGMatrix.PolygonClip` - For polygon boolean operations
145
- - ...and many more!
62
+ // Create an arrow pointing right 3 units and up 4 units
63
+ const arrow = Vector.from([3, 4]);
146
64
 
147
- #### Step 4: Run your code
65
+ // How long is the arrow? (It's 5 - like a 3-4-5 triangle!)
66
+ console.log(arrow.norm().toString()); // "5"
148
67
 
149
- ```bash
150
- node example.js
68
+ // Make it exactly 1 unit long (normalize)
69
+ const unit = arrow.normalize();
70
+ console.log(unit.toNumberArray()); // [0.6, 0.8]
151
71
  ```
152
72
 
153
- #### Complete Working Example
73
+ ### Matrices (Grids of Numbers)
154
74
 
155
- Save this as `my-first-example.js`:
75
+ A matrix is a grid of numbers. You can multiply them, flip them, use them to solve puzzles.
156
76
 
157
77
  ```js
158
- // my-first-example.js
159
- // A complete example showing the main features
160
-
161
- import {
162
- Matrix,
163
- Vector,
164
- Transforms2D,
165
- GeometryToPath,
166
- SVGFlatten
167
- } from '@emasoft/svg-matrix';
168
-
169
- console.log('=== Vector Example ===');
170
- const v = Vector.from([1, 2, 3]);
171
- const w = Vector.from([4, 5, 6]);
172
- console.log('Vector v:', v.toNumberArray());
173
- console.log('Vector w:', w.toNumberArray());
174
- console.log('Dot product (v · w):', v.dot(w).toString());
175
-
176
- console.log('\n=== Matrix Example ===');
177
- const A = Matrix.from([[1, 2], [3, 4]]);
178
- console.log('Matrix A:', A.toNumberArray());
179
- console.log('Determinant:', A.determinant().toString());
180
-
181
- console.log('\n=== Transform Example ===');
182
- // Create a transform: move 100 right, rotate 45°, scale 2x
183
- const transform = Transforms2D.translation(100, 0)
184
- .mul(Transforms2D.rotate(Math.PI / 4))
185
- .mul(Transforms2D.scale(2));
186
-
187
- // Apply to a point
188
- const [x, y] = Transforms2D.applyTransform(transform, 10, 0);
189
- console.log(`Point (10, 0) after transform: (${x.toFixed(2)}, ${y.toFixed(2)})`);
190
-
191
- console.log('\n=== SVG Path Example ===');
192
- // Convert a circle to a path
193
- const circlePath = GeometryToPath.circleToPathData(0, 0, 50);
194
- console.log('Circle as path:', circlePath.substring(0, 50) + '...');
195
-
196
- console.log('\nDone! Everything works!');
197
- ```
198
-
199
- Run it:
200
- ```bash
201
- node my-first-example.js
202
- ```
203
-
204
- #### Troubleshooting
78
+ import { Matrix } from '@emasoft/svg-matrix';
205
79
 
206
- **"Cannot use import statement outside a module"**
80
+ // Create a 2x2 grid
81
+ const grid = Matrix.from([
82
+ [4, 7],
83
+ [2, 6]
84
+ ]);
207
85
 
208
- Add `"type": "module"` to your `package.json`:
86
+ // Find the determinant (a special number about the grid)
87
+ console.log(grid.determinant().toString()); // "10"
209
88
 
210
- ```json
211
- {
212
- "name": "my-project",
213
- "type": "module"
214
- }
89
+ // Solve a puzzle: find x and y where 4x + 7y = 1 and 2x + 6y = 0
90
+ const answer = grid.solve([1, 0]);
91
+ console.log(answer.toNumberArray()); // [0.6, -0.2]
215
92
  ```
216
93
 
217
- Or rename your file from `.js` to `.mjs`.
218
-
219
- **Using older Node.js syntax (require)?**
94
+ ### Transforms (Moving & Spinning Things)
220
95
 
221
- This library uses modern ES modules. If you have old code using `require()`:
96
+ Transforms move, rotate, scale, or skew shapes. This is how video games move characters around!
222
97
 
223
98
  ```js
224
- // OLD WAY (doesn't work directly)
225
- // const { Matrix } = require('@emasoft/svg-matrix'); // ❌ Error!
226
-
227
- // NEW WAY (use dynamic import)
228
- async function main() {
229
- const { Matrix, Vector } = await import('@emasoft/svg-matrix');
230
-
231
- const v = Vector.from([1, 2, 3]);
232
- console.log(v.norm().toString());
233
- }
234
- main();
235
- ```
236
-
237
- ### Browser Usage (CDN)
238
-
239
- You can use this library directly in a web browser without installing anything. Just add a `<script>` tag to your HTML file.
240
-
241
- **What is a CDN?** A CDN (Content Delivery Network) hosts the library files so you can load them directly in your browser. No `npm install` needed!
242
-
243
- #### Option 1: esm.sh (Recommended)
244
-
245
- Best for modern browsers. Automatically handles dependencies.
246
-
247
- ```html
248
- <!DOCTYPE html>
249
- <html>
250
- <head>
251
- <title>SVG Matrix Example</title>
252
- </head>
253
- <body>
254
- <script type="module">
255
- // Import the modules you need
256
- import { Matrix, Vector, Transforms2D } from 'https://esm.sh/@emasoft/svg-matrix';
257
-
258
- // Now you can use them!
259
- const rotation = Transforms2D.rotate(Math.PI / 4); // 45 degrees
260
- const [x, y] = Transforms2D.applyTransform(rotation, 10, 0);
261
-
262
- console.log(`Point (10, 0) rotated 45 degrees = (${x}, ${y})`);
263
- </script>
264
- </body>
265
- </html>
266
- ```
267
-
268
- #### Option 2: unpkg
269
-
270
- Another reliable CDN option.
271
-
272
- ```html
273
- <script type="module">
274
- import { SVGFlatten, GeometryToPath } from 'https://unpkg.com/@emasoft/svg-matrix/src/index.js';
275
-
276
- // Convert a circle to a path
277
- const pathData = GeometryToPath.circleToPathData(100, 100, 50);
278
- console.log(pathData);
279
- </script>
280
- ```
99
+ import { Transforms2D } from '@emasoft/svg-matrix';
281
100
 
282
- #### Option 3: jsDelivr
101
+ // Move something 100 pixels right
102
+ const move = Transforms2D.translation(100, 0);
283
103
 
284
- Popular CDN with good caching.
104
+ // Spin something 45 degrees
105
+ const spin = Transforms2D.rotate(Math.PI / 4);
285
106
 
286
- ```html
287
- <script type="module">
288
- import { Matrix, Vector } from 'https://cdn.jsdelivr.net/npm/@emasoft/svg-matrix/src/index.js';
107
+ // Make something twice as big
108
+ const grow = Transforms2D.scale(2);
289
109
 
290
- const v = Vector.from([1, 2, 3]);
291
- const w = Vector.from([4, 5, 6]);
292
- console.log('Dot product:', v.dot(w).toString());
293
- </script>
110
+ // Apply spin to a point at (10, 0)
111
+ const [x, y] = Transforms2D.applyTransform(spin, 10, 0);
112
+ console.log(x.toFixed(4), y.toFixed(4)); // "7.0711 7.0711"
294
113
  ```
295
114
 
296
- #### Import Everything at Once
115
+ ---
297
116
 
298
- **What if I don't know what I need yet?** You can import the entire library as one big object. This is like getting the whole toolbox instead of picking individual tools.
117
+ <!-- Geometric divider: Intersecting circles construction -->
118
+ <p align="center">
119
+ <img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='20' viewBox='0 0 400 20'%3E%3Cg stroke='%23ccc' stroke-width='0.5' fill='none'%3E%3Cline x1='0' y1='10' x2='140' y2='10'/%3E%3Ccircle cx='170' cy='10' r='8'/%3E%3Ccircle cx='182' cy='10' r='8'/%3E%3Ccircle cx='200' cy='10' r='3'/%3E%3Ccircle cx='218' cy='10' r='8'/%3E%3Ccircle cx='230' cy='10' r='8'/%3E%3Cline x1='260' y1='10' x2='400' y2='10'/%3E%3C/g%3E%3C/svg%3E" alt=""/>
120
+ </p>
299
121
 
300
- ```html
301
- <script type="module">
302
- // Import EVERYTHING as one object called "SVGMatrix"
303
- // The "* as" means "everything as"
304
- import * as SVGMatrix from 'https://esm.sh/@emasoft/svg-matrix';
305
-
306
- // Now use SVGMatrix.ModuleName to access each tool
307
- const v = SVGMatrix.Vector.from([1, 2, 3]);
308
- const m = SVGMatrix.Matrix.identity(3);
309
- const rotation = SVGMatrix.Transforms2D.rotate(Math.PI / 4);
310
- const [x, y] = SVGMatrix.Transforms2D.applyTransform(rotation, 10, 0);
311
-
312
- // See everything that's available:
313
- console.log('All modules:', Object.keys(SVGMatrix));
314
- // Output: ['Matrix', 'Vector', 'Transforms2D', 'Transforms3D', 'SVGFlatten', ...]
315
- </script>
316
- ```
122
+ ## Core Math API Quick Reference
317
123
 
318
- **What's inside?** Here's everything you get:
124
+ ### Vector
319
125
 
320
- | Module | What it does |
126
+ | Method | What It Does |
127
+ |--------|--------------|
128
+ | `Vector.from([x, y, z])` | Create a vector |
129
+ | `.add(v)` | Add two vectors |
130
+ | `.sub(v)` | Subtract vectors |
131
+ | `.scale(n)` | Multiply by a number |
132
+ | `.dot(v)` | Dot product (single number result) |
133
+ | `.cross(v)` | Cross product (3D only) |
134
+ | `.norm()` | Length of the vector |
135
+ | `.normalize()` | Make length = 1 |
136
+ | `.angleBetween(v)` | Angle between two vectors |
137
+ | `.toNumberArray()` | Convert to regular JavaScript array |
138
+
139
+ ### Matrix
140
+
141
+ | Method | What It Does |
142
+ |--------|--------------|
143
+ | `Matrix.from([[...], [...]])` | Create from 2D array |
144
+ | `Matrix.identity(n)` | Identity matrix (1s on diagonal) |
145
+ | `Matrix.zeros(r, c)` | Matrix of zeros |
146
+ | `.mul(M)` | Multiply matrices |
147
+ | `.transpose()` | Flip rows and columns |
148
+ | `.determinant()` | Calculate determinant |
149
+ | `.inverse()` | Calculate inverse |
150
+ | `.solve(b)` | Solve system of equations |
151
+ | `.lu()` | LU decomposition |
152
+ | `.qr()` | QR decomposition |
153
+
154
+ ### Transforms2D / Transforms3D
155
+
156
+ | Method | What It Does |
321
157
  |--------|--------------|
322
- | `SVGMatrix.Matrix` | Matrix math (multiply, inverse, determinant) |
323
- | `SVGMatrix.Vector` | Vector math (add, dot product, cross product) |
324
- | `SVGMatrix.Transforms2D` | 2D transforms (rotate, scale, translate) |
325
- | `SVGMatrix.Transforms3D` | 3D transforms (rotate around axes) |
326
- | `SVGMatrix.SVGFlatten` | Flatten SVG transforms into paths |
327
- | `SVGMatrix.GeometryToPath` | Convert shapes (circle, rect) to paths |
328
- | `SVGMatrix.PolygonClip` | Boolean operations on polygons |
329
- | `SVGMatrix.ClipPathResolver` | Resolve SVG clipPath elements |
330
- | `SVGMatrix.BrowserVerify` | Verify against browser's SVG engine |
331
- | `SVGMatrix.Logger` | Control library logging |
158
+ | `translation(x, y)` | Move transform |
159
+ | `scale(sx, sy)` | Size transform |
160
+ | `rotate(angle)` | Spin transform (radians) |
161
+ | `rotateAroundPoint(angle, px, py)` | Spin around a specific point |
162
+ | `skew(ax, ay)` | Slant transform |
163
+ | `reflectX()` / `reflectY()` | Mirror transform |
164
+ | `applyTransform(M, x, y)` | Apply transform to a point |
332
165
 
333
- #### Pin to a Specific Version
166
+ ---
334
167
 
335
- To avoid breaking changes, pin to a specific version:
168
+ <!-- Geometric divider: Bezier curve construction -->
169
+ <p align="center">
170
+ <img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='30' viewBox='0 0 400 30'%3E%3Cg stroke='%23ccc' stroke-width='0.5' fill='none'%3E%3Cline x1='0' y1='15' x2='100' y2='15'/%3E%3Cpath d='M110 25 C130 5, 150 5, 170 15 S210 25, 230 15 S270 5, 290 15' stroke='%23999'/%3E%3Ccircle cx='110' cy='25' r='2' fill='%23999'/%3E%3Ccircle cx='170' cy='15' r='2' fill='%23999'/%3E%3Ccircle cx='230' cy='15' r='2' fill='%23999'/%3E%3Ccircle cx='290' cy='15' r='2' fill='%23999'/%3E%3Cline x1='110' y1='25' x2='130' y2='5' stroke-dasharray='2,2'/%3E%3Cline x1='150' y1='5' x2='170' y2='15' stroke-dasharray='2,2'/%3E%3Cline x1='300' y1='15' x2='400' y2='15'/%3E%3C/g%3E%3C/svg%3E" alt=""/>
171
+ </p>
336
172
 
337
- ```html
338
- <!-- esm.sh with version -->
339
- <script type="module">
340
- import { Transforms2D } from 'https://esm.sh/@emasoft/svg-matrix@1.0.7';
341
- </script>
173
+ # Part 2: SVG Toolbox
342
174
 
343
- <!-- unpkg with version -->
344
- <script type="module">
345
- import { Matrix } from 'https://unpkg.com/@emasoft/svg-matrix@1.0.7/src/index.js';
346
- </script>
175
+ **For:** Web developers, designers, anyone working with SVG graphics.
347
176
 
348
- <!-- jsDelivr with version -->
349
- <script type="module">
350
- import { Vector } from 'https://cdn.jsdelivr.net/npm/@emasoft/svg-matrix@1.0.7/src/index.js';
351
- </script>
352
- ```
177
+ ## What Can It Do?
353
178
 
354
- #### Complete Working Example
179
+ SVG files are pictures made of shapes, paths, and effects. This toolbox can:
355
180
 
356
- Save this as `example.html` and open it in your browser:
181
+ - **Flatten** - Bake all transforms into coordinates (no more `transform="rotate(45)"`)
182
+ - **Convert** - Turn circles, rectangles into path commands
183
+ - **Validate** - Find and fix problems in SVG files
184
+ - **Optimize** - Remove unused elements, simplify paths
357
185
 
358
- ```html
359
- <!DOCTYPE html>
360
- <html>
361
- <head>
362
- <title>SVG Matrix - Complete Example</title>
363
- <style>
364
- body { font-family: sans-serif; padding: 20px; }
365
- svg { border: 1px solid #ccc; }
366
- pre { background: #f5f5f5; padding: 10px; }
367
- </style>
368
- </head>
369
- <body>
370
- <h1>SVG Matrix Demo</h1>
371
-
372
- <h2>Original Circle</h2>
373
- <svg width="200" height="200">
374
- <circle id="original" cx="100" cy="100" r="40" fill="blue" opacity="0.5"/>
375
- </svg>
376
-
377
- <h2>Transformed (rotate 45 + scale 1.5)</h2>
378
- <svg width="200" height="200">
379
- <path id="transformed" fill="red" opacity="0.5"/>
380
- </svg>
381
-
382
- <h2>Generated Path Data</h2>
383
- <pre id="output"></pre>
384
-
385
- <script type="module">
386
- import {
387
- Transforms2D,
388
- GeometryToPath,
389
- SVGFlatten
390
- } from 'https://esm.sh/@emasoft/svg-matrix';
391
-
392
- // Step 1: Convert circle to path
393
- const circlePath = GeometryToPath.circleToPathData(100, 100, 40);
394
-
395
- // Step 2: Create a combined transform (rotate 45 degrees, then scale 1.5x)
396
- const transform = Transforms2D.scale(1.5)
397
- .mul(Transforms2D.rotate(Math.PI / 4));
398
-
399
- // Step 3: Apply transform to the path
400
- const transformedPath = SVGFlatten.transformPathData(circlePath, transform);
401
-
402
- // Step 4: Display the result
403
- document.getElementById('transformed').setAttribute('d', transformedPath);
404
- document.getElementById('output').textContent = transformedPath;
405
- </script>
406
- </body>
407
- </html>
408
- ```
186
+ ### Why Use This Instead of SVGO?
409
187
 
410
- #### Troubleshooting
188
+ | | SVGO | svg-matrix |
189
+ |--|------|-----------|
190
+ | **Math precision** | 15 digits (can accumulate errors) | 80 digits (no errors) |
191
+ | **Verification** | None (hope it works) | Mathematical proof each step is correct |
192
+ | **Attribute handling** | May lose clip-path, mask, filter | Guarantees ALL attributes preserved |
193
+ | **Use case** | Quick file size reduction | Precision-critical applications |
411
194
 
412
- **"Cannot use import statement outside a module"**
413
- Make sure you have `type="module"` in your script tag:
414
- ```html
415
- <script type="module"> <!-- This is required! -->
416
- ```
195
+ **Use svg-matrix when:** CAD, GIS, scientific visualization, or when visual correctness matters more than file size.
417
196
 
418
- **CORS errors when opening HTML file directly**
419
- Some browsers block CDN imports when opening files with `file://`. Solutions:
420
- 1. Use a local server: `npx serve .` or `python -m http.server`
421
- 2. Or use a code playground like CodePen, JSFiddle, or StackBlitz
197
+ **Use SVGO when:** Quick optimization where small rounding errors are acceptable.
422
198
 
423
- **Want to use with older browsers?**
424
- This library requires ES modules (modern browsers). For IE11 or very old browsers, you'll need a bundler like Webpack or Rollup.
199
+ ---
425
200
 
426
- ## CLI
201
+ ## Command Line Tools
427
202
 
428
- The library includes a command-line interface for batch processing SVG files.
203
+ ### `svg-matrix` - Process SVG files
429
204
 
430
205
  ```bash
431
- # Show help
432
- svg-matrix --help
433
-
434
- # Show command-specific help
435
- svg-matrix flatten --help
436
-
437
- # Show version
438
- svg-matrix --version
439
-
440
- # Process single file
206
+ # Flatten all transforms into coordinates
441
207
  svg-matrix flatten input.svg -o output.svg
442
208
 
443
- # Batch process folder
444
- svg-matrix flatten ./svgs/ -o ./output/
445
-
446
- # Process files from list
447
- svg-matrix flatten --list files.txt -o ./output/
448
-
449
- # Convert shapes to paths
209
+ # Convert shapes (circle, rect, etc.) to paths
450
210
  svg-matrix convert input.svg -o output.svg
451
211
 
452
- # Normalize paths to cubic Beziers
212
+ # Normalize all paths to cubic Beziers
453
213
  svg-matrix normalize input.svg -o output.svg
454
214
 
455
- # Show SVG file info
215
+ # Show file information
456
216
  svg-matrix info input.svg
457
217
  ```
458
218
 
459
- ### CLI Options
219
+ **Options:**
460
220
 
461
- | Option | Description |
462
- |--------|-------------|
463
- | `-o, --output <path>` | Output file or directory |
464
- | `-l, --list <file>` | Read input files from text file |
465
- | `-r, --recursive` | Process directories recursively |
466
- | `-p, --precision <n>` | Decimal precision (default: 6) |
467
- | `-f, --force` | Overwrite existing files |
468
- | `-n, --dry-run` | Show what would be done |
469
- | `-q, --quiet` | Suppress all output except errors |
470
- | `-v, --verbose` | Enable verbose/debug output |
471
- | `--log-file <path>` | Write log to file |
221
+ | Option | What It Does |
222
+ |--------|--------------|
223
+ | `-o file.svg` | Output file |
224
+ | `-r` | Process folders recursively |
225
+ | `-f` | Overwrite existing files |
226
+ | `-p N` | Decimal precision (default: 6, max: 50) |
227
+ | `-q` | Quiet mode |
228
+ | `-v` | Verbose mode |
229
+ | `--transform-only` | Only flatten transforms (faster) |
230
+ | `--no-clip-paths` | Skip clip-path processing |
231
+ | `--no-masks` | Skip mask processing |
472
232
 
473
- ### File List Format
233
+ Run `svg-matrix --help` for all options.
474
234
 
475
- Create a text file with one path per line:
235
+ ### `svglinter` - Find problems in SVG files
476
236
 
237
+ ```bash
238
+ svglinter myfile.svg # Check one file
239
+ svglinter icons/ # Check all SVGs in folder
240
+ svglinter --fix icons/ # Auto-fix problems
241
+ svglinter --errors-only icons/ # Only show errors
477
242
  ```
478
- # This is a comment
479
- ./folder1/file1.svg
480
- ./folder2/file2.svg
481
- ./entire-folder/
482
- ```
483
-
484
- ## Quick Start
485
243
 
486
- ```js
487
- import { Decimal, Matrix, Vector, Transforms2D, SVGFlatten } from '@emasoft/svg-matrix';
488
-
489
- Decimal.set({ precision: 80 });
490
-
491
- // Compose transforms (right-to-left: scale first, then rotate, then translate)
492
- const M = Transforms2D.translation(10, 20)
493
- .mul(Transforms2D.rotate(Math.PI / 4))
494
- .mul(Transforms2D.scale(2));
244
+ Finds: broken references, invalid colors, typos in element names, missing attributes.
495
245
 
496
- // Apply to point
497
- const [x, y] = Transforms2D.applyTransform(M, 1, 0);
498
-
499
- // Round-trip with inverse
500
- const [xBack, yBack] = Transforms2D.applyTransform(M.inverse(), x, y);
501
- ```
246
+ See [full svglinter documentation](docs/SVGLINTER.md).
502
247
 
503
248
  ---
504
249
 
505
- ## API Reference
506
-
507
- ### Linear Algebra
250
+ <!-- Geometric divider: Triangle construction -->
251
+ <p align="center">
252
+ <img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='20' viewBox='0 0 400 20'%3E%3Cg stroke='%23ccc' stroke-width='0.5' fill='none'%3E%3Cline x1='0' y1='10' x2='150' y2='10'/%3E%3Cpolygon points='175,5 200,15 175,15'/%3E%3Cline x1='175' y1='5' x2='187.5' y2='10' stroke-dasharray='2,2'/%3E%3Cpolygon points='210,15 235,5 235,15'/%3E%3Cline x1='235' y1='5' x2='222.5' y2='10' stroke-dasharray='2,2'/%3E%3Cline x1='250' y1='10' x2='400' y2='10'/%3E%3C/g%3E%3C/svg%3E" alt=""/>
253
+ </p>
508
254
 
509
- #### Vector
255
+ ## SVG Toolbox API Quick Reference
510
256
 
511
- ```js
512
- import { Vector } from '@emasoft/svg-matrix';
513
-
514
- const v = Vector.from([1, 2, 3]);
515
- const w = Vector.from([4, 5, 6]);
516
-
517
- v.add(w) // Element-wise addition
518
- v.sub(w) // Element-wise subtraction
519
- v.scale(2) // Scalar multiplication
520
- v.dot(w) // Dot product → Decimal
521
- v.cross(w) // Cross product (3D)
522
- v.norm() // Euclidean length
523
- v.normalize() // Unit vector
524
- v.angleBetween(w) // Angle in radians
525
- v.projectOnto(w) // Vector projection
526
- v.orthogonal() // Perpendicular vector
527
- v.distance(w) // Euclidean distance
528
- v.toNumberArray() // [1, 2, 3]
529
- ```
257
+ ### GeometryToPath
530
258
 
531
- #### Matrix
259
+ Convert shapes to path data:
532
260
 
533
261
  ```js
534
- import { Matrix } from '@emasoft/svg-matrix';
535
-
536
- const A = Matrix.from([[1, 2], [3, 4]]);
537
- const I = Matrix.identity(3);
538
- const Z = Matrix.zeros(2, 3);
539
-
540
- A.add(B) // Element-wise addition
541
- A.sub(B) // Element-wise subtraction
542
- A.mul(B) // Matrix multiplication
543
- A.transpose() // Transpose
544
- A.trace() // Sum of diagonal
545
- A.determinant() // Determinant
546
- A.inverse() // Matrix inverse
547
- A.solve([1, 1]) // Solve Ax = b
548
- A.lu() // { L, U, P } decomposition
549
- A.qr() // { Q, R } decomposition
550
- A.exp() // Matrix exponential
551
- A.applyToVector(v) // Matrix-vector product
552
- ```
553
-
554
- ### Transforms
555
-
556
- #### 2D (3x3 matrices)
557
-
558
- ```js
559
- import { Transforms2D } from '@emasoft/svg-matrix';
560
-
561
- Transforms2D.translation(tx, ty)
562
- Transforms2D.scale(sx, sy) // sy defaults to sx
563
- Transforms2D.rotate(theta) // radians
564
- Transforms2D.rotateAroundPoint(theta, px, py)
565
- Transforms2D.skew(ax, ay)
566
- Transforms2D.stretchAlongAxis(ux, uy, k)
567
- Transforms2D.reflectX() // flip across X axis
568
- Transforms2D.reflectY() // flip across Y axis
569
- Transforms2D.reflectOrigin()
570
-
571
- // Apply to point
572
- const [x, y] = Transforms2D.applyTransform(matrix, px, py);
573
- ```
574
-
575
- #### 3D (4x4 matrices)
262
+ import { GeometryToPath } from '@emasoft/svg-matrix';
576
263
 
577
- ```js
578
- import { Transforms3D } from '@emasoft/svg-matrix';
579
-
580
- Transforms3D.translation(tx, ty, tz)
581
- Transforms3D.scale(sx, sy, sz)
582
- Transforms3D.rotateX(theta)
583
- Transforms3D.rotateY(theta)
584
- Transforms3D.rotateZ(theta)
585
- Transforms3D.rotateAroundAxis(ux, uy, uz, theta)
586
- Transforms3D.rotateAroundPoint(ux, uy, uz, theta, px, py, pz)
587
- Transforms3D.reflectXY() // flip Z
588
- Transforms3D.reflectXZ() // flip Y
589
- Transforms3D.reflectYZ() // flip X
590
-
591
- const [x, y, z] = Transforms3D.applyTransform(matrix, px, py, pz);
264
+ const circle = GeometryToPath.circleToPathData(50, 50, 25);
265
+ const rect = GeometryToPath.rectToPathData(0, 0, 100, 50, 5, 5);
266
+ const ellipse = GeometryToPath.ellipseToPathData(50, 50, 30, 20);
592
267
  ```
593
268
 
594
- ### SVG Processing
269
+ ### SVGFlatten
595
270
 
596
- #### SVGFlatten - Transform Parsing & CTM
271
+ Parse and transform SVG data:
597
272
 
598
273
  ```js
599
274
  import { SVGFlatten } from '@emasoft/svg-matrix';
600
275
 
601
- // Parse transform attributes
602
- const m = SVGFlatten.parseTransformAttribute('translate(50,50) rotate(45) scale(2)');
603
-
604
- // Build CTM from transform stack
605
- const ctm = SVGFlatten.buildCTM([
606
- 'scale(1.5)',
607
- 'translate(-13.6, -10.2)',
608
- 'rotate(15)',
609
- 'matrix(0.716, 0, 0, 1.397, 0, 0)'
610
- ]);
611
-
612
- // Apply to point
613
- const { x, y } = SVGFlatten.applyToPoint(ctm, 10, 10);
276
+ // Parse transform string
277
+ const matrix = SVGFlatten.parseTransformAttribute('rotate(45) scale(2)');
614
278
 
615
279
  // Transform path data
616
- const transformed = SVGFlatten.transformPathData('M 100 100 L 200 200', ctm);
617
-
618
- // viewBox handling
619
- const viewBox = SVGFlatten.parseViewBox('0 0 100 100');
620
- const par = SVGFlatten.parsePreserveAspectRatio('xMidYMid meet');
621
- const vbTransform = SVGFlatten.computeViewBoxTransform(viewBox, 800, 600, par);
622
-
623
- // Full CTM with viewBox + nested transforms
624
- const fullCtm = SVGFlatten.buildFullCTM([
625
- { type: 'svg', width: 800, height: 600, viewBox: '0 0 400 300' },
626
- { type: 'g', transform: 'translate(50, 50)' },
627
- { type: 'g', transform: 'rotate(45)' },
628
- { type: 'element', transform: 'scale(2)' }
629
- ]);
630
-
631
- // Unit resolution (px, %, em, pt, in, cm, mm, pc)
632
- SVGFlatten.resolveLength('50%', 800); // → 400
633
- SVGFlatten.resolveLength('1in', 800); // → 96
634
-
635
- // objectBoundingBox transform
636
- const bboxTransform = SVGFlatten.objectBoundingBoxTransform(100, 50, 200, 100);
637
- ```
638
-
639
- #### GeometryToPath - Shape Conversion
640
-
641
- ```js
642
- import { GeometryToPath } from '@emasoft/svg-matrix';
643
-
644
- // Shape to path
645
- GeometryToPath.circleToPathData(cx, cy, r, precision)
646
- GeometryToPath.ellipseToPathData(cx, cy, rx, ry, precision)
647
- GeometryToPath.rectToPathData(x, y, w, h, rx, ry, useArcs, precision)
648
- GeometryToPath.lineToPathData(x1, y1, x2, y2, precision)
649
- GeometryToPath.polylineToPathData(points, precision)
650
- GeometryToPath.polygonToPathData(points, precision)
651
- GeometryToPath.convertElementToPath(element, precision)
652
-
653
- // Path manipulation
654
- GeometryToPath.parsePathData(pathData) // → [{command, args}]
655
- GeometryToPath.pathArrayToString(commands) // → path string
656
- GeometryToPath.pathToAbsolute(pathData) // relative → absolute
657
- GeometryToPath.pathToCubics(pathData) // all → cubic Beziers
658
- GeometryToPath.transformPathData(pathData, matrix, precision)
659
-
660
- // Bezier kappa constant: 4*(sqrt(2)-1)/3
661
- GeometryToPath.getKappa()
662
- ```
663
-
664
- #### BrowserVerify - Chrome Verification
280
+ const newPath = SVGFlatten.transformPathData('M 0 0 L 100 100', matrix);
665
281
 
666
- ```js
667
- import { BrowserVerify } from '@emasoft/svg-matrix';
668
-
669
- // One-off verification
670
- await BrowserVerify.verifyViewBox(800, 600, '0 0 400 300', 'xMidYMid meet');
671
- await BrowserVerify.verifyTransform('rotate(45) translate(100, 50) scale(2)');
672
-
673
- // Session-based verification
674
- const verifier = new BrowserVerify.BrowserVerifier();
675
- await verifier.init({ headless: true });
676
- await verifier.verifyViewBoxTransform(800, 600, '0 0 100 100');
677
- await verifier.verifyMatrix(ctm, { width: 100, height: 100, transform: '...' });
678
- await verifier.verifyPointTransform(ctm, 10, 20, config);
679
- await verifier.close();
680
-
681
- // Standard test suite (28 tests including W3C issue #215 cases)
682
- await BrowserVerify.runStandardTests({ verbose: true });
282
+ // Resolve CSS units
283
+ SVGFlatten.resolveLength('50%', 800); // 400
284
+ SVGFlatten.resolveLength('1in', 96); // 96
683
285
  ```
684
286
 
685
- ### Polygon Operations
686
-
687
- #### PolygonClip
688
-
689
- ```js
690
- import { PolygonClip } from '@emasoft/svg-matrix';
691
-
692
- const square = [
693
- PolygonClip.point(0, 0),
694
- PolygonClip.point(2, 0),
695
- PolygonClip.point(2, 2),
696
- PolygonClip.point(0, 2)
697
- ];
698
-
699
- // Boolean operations
700
- PolygonClip.polygonIntersection(poly1, poly2)
701
- PolygonClip.polygonUnion(poly1, poly2)
702
- PolygonClip.polygonDifference(poly1, poly2)
703
-
704
- // Properties
705
- PolygonClip.polygonArea(polygon)
706
- PolygonClip.isCounterClockwise(polygon)
707
- PolygonClip.isConvex(polygon)
708
- PolygonClip.pointInPolygon(point, polygon) // 1=inside, 0=boundary, -1=outside
709
-
710
- // Convex hull
711
- PolygonClip.convexHull(points)
712
-
713
- // Bounding box
714
- PolygonClip.boundingBox(polygon) // {minX, minY, maxX, maxY}
715
- PolygonClip.bboxIntersects(bbox1, bbox2)
716
- ```
287
+ ### Validation
717
288
 
718
- #### ClipPathResolver
289
+ Find and fix problems:
719
290
 
720
291
  ```js
721
- import { ClipPathResolver } from '@emasoft/svg-matrix';
722
-
723
- // Parse and resolve clipPath
724
- const clipData = ClipPathResolver.parseClipPathElement(element);
725
- const clipPolygon = ClipPathResolver.resolveClipPath(clipData, targetBBox);
726
-
727
- // Shape to polygon
728
- ClipPathResolver.shapeToPolygon({ type: 'circle', cx: 100, cy: 100, r: 50 }, { samples: 32 })
729
- ClipPathResolver.pathToPolygon(pathData, { samples: 20 })
730
- ClipPathResolver.polygonToPathData(polygon)
731
-
732
- // Apply clipPath to element
733
- ClipPathResolver.applyClipPath(elementData, clipPathData, targetBBox)
734
- ```
735
-
736
- ### SVG Element Resolution
292
+ import { validateSvg, fixInvalidSvg } from '@emasoft/svg-matrix';
737
293
 
738
- #### UseSymbolResolver
294
+ const result = await validateSvg('icon.svg');
295
+ console.log(result.valid); // true/false
296
+ console.log(result.issues); // Array of problems
739
297
 
740
- ```js
741
- import { UseSymbolResolver } from '@emasoft/svg-matrix';
742
-
743
- const useData = UseSymbolResolver.parseUseElement(useElement);
744
- const symbolData = UseSymbolResolver.parseSymbolElement(symbolElement);
745
- const resolved = UseSymbolResolver.resolveUse(useData, svgDocument);
746
- const flattened = UseSymbolResolver.flattenResolvedUse(resolved);
747
- UseSymbolResolver.resolveAllUses(svgDocument)
298
+ const fixed = await fixInvalidSvg('broken.svg');
299
+ console.log(fixed.svg); // Fixed SVG string
748
300
  ```
749
301
 
750
- #### MarkerResolver
751
-
752
- ```js
753
- import { MarkerResolver } from '@emasoft/svg-matrix';
754
-
755
- const markerData = MarkerResolver.parseMarkerElement(markerElement);
756
- const vertices = MarkerResolver.getPathVertices(pathData);
757
- const transform = MarkerResolver.getMarkerTransform(markerData, vertex, angle, strokeWidth);
758
- const instances = MarkerResolver.resolveMarkers(pathD, { 'marker-start': m1, 'marker-end': m2, strokeWidth: 2 });
759
- MarkerResolver.markersToPathData(instances)
760
- ```
302
+ ### Exclusive Features (Not in SVGO)
761
303
 
762
- #### PatternResolver
304
+ | Function | Description |
305
+ |----------|-------------|
306
+ | `flattenClipPaths()` | Flatten clip-paths to geometry |
307
+ | `flattenMasks()` | Flatten masks to geometry |
308
+ | `flattenGradients()` | Bake gradients into fills |
309
+ | `flattenPatterns()` | Expand pattern tiles |
310
+ | `flattenUseElements()` | Inline use/symbol references |
311
+ | `textToPath()` | Convert text to path outlines |
312
+ | `detectCollisions()` | GJK collision detection |
313
+ | `validateSVG()` | W3C schema validation |
314
+ | `decomposeTransform()` | Matrix decomposition |
763
315
 
764
- ```js
765
- import { PatternResolver } from '@emasoft/svg-matrix';
316
+ ### Attribute Preservation
766
317
 
767
- const patternData = PatternResolver.parsePatternElement(patternElement);
768
- const tiles = PatternResolver.resolvePattern(patternData, targetBBox);
769
- PatternResolver.applyPattern(elementData, patternData, targetBBox)
770
- PatternResolver.patternToClipPath(patternData, targetBBox)
771
- PatternResolver.patternToPathData(patternData, targetBBox)
772
- ```
318
+ When converting shapes or flattening transforms, ALL attributes are preserved:
773
319
 
774
- #### MaskResolver
320
+ | Category | Attributes |
321
+ |----------|------------|
322
+ | **Critical** | `clip-path`, `mask`, `filter`, `opacity` |
323
+ | **Markers** | `marker-start`, `marker-mid`, `marker-end` |
324
+ | **Paint** | `fill`, `stroke`, `fill-opacity`, `stroke-opacity` |
325
+ | **Stroke** | `stroke-width`, `stroke-dasharray`, `stroke-linecap` |
326
+ | **URL refs** | `url(#gradient)`, `url(#pattern)`, `url(#clip)` |
775
327
 
776
- ```js
777
- import { MaskResolver } from '@emasoft/svg-matrix';
328
+ > **Why this matters:** Many SVG tools silently drop `clip-path` and `mask` attributes, causing visual corruption. svg-matrix preserves everything.
778
329
 
779
- const maskData = MaskResolver.parseMaskElement(maskElement);
780
- const maskPolygon = MaskResolver.resolveMask(maskData, targetBBox);
781
- MaskResolver.applyMask(elementPolygon, maskData, targetBBox)
782
- MaskResolver.colorToLuminance({ r, g, b }) // sRGB luminance
783
- ```
330
+ ---
784
331
 
785
- ### Advanced Features
332
+ <!-- Geometric divider: Angle construction -->
333
+ <p align="center">
334
+ <img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='20' viewBox='0 0 400 20'%3E%3Cg stroke='%23ccc' stroke-width='0.5' fill='none'%3E%3Cline x1='0' y1='10' x2='160' y2='10'/%3E%3Cpath d='M180 15 L200 5 L220 15'/%3E%3Cpath d='M190 12 A7 7 0 0 1 195 8' stroke='%23999'/%3E%3Cline x1='240' y1='10' x2='400' y2='10'/%3E%3C/g%3E%3C/svg%3E" alt=""/>
335
+ </p>
786
336
 
787
- #### MeshGradient (SVG 2.0)
337
+ ## Precision Comparison
788
338
 
789
- ```js
790
- import { MeshGradient } from '@emasoft/svg-matrix';
339
+ svg-matrix vs standard JavaScript (float64):
791
340
 
792
- // Parse mesh gradient
793
- const meshDef = MeshGradient.parseMeshGradientElement(element);
794
- const meshData = MeshGradient.parseMeshGradient(meshDef);
341
+ | Operation | JS Error | svg-matrix Error | Improvement |
342
+ |-----------|----------|------------------|-------------|
343
+ | Point evaluation | `1.4e-14` | `0` (exact) | 14+ digits |
344
+ | Bezier tangent | `1.1e-16` | `< 1e-78` | 62+ digits |
345
+ | Arc length | `2.8e-13` | `< 1e-50` | 37+ digits |
346
+ | Bounding box | `1.1e-13` | `0` (exact) | 13+ digits |
347
+ | Self-intersection | Boolean only | `1.4e-58` | 58+ digits |
795
348
 
796
- // Coons patch evaluation
797
- const patch = new MeshGradient.CoonsPatch(topEdge, rightEdge, bottomEdge, leftEdge, cornerColors);
798
- const { point, color } = patch.evaluate(u, v);
349
+ ---
799
350
 
800
- // Rasterize to ImageData
801
- const imageData = MeshGradient.rasterizeMeshGradient(meshData, width, height);
351
+ ## Installation
802
352
 
803
- // Convert to polygons for vector export
804
- const polygons = MeshGradient.meshGradientToPolygons(meshData, { subdivisions: 16 });
353
+ **Requires Node.js 24+** (released 2025)
805
354
 
806
- // Clip and export
807
- const clipped = MeshGradient.clipMeshGradient(meshData, clipPolygon, { subdivisions: 32 });
808
- const svgPaths = MeshGradient.clippedMeshToSVG(clipped);
355
+ ```bash
356
+ npm install @emasoft/svg-matrix
809
357
  ```
810
358
 
811
- #### TextToPath
359
+ ### In JavaScript/TypeScript
812
360
 
813
361
  ```js
814
- import { TextToPath } from '@emasoft/svg-matrix';
815
- import opentype from 'opentype.js';
816
-
817
- const font = await opentype.load('font.ttf');
818
-
819
- // Convert text to path
820
- const pathData = TextToPath.textToPath("Hello", {
821
- x: 100, y: 100,
822
- fontSize: 24,
823
- font: font,
824
- textAnchor: TextToPath.TextAnchor.MIDDLE,
825
- dominantBaseline: TextToPath.DominantBaseline.MIDDLE
826
- });
827
-
828
- // Parse text element
829
- const textData = TextToPath.parseTextElement(textElement);
830
- const result = TextToPath.textElementToPath(textData, { font });
831
-
832
- // Measure text
833
- const metrics = TextToPath.measureText("Hello", { fontSize: "20px" }, font);
834
- const bbox = TextToPath.getTextBBox(textData);
362
+ import { Matrix, Vector, Transforms2D } from '@emasoft/svg-matrix';
835
363
  ```
836
364
 
837
- ### Convenience Functions
838
-
839
- Direct exports for common operations:
840
-
841
- ```js
842
- import {
843
- // Transforms
844
- translate2D, rotate2D, scale2D, transform2D,
845
- translate3D, scale3D, transform3D,
846
-
847
- // Shape conversion
848
- circleToPath, ellipseToPath, rectToPath, lineToPath,
849
- polygonToPath, polylineToPath,
850
-
851
- // Path manipulation
852
- parsePath, pathToString, pathToAbsolute, pathToCubics, transformPath,
853
- elementToPath,
854
-
855
- // Matrix/Vector creation
856
- identity, zeros, vec, mat,
857
-
858
- // Precision control
859
- setPrecision, getPrecision,
365
+ ### In HTML (no install)
860
366
 
861
- // Constants
862
- getKappa
863
- } from '@emasoft/svg-matrix';
367
+ ```html
368
+ <script type="module">
369
+ import { Matrix, Transforms2D } from 'https://esm.sh/@emasoft/svg-matrix';
370
+ </script>
864
371
  ```
865
372
 
866
- ### Logging
867
-
868
- Control library logging output:
869
-
870
- ```js
871
- import { Logger, LogLevel, setLogLevel, enableFileLogging } from '@emasoft/svg-matrix';
373
+ > **Note:** Node.js 24+ is required for modern ECMAScript features. If you need older Node support, please [open an issue](https://github.com/Emasoft/SVG-MATRIX/issues).
872
374
 
873
- // Suppress all logging
874
- setLogLevel(LogLevel.SILENT);
875
-
876
- // Enable only errors
877
- setLogLevel(LogLevel.ERROR);
878
-
879
- // Enable warnings and errors (default)
880
- setLogLevel(LogLevel.WARN);
375
+ ---
881
376
 
882
- // Enable all logging including debug
883
- setLogLevel(LogLevel.DEBUG);
377
+ ## More Documentation
884
378
 
885
- // Write logs to file
886
- enableFileLogging('/path/to/log.txt');
379
+ - [Full API Reference](API.md)
380
+ - [svglinter Documentation](docs/SVGLINTER.md)
381
+ - [Bezier Analysis Examples](test/bezier-analysis-example.js)
382
+ - [Path Analysis Examples](test/path-analysis-example.js)
887
383
 
888
- // Direct Logger access
889
- Logger.level = LogLevel.INFO;
890
- Logger.warn('Custom warning');
891
- Logger.debug('Debug info');
892
- ```
384
+ ---
893
385
 
894
- ## License
386
+ <p align="center">
387
+ <img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='30' viewBox='0 0 200 30'%3E%3Cg stroke='%23ddd' stroke-width='0.5' fill='none'%3E%3Cline x1='0' y1='15' x2='60' y2='15'/%3E%3Crect x='70' y='10' width='10' height='10' transform='rotate(45 75 15)'/%3E%3Ccircle cx='100' cy='15' r='5'/%3E%3Crect x='120' y='10' width='10' height='10' transform='rotate(45 125 15)'/%3E%3Cline x1='140' y1='15' x2='200' y2='15'/%3E%3C/g%3E%3C/svg%3E" alt=""/>
388
+ </p>
895
389
 
896
- MIT
390
+ <p align="center">
391
+ <strong>MIT License</strong><br/>
392
+ <em>Built with mathematical precision</em>
393
+ </p>