@jscad/x3d-serializer 2.4.4 → 3.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -191
- package/README.md +7 -2
- package/dist/jscad-x3d-serializer.es.js +13 -0
- package/dist/jscad-x3d-serializer.min.js +14 -0
- package/package.json +22 -12
- package/rollup.config.js +25 -0
- package/src/index.js +40 -36
- package/tests/geom2ToX3D.test.js +11 -12
- package/tests/geom3ToX3D.test.js +15 -12
- package/tests/path2ToX3D.test.js +10 -12
package/src/index.js
CHANGED
|
@@ -1,20 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
JSCAD Object to X3D (XML) Format Serialization
|
|
3
|
-
|
|
4
|
-
## License
|
|
1
|
+
import { generalize, geom2, geom3, path2, poly2, poly3 } from '@jscad/modeling'
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
import { flatten } from '@jscad/array-utils'
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Notes:
|
|
11
|
-
1) geom2 conversion to:
|
|
12
|
-
Polyline2D with lineSegment and Color
|
|
13
|
-
2) geom3 conversion to:
|
|
14
|
-
IndexedTriangleSet with Coordinates and Colors
|
|
15
|
-
3) path2 conversion to:
|
|
16
|
-
Polyline2D with lineSegment and Color
|
|
17
|
-
*/
|
|
5
|
+
import { stringify } from '@jscad/io-utils'
|
|
18
6
|
|
|
19
7
|
/**
|
|
20
8
|
* Serializer of JSCAD geometries to X3D source data (XML).
|
|
@@ -31,23 +19,14 @@ Notes:
|
|
|
31
19
|
* const { serializer, mimeType } = require('@jscad/x3d-serializer')
|
|
32
20
|
*/
|
|
33
21
|
|
|
34
|
-
const { geometries, modifiers } = require('@jscad/modeling')
|
|
35
|
-
const { geom2, geom3, path2, poly2, poly3 } = geometries
|
|
36
|
-
|
|
37
|
-
const { flatten } = require('@jscad/array-utils')
|
|
38
|
-
|
|
39
|
-
const stringify = require('onml/lib/stringify')
|
|
40
|
-
|
|
41
|
-
// http://www.web3d.org/x3d/content/X3dTooltips.html
|
|
42
|
-
// http://www.web3d.org/x3d/content/examples/X3dSceneAuthoringHints.html#Meshes
|
|
43
|
-
// https://x3dgraphics.com/examples/X3dForWebAuthors/Chapter13GeometryTrianglesQuadrilaterals/
|
|
44
|
-
|
|
45
22
|
const mimeType = 'model/x3d+xml'
|
|
23
|
+
const defNames = new Map()
|
|
46
24
|
|
|
47
25
|
/**
|
|
48
26
|
* Serialize the give objects to X3D elements (XML).
|
|
49
27
|
* @param {Object} options - options for serialization, REQUIRED
|
|
50
28
|
* @param {Array} [options.color=[0,0,1,1]] - default color for objects
|
|
29
|
+
* @param {Number} [options.shininess=8/256] - x3d shininess for specular highlights
|
|
51
30
|
* @param {Boolean} [options.smooth=false] - use averaged vertex normals
|
|
52
31
|
* @param {Number} [options.decimals=1000] - multiplier before rounding to limit precision
|
|
53
32
|
* @param {Boolean} [options.metadata=true] - add metadata to 3MF contents, such at CreationDate
|
|
@@ -63,6 +42,7 @@ const mimeType = 'model/x3d+xml'
|
|
|
63
42
|
const serialize = (options, ...objects) => {
|
|
64
43
|
const defaults = {
|
|
65
44
|
color: [0, 0, 1, 1.0], // default colorRGBA specification
|
|
45
|
+
shininess: 8 / 256,
|
|
66
46
|
smooth: false,
|
|
67
47
|
decimals: 1000,
|
|
68
48
|
metadata: true,
|
|
@@ -117,7 +97,7 @@ const convertObjects = (objects, options) => {
|
|
|
117
97
|
|
|
118
98
|
if (geom3.isA(object)) {
|
|
119
99
|
// convert to triangles
|
|
120
|
-
object =
|
|
100
|
+
object = generalize({ snap: true, triangulate: true }, object)
|
|
121
101
|
const polygons = geom3.toPolygons(object)
|
|
122
102
|
if (polygons.length > 0) {
|
|
123
103
|
shapes.push(convertGeom3(object, options))
|
|
@@ -141,7 +121,7 @@ const convertObjects = (objects, options) => {
|
|
|
141
121
|
const convertPath2 = (object, options) => {
|
|
142
122
|
const points = path2.toPoints(object).slice()
|
|
143
123
|
if (points.length > 1 && object.isClosed) points.push(points[0])
|
|
144
|
-
const shape = ['Shape',
|
|
124
|
+
const shape = ['Shape', shapeAttributes(object), convertPolyline2D(poly2.create(points), options)]
|
|
145
125
|
if (object.color) {
|
|
146
126
|
shape.push(convertAppearance(object, 'emissiveColor', options))
|
|
147
127
|
}
|
|
@@ -156,7 +136,7 @@ const convertGeom2 = (object, options) => {
|
|
|
156
136
|
const group = ['Group', {}]
|
|
157
137
|
outlines.forEach((outline) => {
|
|
158
138
|
if (outline.length > 1) outline.push(outline[0]) // close the outline for conversion
|
|
159
|
-
const shape = ['Shape',
|
|
139
|
+
const shape = ['Shape', shapeAttributes(object), convertPolyline2D(poly2.create(outline), options)]
|
|
160
140
|
if (object.color) {
|
|
161
141
|
shape.push(convertAppearance(object, 'emissiveColor', options))
|
|
162
142
|
}
|
|
@@ -165,11 +145,29 @@ const convertGeom2 = (object, options) => {
|
|
|
165
145
|
return group
|
|
166
146
|
}
|
|
167
147
|
|
|
148
|
+
/*
|
|
149
|
+
* generate attributes for Shape node
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
const shapeAttributes = (object, attributes = {}) => {
|
|
153
|
+
if (object.id) {
|
|
154
|
+
Object.assign(attributes, { DEF: checkDefName(object.id) })
|
|
155
|
+
}
|
|
156
|
+
return attributes
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const checkDefName = (defName) => {
|
|
160
|
+
const count = defNames.get(defName) || 0
|
|
161
|
+
defNames.set(defName, count + 1)
|
|
162
|
+
if (count > 0) console.warn(`Warning: object.id set as DEF but not unique. ${defName} set ${count + 1} times.`)
|
|
163
|
+
return defName
|
|
164
|
+
}
|
|
165
|
+
|
|
168
166
|
/*
|
|
169
167
|
* Convert the given object (poly2) to X3D source
|
|
170
168
|
*/
|
|
171
169
|
const convertPolyline2D = (object, options) => {
|
|
172
|
-
const lineSegments = object.
|
|
170
|
+
const lineSegments = object.points.map((p) => `${p[0]} ${p[1]}`).join(' ')
|
|
173
171
|
return ['Polyline2D', { lineSegments }]
|
|
174
172
|
}
|
|
175
173
|
|
|
@@ -180,14 +178,20 @@ const convertAppearance = (object, colorField, options) => {
|
|
|
180
178
|
const colorRGB = object.color.slice(0, 3)
|
|
181
179
|
const color = colorRGB.join(' ')
|
|
182
180
|
const transparency = roundToDecimals(1.0 - object.color[3], options)
|
|
183
|
-
|
|
181
|
+
const materialFields = { [colorField]: color, transparency }
|
|
182
|
+
if (colorField === 'diffuseColor') {
|
|
183
|
+
Object.assign(
|
|
184
|
+
materialFields,
|
|
185
|
+
{ specularColor: '0.2 0.2 0.2', shininess: options.shininess })
|
|
186
|
+
}
|
|
187
|
+
return ['Appearance', ['Material', materialFields]]
|
|
184
188
|
}
|
|
185
189
|
|
|
186
190
|
/*
|
|
187
191
|
* Convert the given object (geom3) to X3D source
|
|
188
192
|
*/
|
|
189
193
|
const convertGeom3 = (object, options) => {
|
|
190
|
-
const shape = ['Shape',
|
|
194
|
+
const shape = ['Shape', shapeAttributes(object), convertMesh(object, options)]
|
|
191
195
|
let appearance = ['Appearance', {}, ['Material']]
|
|
192
196
|
if (object.color) {
|
|
193
197
|
appearance = convertAppearance(object, 'diffuseColor', options)
|
|
@@ -221,7 +225,7 @@ const convertToTriangles = (object, options) => {
|
|
|
221
225
|
polygons.forEach((poly) => {
|
|
222
226
|
const firstVertex = poly.vertices[0]
|
|
223
227
|
for (let i = poly.vertices.length - 3; i >= 0; i--) {
|
|
224
|
-
const triangle = poly3.
|
|
228
|
+
const triangle = poly3.create([
|
|
225
229
|
firstVertex,
|
|
226
230
|
poly.vertices[i + 1],
|
|
227
231
|
poly.vertices[i + 2]
|
|
@@ -285,7 +289,7 @@ const polygons2coordinates = (polygons, options) => {
|
|
|
285
289
|
return [indexList, pointList, colorList]
|
|
286
290
|
}
|
|
287
291
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
292
|
+
export {
|
|
293
|
+
mimeType,
|
|
294
|
+
serialize
|
|
291
295
|
}
|
package/tests/geom2ToX3D.test.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import test from 'ava'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import { countOf } from '../../test/helpers/countOf.js'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import { colorize, geom2, rectangle } from '@jscad/modeling'
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import { serialize } from '../src/index.js'
|
|
8
8
|
|
|
9
9
|
test('serialize 2D geometry to X3D Polyline2D', (t) => {
|
|
10
|
-
const shape1 =
|
|
10
|
+
const shape1 = geom2.create()
|
|
11
11
|
|
|
12
12
|
let results = serialize({}, shape1)
|
|
13
13
|
t.is(results.length, 1)
|
|
@@ -22,9 +22,9 @@ test('serialize 2D geometry to X3D Polyline2D', (t) => {
|
|
|
22
22
|
t.is(countOf('Scene', obs), 2)
|
|
23
23
|
t.is(countOf('Group', obs), 1)
|
|
24
24
|
|
|
25
|
-
const shape2 =
|
|
25
|
+
const shape2 = rectangle()
|
|
26
26
|
|
|
27
|
-
results = serialize({metadata: false}, shape2)
|
|
27
|
+
results = serialize({ metadata: false }, shape2)
|
|
28
28
|
t.is(results.length, 1)
|
|
29
29
|
|
|
30
30
|
obs = results[0]
|
|
@@ -39,9 +39,9 @@ test('serialize 2D geometry to X3D Polyline2D', (t) => {
|
|
|
39
39
|
t.is(countOf('Shape', obs), 2)
|
|
40
40
|
t.is(countOf('Polyline2D', obs), 1)
|
|
41
41
|
|
|
42
|
-
const shape3 =
|
|
42
|
+
const shape3 = colorize([0, 0, 0], shape2)
|
|
43
43
|
|
|
44
|
-
results = serialize({metadata: false}, shape3)
|
|
44
|
+
results = serialize({ metadata: false }, shape3)
|
|
45
45
|
t.is(results.length, 1)
|
|
46
46
|
|
|
47
47
|
obs = results[0]
|
|
@@ -61,8 +61,7 @@ test('serialize 2D geometry to X3D Polyline2D', (t) => {
|
|
|
61
61
|
t.is(countOf('diffuseColor', obs), 0)
|
|
62
62
|
t.is(countOf('emissiveColor', obs), 1)
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
results = serialize({metadata: false}, shape2, shape3)
|
|
64
|
+
results = serialize({ metadata: false }, shape2, shape3)
|
|
66
65
|
t.is(results.length, 1)
|
|
67
66
|
|
|
68
67
|
obs = results[0]
|
|
@@ -80,6 +79,6 @@ test('serialize 2D geometry to X3D Polyline2D', (t) => {
|
|
|
80
79
|
t.is(countOf('Appearance', obs), 2)
|
|
81
80
|
t.is(countOf('Material', obs), 1)
|
|
82
81
|
t.is(countOf('diffuseColor', obs), 0)
|
|
82
|
+
t.is(countOf('specularColor', obs), 0)
|
|
83
83
|
t.is(countOf('emissiveColor', obs), 1)
|
|
84
84
|
})
|
|
85
|
-
|
package/tests/geom3ToX3D.test.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import test from 'ava'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import { countOf } from '../../test/helpers/countOf.js'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import { center, colorize, cube, geom3 } from '@jscad/modeling'
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import { serialize } from '../src/index.js'
|
|
8
8
|
|
|
9
9
|
test('serialize 3D geometry to X3D IndexedTriangleSet', (t) => {
|
|
10
|
-
const
|
|
10
|
+
const g1 = geom3.create()
|
|
11
11
|
|
|
12
|
-
let results =
|
|
12
|
+
let results = serialize({}, g1)
|
|
13
13
|
t.is(results.length, 1)
|
|
14
14
|
|
|
15
15
|
let obs = results[0]
|
|
@@ -21,9 +21,9 @@ test('serialize 3D geometry to X3D IndexedTriangleSet', (t) => {
|
|
|
21
21
|
t.is(countOf('Created by JSCAD', obs), 1)
|
|
22
22
|
t.is(countOf('Scene', obs), 2)
|
|
23
23
|
|
|
24
|
-
const
|
|
24
|
+
const g2 = cube()
|
|
25
25
|
|
|
26
|
-
results =
|
|
26
|
+
results = serialize({ metadata: false }, g2)
|
|
27
27
|
t.is(results.length, 1)
|
|
28
28
|
|
|
29
29
|
obs = results[0]
|
|
@@ -36,13 +36,15 @@ test('serialize 3D geometry to X3D IndexedTriangleSet', (t) => {
|
|
|
36
36
|
t.is(countOf('Scene', obs), 2)
|
|
37
37
|
t.is(countOf('Transform', obs), 2)
|
|
38
38
|
t.is(countOf('Shape', obs), 2)
|
|
39
|
+
t.is(countOf('DEF', obs), 0)
|
|
39
40
|
t.is(countOf('IndexedTriangleSet', obs), 2)
|
|
40
41
|
t.is(countOf('Coordinate', obs), 1)
|
|
41
42
|
t.is(countOf('Color', obs), 1)
|
|
42
43
|
|
|
43
|
-
const
|
|
44
|
+
const g3 = colorize([0.5, 1, 0.5, 1.0], center({ relativeTo: [5, 5, 5] }, cube()))
|
|
45
|
+
g2.id = g3.id = 'g23'
|
|
44
46
|
|
|
45
|
-
results =
|
|
47
|
+
results = serialize({ metadata: false }, g2, g3)
|
|
46
48
|
t.is(results.length, 1)
|
|
47
49
|
|
|
48
50
|
obs = results[0]
|
|
@@ -54,6 +56,7 @@ test('serialize 3D geometry to X3D IndexedTriangleSet', (t) => {
|
|
|
54
56
|
t.is(countOf('Created by JSCAD', obs), 1)
|
|
55
57
|
t.is(countOf('Scene', obs), 2)
|
|
56
58
|
t.is(countOf('Shape', obs), 4)
|
|
59
|
+
t.is(countOf('DEF', obs), 2)
|
|
57
60
|
t.is(countOf('IndexedTriangleSet', obs), 4)
|
|
58
61
|
t.is(countOf('Coordinate', obs), 2)
|
|
59
62
|
// for color
|
|
@@ -61,7 +64,7 @@ test('serialize 3D geometry to X3D IndexedTriangleSet', (t) => {
|
|
|
61
64
|
t.is(countOf('Appearance', obs), 4)
|
|
62
65
|
// for RGB
|
|
63
66
|
t.is(countOf('diffuseColor="0.5 1 0.5"', obs), 1)
|
|
67
|
+
t.is(countOf('specularColor', obs), 1)
|
|
64
68
|
// for facets
|
|
65
69
|
t.is(countOf('normalPerVertex="false"', obs), 2)
|
|
66
|
-
|
|
67
|
-
})
|
|
70
|
+
})
|
package/tests/path2ToX3D.test.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import test from 'ava'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import { countOf } from '../../test/helpers/countOf.js'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import { arc, colorize, path2 } from '@jscad/modeling'
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import { serialize } from '../src/index.js'
|
|
8
8
|
|
|
9
9
|
test('serialize 2D path to X3D Polyline2D', (t) => {
|
|
10
|
-
const
|
|
10
|
+
const p1 = path2.create()
|
|
11
11
|
|
|
12
|
-
let results = serialize({},
|
|
12
|
+
let results = serialize({}, p1)
|
|
13
13
|
t.is(results.length, 1)
|
|
14
14
|
|
|
15
15
|
let obs = results[0]
|
|
@@ -20,9 +20,9 @@ test('serialize 2D path to X3D Polyline2D', (t) => {
|
|
|
20
20
|
t.is(countOf('content', obs), 3)
|
|
21
21
|
t.is(countOf('Created by JSCAD', obs), 1)
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const p2 = arc({ center: [5, 5], endAngle: 45, segments: 16 })
|
|
24
24
|
|
|
25
|
-
results = serialize({metadata: false},
|
|
25
|
+
results = serialize({ metadata: false }, p2)
|
|
26
26
|
t.is(results.length, 1)
|
|
27
27
|
|
|
28
28
|
obs = results[0]
|
|
@@ -33,9 +33,9 @@ test('serialize 2D path to X3D Polyline2D', (t) => {
|
|
|
33
33
|
t.is(countOf('Polyline2D', obs), 1)
|
|
34
34
|
t.is(countOf('lineSegments', obs), 1)
|
|
35
35
|
|
|
36
|
-
const
|
|
36
|
+
const p3 = colorize([0, 0, 0], p2)
|
|
37
37
|
|
|
38
|
-
results = serialize({metadata: false},
|
|
38
|
+
results = serialize({ metadata: false }, p2, p3)
|
|
39
39
|
t.is(results.length, 1)
|
|
40
40
|
|
|
41
41
|
obs = results[0]
|
|
@@ -50,6 +50,4 @@ test('serialize 2D path to X3D Polyline2D', (t) => {
|
|
|
50
50
|
t.is(countOf('Material', obs), 1)
|
|
51
51
|
t.is(countOf('diffuseColor', obs), 0)
|
|
52
52
|
t.is(countOf('emissiveColor', obs), 1)
|
|
53
|
-
|
|
54
53
|
})
|
|
55
|
-
|