@jscad/svg-deserializer 2.5.11 → 2.5.12

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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [2.5.12](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/svg-deserializer@2.5.11...@jscad/svg-deserializer@2.5.12) (2025-09-20)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **svg-deserializer:** svg path should return one geom2 ([4500b71](https://github.com/jscad/OpenJSCAD.org/commit/4500b71579fd805e5f740a7b4605b14986bbe14e))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [2.5.11](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/svg-deserializer@2.5.10...@jscad/svg-deserializer@2.5.11) (2024-12-29)
7
18
 
8
19
  **Note:** Version bump only for package @jscad/svg-deserializer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jscad/svg-deserializer",
3
- "version": "2.5.11",
3
+ "version": "2.5.12",
4
4
  "description": "SVG Deserializer for JSCAD",
5
5
  "homepage": "https://openjscad.xyz/",
6
6
  "repository": "https://github.com/jscad/OpenJSCAD.org",
@@ -33,13 +33,13 @@
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
35
  "@jscad/array-utils": "2.1.4",
36
- "@jscad/io-utils": "2.0.30",
37
- "@jscad/modeling": "2.12.5",
36
+ "@jscad/io-utils": "2.0.31",
37
+ "@jscad/modeling": "2.12.6",
38
38
  "saxes": "5.0.1"
39
39
  },
40
40
  "devDependencies": {
41
41
  "ava": "3.15.0",
42
42
  "nyc": "15.1.0"
43
43
  },
44
- "gitHead": "d8010c4f1d70685404a510d604f8b6d5c362f93a"
44
+ "gitHead": "11a9a2d9235d804360116f776076c9f3237a3eb0"
45
45
  }
@@ -133,16 +133,23 @@ const shapesMapGeometry = (obj, objectify, params) => {
133
133
  const listofpaths = expandPath(obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups, segments, pathSelfClosed)
134
134
  // order is important
135
135
  const listofentries = Object.entries(listofpaths).sort((a, b) => a[0].localeCompare(b[0]))
136
- const shapes = listofentries.map((entry) => {
137
- const path = entry[1]
138
- if (target === 'geom2' && path.isClosed) {
139
- const points = geometries.path2.toPoints(path).slice()
140
- points.push(points[0]) // add first point again to create closing sides
141
- return geometries.geom2.fromPoints(points)
142
- }
143
- return path
144
- })
145
- return shapes
136
+
137
+ if (target === 'geom2') {
138
+ // concatenate all sides to a single geom2
139
+ const outlines = []
140
+ listofentries.forEach((entry) => {
141
+ const path = entry[1]
142
+ // discard unclosed paths
143
+ if (path.isClosed) {
144
+ const points = geometries.path2.toPoints(path)
145
+ outlines.push(points)
146
+ }
147
+ })
148
+ if (outlines.length === 0) return []
149
+ return geometries.geom2.create(outlines)
150
+ } else {
151
+ return listofentries.map((entry) => entry[1])
152
+ }
146
153
  }
147
154
  }
148
155
 
@@ -156,6 +163,18 @@ const appendPoints = (points, geometry) => {
156
163
  return geometries.path2.fromPoints({ }, points)
157
164
  }
158
165
 
166
+ /*
167
+ * Expands the given SVG path object into a set of path segments.
168
+ * @param {object} obj - SVG path object to expand
169
+ * @param {number} svgUnitsPmm - SVG units per millimeter
170
+ * @param {number} svgUnitsX - X-axis SVG units
171
+ * @param {number} svgUnitsY - Y-axis SVG units
172
+ * @param {number} svgUnitsV - SVG units value
173
+ * @param {object} svgGroups - SVG groups object
174
+ * @param {number} segments - number of segments per full rotation
175
+ * @param {boolean} pathSelfClosed - Whether the path is self-closed
176
+ * @returns {object} a object containing the named paths
177
+ */
159
178
  const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups, segments, pathSelfClosed) => {
160
179
  const paths = {}
161
180
  const on = 'path'
@@ -454,13 +473,13 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
454
473
  by = rf[1]
455
474
  }
456
475
  break
