/** Copyright Stewart Allen -- All Rights Reserved */ "use strict"; // dep: main.kiri // dep: geo.base // dep: geo.polygon // dep: kiri.widget // dep: kiri.slice // dep: kiri.layers gapp.register("kiri.codec", [], (root, exports) => { const { base, kiri } = root; const decoders = {}; const freeMem = true; const codec = exports({ undef: undefined, encode: encode, decode: decode, registerDecoder: registerDecoder, allocFloat32Array: allocFloat32Array, encodePointArray, decodePointArray, encodePointArray2D, decodePointArray2D, toCodable }); const TYPE = { WIDGET: 100, SLICE: 200, TOP: 300, POLY: 400, LAYERS: 500, }; function toCodable(object) { switch (typeof object) { case 'function': return undefined; // case 'boolean': case 'string': case 'number': return object; case 'object': if (Array.isArray(object)) { return object.map(v => toCodable(v)); } break; } let o = {}; for (let [key, val] of Object.entries(object)) { switch (typeof val) { case 'function': break; case 'object': if (Array.isArray(val)) { val = val.map(v => toCodable(v)); } else { val = toCodable(val); } default: o[key] = val; break; } } return o; } function allocFloat32Array(arg, zeros) { if (arg === undefined) { return undefined; } if (arg.length === 0) { return []; } let f32; if (arg.byteLength) { // already a float array f32 = arg; } else if (Array.isArray(arg)) { // create float array from array f32 = new Float32Array(arg); } else { // usually a number (size) for array f32 = new Float32Array(arg); } if (zeros && f32.byteLength > 0) { zeros.push(f32.buffer); } return f32; } function encode(o, state) { state = state || {}; if (o === null) return null; if (o === undefined) return undefined; if (Array.isArray(o)) { let arr = new Array(o.length), i=0; while (i < o.length) arr[i] = encode(o[i++], state); return arr; } switch (typeof(o)) { case 'boolean': case 'string': case 'number': return o; case 'object': if (o.encode) return o.encode(state); return genericObjectEncode(o, state); } return null; } function decode(o, state) { state = state || {}; if (o === null) return null; if (o === undefined) return undefined; if (Array.isArray(o)) { for (let i=0; i < o.length; i++) o[i] = decode(o[i],state); return o; } switch (typeof(o)) { case 'boolean': case 'string': case 'number': return o; case 'object': if (o.type && decoders[o.type]) return decoders[o.type](o,state); return genODecode(o, state); } return null; } function registerDecoder(type, handler) { decoders[type] = handler; } function genericObjectEncode(o, state) { if (o instanceof Float32Array) return o; let out = {}; for (let k in o) { if (o.hasOwnProperty(k)) out[k] = encode(o[k], state); } return out; } function genODecode(o, state) { if (o instanceof Float32Array) return o; let out = {}; for (let k in o) { if (o.hasOwnProperty(k)) out[k] = decode(o[k], state); } return out; } function encodePointArray(points, state, z) { if (!points) { return null; } const length = points.length; if (length === 0) { return points; } const zeros = state ? state.zeros : undefined; const array = codec.allocFloat32Array(length * 3, zeros); for (let i=0, pos=0, point; i { return { open: poly.open, color: poly.color, points: codec.encodePointArray(poly.points, state.zeros) } }); } kiri.Layers.prototype.encode = function(state) { let zeros = state.zeros; let enc = { type: TYPE.LAYERS, layers: Object.keys(this.layers), data: Object.values(this.layers).map(layer => { const paths = layer.paths; return { // defaults off: layer.off, color: layer.color, // 1D lines and polylines polys: encodeLayerPolys(layer.polys, state), lines: encodePointArray(layer.lines, state), // 2D lines and flat areas faces: codec.allocFloat32Array(layer.faces, zeros), norms: codec.allocFloat32Array(layer.norms, zeros), cface: layer.cface || codec.undef, // 3D lines paths: paths ? { z: paths.z, index: paths.index, faces: codec.allocFloat32Array(paths.faces, zeros), norms: codec.allocFloat32Array(paths.norms, zeros) } : undefined, cpath: layer.cpath || codec.undef, rotation: layer.rotation, position: layer.position } }) }; // aggressively free memory if (freeMem) this.init(); return enc; }; registerDecoder(TYPE.LAYERS, function(v, state) { const render = new kiri.Layers(); const { layers } = v; for (let i=0; i Infinity in material counts (JSON stringify sucks) if (d.cface) { d.cface.forEach(rec => { if (rec.count === null) { rec.count = Infinity; } }); } if (d.cpath) { d.cpath.forEach(rec => { if (rec.count === null) { rec.count = Infinity; } }); } } return render; }); });