/
index.js
156 lines (135 loc) · 4.35 KB
/
index.js
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
/*
JSCAD Object to SVG Format Serialization
## License
Copyright (c) 2018 JSCAD Organization https://github.com/jscad
All code released under MIT license
Notes:
1) CAG conversion to:
SVG GROUP containing a SVG PATH for each CAG outline path
2) CSG conversion to:
mesh
3) Path2D conversion to:
none
TBD
1) add Path2D conversion
*/
const {isCAG} = require('@jscad/csg')
const {toArray} = require('@jscad/io-utils/arrays')
const stringify = require('onml/lib/stringify')
const mimeType = 'image/svg+xml'
/** Serialize the give objects to SVG format.
* @param {Object} [options] - options for serialization
* @param {Object|Array} objects - objects to serialize as SVG
* @returns {Array} serialized contents, SVG format
*/
const serialize = (...params) => {
let options = {}
let objects
if (params.length === 0) {
throw new Error('no arguments supplied to serialize function !')
} else if (params.length === 1) {
// assumed to be object(s)
objects = Array.isArray(params[0]) ? params[0] : params
} else if (params.length > 1) {
options = params[0]
objects = params[1]
}
// make sure we always deal with arrays of objects as inputs
objects = toArray(objects)
console.log('params', params)
console.log('options', options)
console.log('objects', objects)
const defaults = {
statusCallback: null,
unit: 'mm', // em | ex | px | in | cm | mm | pt | pc
decimals: 10000
}
options = Object.assign({}, defaults, options)
options.statusCallback && options.statusCallback({progress: 0})
// get the lower and upper bounds of ALL convertable objects
var bounds = getBounds(objects)
var width = 0
var height = 0
if (bounds) {
width = Math.round((bounds[1].x - bounds[0].x) * options.decimals) / options.decimals
height = Math.round((bounds[1].y - bounds[0].y) * options.decimals) / options.decimals
}
var body = ['svg',
{
width: width + options.unit,
height: height + options.unit,
viewBox: ('0 0 ' + width + ' ' + height),
version: '1.1',
baseProfile: 'tiny',
xmlns: 'http://www.w3.org/2000/svg',
'xmlns:xlink': 'http://www.w3.org/1999/xlink'
}
]
if (bounds) {
body = body.concat(convertObjects(objects, bounds, options))
}
var svg = `<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by OpenJSCAD.org -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
${stringify(body)}`
options.statusCallback && options.statusCallback({progress: 100})
return [svg]
}
const getBounds = (objects) => {
let bounds = null
objects.forEach(function (object, i) {
if (isCAG(object) && object.sides.length > 0) {
let cagBounds = object.getBounds()
if (bounds !== null) {
bounds[0] = bounds[0].min(cagBounds[0])
bounds[1] = bounds[1].max(cagBounds[1])
} else {
bounds = cagBounds
}
}
})
return bounds
}
const convertObjects = (objects, bounds, options) => {
var xoffset = 0 - bounds[0].x
var yoffset = 0 - bounds[0].y
let contents = []
objects.forEach(function (object, i) {
options.statusCallback && options.statusCallback({progress: 100 * i / objects.length})
if (isCAG(object) && object.sides.length > 0) {
contents.push(convertCAG(object, [xoffset, yoffset], options))
}
})
return contents
}
const convertCAG = (object, offsets, options) => {
var paths = object.getOutlinePaths()
return convertPaths(paths, offsets, options)
}
const convertPaths = (paths, offsets, options) => {
return paths.reduce(function (res, path, i) {
return res.concat([['path', {d: convertPath(path, offsets, options)}]])
}, ['g'])
}
const convertPath = (path, offsets, options) => {
var pointindex
var str = ''
var numpointsClosed = path.points.length + (path.closed ? 1 : 0)
for (pointindex = 0; pointindex < numpointsClosed; pointindex++) {
var pointindexwrapped = pointindex
if (pointindexwrapped >= path.points.length) pointindexwrapped -= path.points.length
var point = path.points[pointindexwrapped]
let x = Math.round((point.x + offsets[0]) * options.decimals) / options.decimals
let y = Math.round((point.y + offsets[1]) * options.decimals) / options.decimals
if (pointindex > 0) {
str += `L${x} ${y}`
} else {
str += `M${x} ${y}`
}
}
return str
}
module.exports = {
serialize,
mimeType
}