forked from jscad/OpenJSCAD.org
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
210 lines (167 loc) · 5.78 KB
/
index.js
File metadata and controls
210 lines (167 loc) · 5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
JSCAD Object to AMF (XML) Format Serialization
## License
Copyright (c) 2018 JSCAD Organization https://github.com/jscad
All code released under MIT license
Notes:
1) geom2 conversion to:
none
2) geom3 conversion to:
mesh
3) path2 conversion to:
none
TBD
1) support zip output
*/
/**
* Serializer of JSCAD geometries to AMF source data (XML)
*
* The serialization of the following geometries are possible.
* - serialization of 3D geometry (geom3) to AMF object (a unique mesh containing both vertices and volumes)
*
* Colors are added to volumes when found on the 3D geometry.
* Colors are added to triangles when found on individual polygons.
*
* @module io/amf-serializer
* @example
* const { serializer, mimeType } = require('@jscad/amf-serializer')
*/
const stringify = require('onml/lib/stringify')
const { geometries, modifiers } = require('@jscad/modeling')
const { flatten, toArray } = require('@jscad/array-utils')
const mimeType = 'application/amf+xml'
/**
* Serialize the give objects (geometry) to AMF source data (XML).
* @param {Object} options - options for serialization
* @param {String} [options.unit='millimeter'] - unit of design; millimeter, inch, feet, meter or micrometer
* @param {Function} [options.statusCallback] - call back function for progress ({ progress: 0-100 })
* @param {...Object} objects - objects to serialize into AMF source data
* @returns {Array} serialized contents, AMF source data(XML)
* @alias module:io/amf-serializer.serialize
* @example
* const geometry = primitives.cube()
* const amfData = serializer({unit: 'meter'}, geometry)
*/
const serialize = (options, ...objects) => {
const defaults = {
statusCallback: null,
unit: 'millimeter' // millimeter, inch, feet, meter or micrometer
}
options = Object.assign({}, defaults, options)
objects = flatten(objects)
// convert only 3D geometries
let objects3d = objects.filter((object) => geometries.geom3.isA(object))
if (objects3d.length === 0) throw new Error('only 3D geometries can be serialized to AMF')
if (objects.length !== objects3d.length) console.warn('some objects could not be serialized to AMF')
// convert to triangles
objects3d = toArray(modifiers.generalize({ snap: true, triangulate: true }, objects3d))
options.statusCallback && options.statusCallback({ progress: 0 })
// construct the contents of the XML
let body = ['amf',
{
unit: options.unit,
version: '1.1'
},
['metadata', { type: 'author' }, 'Created by JSCAD']
]
body = body.concat(translateObjects(objects3d, options))
// convert the contents to AMF (XML) format
const amf = `<?xml version="1.0" encoding="UTF-8"?>
${stringify(body, 2)}`
options && options.statusCallback && options.statusCallback({ progress: 100 })
return [amf]
}
const translateObjects = (objects, options) => {
const contents = []
objects.forEach((object, i) => {
const polygons = geometries.geom3.toPolygons(object)
if (polygons.length > 0) {
options.id = i
contents.push(convertToObject(object, options))
}
})
return contents
}
const convertToObject = (object, options) => {
const contents = ['object', { id: options.id }, convertToMesh(object, options)]
return contents
}
const convertToMesh = (object, options) => {
let contents = ['mesh', {}, convertToVertices(object, options)]
contents = contents.concat(convertToVolumes(object, options))
return contents
}
/*
* This section converts each 3D geometry to a list of vertex / coordinates
*/
const convertToVertices = (object, options) => {
const contents = ['vertices', {}]
const vertices = []
const polygons = geometries.geom3.toPolygons(object)
polygons.forEach((polygon) => {
for (let i = 0; i < polygon.vertices.length; i++) {
vertices.push(convertToVertex(polygon.vertices[i], options))
}
})
return contents.concat(vertices)
}
const convertToVertex = (vertex, options) => {
const contents = ['vertex', {}, convertToCoordinates(vertex, options)]
return contents
}
const convertToCoordinates = (vertex, options) => {
const contents = ['coordinates', {}, ['x', {}, vertex[0]], ['y', {}, vertex[1]], ['z', {}, vertex[2]]]
return contents
}
/*
* This section converts each 3D geometry to a list of volumes consisting of indexes into the list of vertices
*/
const convertToVolumes = (object, options) => {
const objectcolor = convertColor(object.color)
const polygons = geometries.geom3.toPolygons(object)
const contents = []
let volume = ['volume', {}]
// add color specification if available
if (objectcolor) {
volume.push(objectcolor)
}
let vcount = 0
polygons.forEach((polygon) => {
if (polygon.vertices.length < 3) {
return
}
const triangles = convertToTriangles(polygon, vcount, options)
volume = volume.concat(triangles)
vcount += polygon.vertices.length
})
contents.push(volume)
return contents
}
const convertColor = (color) => {
if (color) {
if (color.length < 4) color.push(1.0)
return ['color', {}, ['r', {}, color[0]], ['g', {}, color[1]], ['b', {}, color[2]], ['a', {}, color[3]]]
}
return null
}
const convertToColor = (polygon, options) => {
const color = polygon.color
return convertColor(color)
}
const convertToTriangles = (polygon, index, options) => {
const polycolor = convertToColor(polygon, options)
// making sure they are all triangles (triangular polygons)
const contents = []
for (let i = 0; i < polygon.vertices.length - 2; i++) {
if (polycolor) {
contents.push(['triangle', {}, polycolor, ['v1', {}, index], ['v2', {}, (index + i + 1)], ['v3', {}, (index + i + 2)]])
} else {
contents.push(['triangle', {}, ['v1', {}, index], ['v2', {}, (index + i + 1)], ['v3', {}, (index + i + 2)]])
}
}
return contents
}
module.exports = {
serialize,
mimeType
}