457
- case 'h': // relative Horzontal line to
476
+ case 'h': // relative Horizontal line to
458
477
  while (pts.length >= i + 1) {
459
478
  cx = cx + parseFloat(pts[i++])
460
479
  paths[pathName] = appendPoints([svg2cag([cx, cy], svgUnitsPmm)], paths[pathName])
461
480
  }
462
481
  break
463
- case 'H': // absolute Horzontal line to
482
+ case 'H': // absolute Horizontal line to
464
483
  while (pts.length >= i + 1) {
465
484
  cx = parseFloat(pts[i++])
466
485
  paths[pathName] = appendPoints([svg2cag([cx, cy], svgUnitsPmm)], paths[pathName])
@@ -500,7 +519,7 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
500
519
  pc = true
501
520
  break
502
521
  default:
503
- console.log('Warning: Unknow PATH command [' + co.c + ']')
522
+ console.log('Warning: Unknown PATH command [' + co.c + ']')
504
523
  break
505
524
  }
506
525
 
@@ -2,7 +2,7 @@ const test = require('ava')
2
2
 
3
3
  const deserializer = require('../src/index.js')
4
4
 
5
- const { measurements } = require('@jscad/modeling')
5
+ const { geometries, measurements } = require('@jscad/modeling')
6
6
 
7
7
  // deserializer
8
8
 
