/** Copyright Stewart Allen -- All Rights Reserved */ "use strict"; (function() { const KIRI = self.kiri, BASE = self.base, POLY = BASE.polygons; class Render { constructor() { this.layers = {}; this.profiles = {}; this.stats = { contour: 0, flat_line: 0, flat_poly: 0, line_poly: 0, line: 0 }; } setLayer(layer, colors, off) { let layers = this.layers; if (typeof(colors) === 'number') { colors = { line: colors, face: colors, opacity: 1 }; } this.current = layers[layer] = layers[layer] || { off: off === true, lines: [], polys: [], faces: [], paths: [], color: colors || { line: 0, face: 0, opacity: 1 }, }; return this; } addLine(p1, p2) { this.current.lines.push(p1, p2); return this; } addLines(lines, options) { if (options) { options.open = true; const polys = []; for (let i=0; i { if (options.z !== undefined) p.setZ(options.z); if (options.color) p.color = options.color; }); } this.current.polys.appendAll(polys); this.stats.line_poly += polys.length; return this; } addAreas(polys, options) { const faces = this.current.faces; polys = Array.isArray(polys) ? polys : [ polys ]; polys.forEach(poly => { poly.earcut().forEach(ep => { ep.forEachPoint(p => { faces.push(p.x, p.y, p.z) }); }); }); if (options && options.outline) { this.addPolys(polys.clone(true)); } } addFlats(polys, options) { const opts = options || {}; const offset = opts.offset || 1; polys = flat(polys); if (!polys.length) { return; } const z = polys[0].getZ(), faces = this.current.faces; const open = opts.open || false; const off_opt = { z, flat: true, type: open ? ClipperLib.EndType.etOpenSquare : undefined, }; polys.forEach(poly => { let exp = off_opt.outs = []; if (open) { exp.appendAll(POLY.expand_lines(poly, offset * 0.9, z)); this.stats.flat_line = 0; } else { POLY.offset([poly], offset * 0.9, off_opt); POLY.offset([poly], -offset * 0.9, off_opt); this.stats.flat_poly = 0; } if (opts.outline) { this.addPolys(exp.clone()); } POLY.nest(exp).forEach((poly,i) => { poly.earcut().forEach(ep => { ep.forEachPoint(p => { faces.push(p.x, p.y, p.z) }); }); }); }); return this; } addPaths(polys, options) { const opts = options || {}; const height = opts.height || 1; const offset = opts.offset || 1; polys = flat(polys); if (!polys.length) { return; } const profiles = this.profiles; const prokey = `${offset}x${height}`; if (!profiles[prokey]) { const profile = new THREE.Shape(); profile.moveTo(-offset, -height); profile.lineTo(-offset, height); profile.lineTo( offset, height); profile.lineTo( offset, -height); profiles[prokey] = profile; } const profile = profiles[prokey].clone(); polys.forEach(poly => { const contour = []; poly.points.forEach(p => { contour.push(new THREE.Vector2(p.x, p.y)); }); const {index, faces} = ProfiledContourGeometry(profile, contour, poly.isClosed()); const cur = this.current; const one = cur.paths[0]; if (one) { // merge all contour geometry for massive speed gain const add = one.faces.length / 3; for (let i=0; i