@@ -173,7 +173,8 @@ test('deserialize : instantiate svg (path: simple) to objects', (t) => {
173
173
  let observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
174
174
  t.is(observed.length, 1)
175
175
  let shape = observed[0]
176
- t.is(shape.sides.length, 3)
176
+ t.is(shape.sides.length, 1)
177
+ t.true(geometries.geom2.isA(shape))
177
178
 
178
179
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
179
180
  t.is(observed.length, 1)
@@ -190,7 +191,8 @@ test('deserialize : instantiate svg (path: simple) to objects', (t) => {
190
191
  observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
191
192
  t.is(observed.length, 1)
192
193
  shape = observed[0]
193
- t.is(shape.sides.length, 3)
194
+ t.is(shape.sides.length, 1)
195
+ t.true(geometries.geom2.isA(shape))
194
196
 
195
197
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
196
198
  t.is(observed.length, 1)
@@ -204,7 +206,8 @@ test('deserialize : instantiate svg (path: simple) to objects', (t) => {
204
206
  observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
205
207
  t.is(observed.length, 1)
206
208
  shape = observed[0]
207
- t.is(shape.sides.length, 3)
209
+ t.is(shape.sides.length, 1)
210
+ t.true(geometries.geom2.isA(shape))
208
211
 
209
212
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
210
213
  t.is(observed.length, 1)
@@ -218,7 +221,8 @@ test('deserialize : instantiate svg (path: simple) to objects', (t) => {
218
221
  observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
219
222
  t.is(observed.length, 1)
220
223
  shape = observed[0]
221
- t.is(shape.sides.length, 8)
224
+ t.is(shape.sides.length, 1)
225
+ t.true(geometries.geom2.isA(shape))
222
226
 
223
227
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
224
228
  t.is(observed.length, 1)
@@ -237,7 +241,11 @@ test('deserialize : instantiate svg (path: arc) to objects', (t) => {
237
241
  let observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
238
242
  t.is(observed.length, 2)
239
243
  let shape = observed[0]
240
- t.is(shape.sides.length, 27)
244
+ t.is(shape.sides.length, 1)
245
+ t.true(geometries.geom2.isA(shape))
246
+ shape = observed[1]
247
+ t.is(shape.sides.length, 1)
248
+ t.true(geometries.geom2.isA(shape))
241
249
 
242
250
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
243
251
  t.is(observed.length, 2)
@@ -264,7 +272,8 @@ test('deserialize : instantiate svg (path: with bezier) to objects', (t) => {
264
272
  let observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
265
273
  t.is(observed.length, 1)
266
274
  let shape = observed[0]
267
- t.is(shape.sides.length, 14)
275
+ t.is(shape.sides.length, 1)
276
+ t.true(geometries.geom2.isA(shape))
268
277
 
269
278
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
270
279
  t.is(observed.length, 1)
@@ -285,7 +294,8 @@ test('deserialize : instantiate svg (path: with bezier) to objects', (t) => {
285
294
  observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
286
295
  t.is(observed.length, 1)
287
296
  shape = observed[0]
288
- t.is(shape.sides.length, 62)
297
+ t.is(shape.sides.length, 1)
298
+ t.true(geometries.geom2.isA(shape))
289
299
 
290
300
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
291
301
  t.is(observed.length, 1)
@@ -300,7 +310,8 @@ test('deserialize : instantiate svg (path: with bezier) to objects', (t) => {
300
310
  observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
301
311
  t.is(observed.length, 1)
302
312
  shape = observed[0]
303
- t.is(shape.sides.length, 28)
313
+ t.is(shape.sides.length, 1)
314
+ t.true(geometries.geom2.isA(shape))
304
315
 
305
316
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
306
317
  t.is(observed.length, 1)
@@ -314,9 +325,7 @@ test('deserialize : instantiate svg (path: with bezier) to objects', (t) => {
314
325
  </svg>`
315
326
 
316
327
  observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
317
- t.is(observed.length, 2)
318
- shape = observed[0]
319
- t.is(shape.points.length, 14) // open path
328
+ t.is(observed.length, 0) // open path
320
329
 
321
330
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
322
331
  t.is(observed.length, 2)
@@ -332,9 +341,7 @@ test('deserialize : instantiate svg (path: with bezier) to objects', (t) => {
332
341
  </svg>`
333
342
 
334
343
  observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
335
- t.is(observed.length, 1)
336
- shape = observed[0]
337
- t.is(shape.points.length, 29) // open path
344
+ t.is(observed.length, 0) // open path
338
345
 
339
346
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
340
347
  t.is(observed.length, 1)
@@ -349,7 +356,7 @@ test('deserialize : instantiate svg (path: with bezier) to objects', (t) => {
349
356
  observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
350
357
  t.is(observed.length, 1)
351
358
  shape = observed[0]
352
- t.is(shape.sides.length, 101)
359
+ t.is(shape.sides.length, 1)
353
360
  t.deepEqual(shape.color, [1, 0, 0, 1])
354
361
 
355
362
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
@@ -408,11 +415,10 @@ test('deserialize : instantiate svg produced by inkscape to objects', (t) => {
408
415
  `
409
416
 
410
417
  let observed = deserializer.deserialize({ filename: 'inkscape', output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
411
- t.is(observed.length, 2)
418
+ t.is(observed.length, 1)
412
419
  let shape = observed[0]
413
- t.is(shape.sides.length, 19)
414
- shape = observed[1]
415
- t.is(shape.sides.length, 20)
420
+ t.is(shape.sides.length, 2)
421
+ t.true(geometries.geom2.isA(shape))
416
422
 
417
423
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
418
424
  t.is(observed.length, 2)
@@ -432,11 +438,10 @@ test('deserialize : instantiate shape with a hole to objects', (t) => {
432
438
  `
433
439
 
434
440
  let observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
435
- t.is(observed.length, 2)
441
+ t.is(observed.length, 1)
436
442
  let shape = observed[0]
437
- t.is(shape.sides.length, 38)
438
- shape = observed[1]
439
- t.is(shape.sides.length, 38)
443
+ t.is(shape.sides.length, 2)
444
+ t.true(geometries.geom2.isA(shape))
440
445
 
441
446
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
442
447
  t.is(observed.length, 2)
@@ -456,9 +461,10 @@ test('deserialize : instantiate shape with a nested hole to objects', (t) => {
456
461
  `
457
462
 
458
463
  let observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
459
- t.is(observed.length, 4)
464
+ t.is(observed.length, 1)
460
465
  let shape = observed[0]
461
- t.is(shape.sides.length, 38)
466
+ t.is(shape.sides.length, 4)
467
+ t.true(geometries.geom2.isA(shape))
462
468
 
463
469
  observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
464
470
  t.is(observed.length, 4)