From 3be111af437d599fd691392a05a745b668b9de4d Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Tue, 19 May 2026 19:42:55 -0400 Subject: [PATCH] add volume rendering and use that for sla by default now --- src/kiri/app/layers.js | 36 +++++++++++++++++++++++ src/kiri/mode/sla/work/slice.js | 51 ++++++++++++++++++++++++++------- 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/kiri/app/layers.js b/src/kiri/app/layers.js index efbaf061..7111984c 100644 --- a/src/kiri/app/layers.js +++ b/src/kiri/app/layers.js @@ -212,6 +212,42 @@ export class Layers { } } + /** + * Add closed polygons as filled Z volumes. + * By default, the polygon Z is treated as the volume center. Pass + * { base:true } to treat polygon Z as the bottom face instead. + * @param {Polygon|Array} polys - Closed polygon(s), with inners preserved as voids + * @param {object} [options] - Options: {height, z, base, outline} + * @returns {Layers} This instance for chaining + */ + addZVolume(polys, options) { + const opts = options || {}; + const height = opts.height || 1; + const faces = this.current.faces; + polys = Array.isArray(polys) ? polys.clone(true) : [ polys.clone(true) ]; + + for (let poly of polys) { + if (poly.open || poly.length < 3) { + continue; + } + + let z = opts.z !== undefined ? opts.z : poly.getZ(), + zoff = opts.base ? z : z - height / 2, + volume = poly.setZ(0).extrude(height, { zadd: zoff }); + + faces.appendAll(volume); + + if (opts.outline) { + this.addPolys([ + poly.clone(true).setZ(zoff), + poly.clone(true).setZ(zoff + height) + ]); + } + } + + return this; + } + /** * Add 2D polyline paths with width (usually FDM extrusion paths). * Creates flat ribbons with vertex normals for lighting. diff --git a/src/kiri/mode/sla/work/slice.js b/src/kiri/mode/sla/work/slice.js index 3f9b865a..76dd43fd 100644 --- a/src/kiri/mode/sla/work/slice.js +++ b/src/kiri/mode/sla/work/slice.js @@ -245,36 +245,67 @@ export function sla_slice(settings, widget, onupdate, ondone) { }); }; +const SLA_RENDER_THICK = true; + function doRender(widget) { widget.slices.forEach(slice => { - const render = slice.output(), lopacity = 0.6, line = 0x010101; + const render = slice.output(), + height = slice.height || 0.05, + lopacity = SLA_RENDER_THICK ? 1 : 0.6, + line = 0x010101; if (slice.unioned) { slice.unioned.forEach(poly => { poly = poly.clone(true);//.move(widget.track.pos); - render - .setLayer("layers", { line, face: 0x0099cc, lopacity }) - .addAreas([poly], { outline: true }); + renderSlicePoly(render, "layers", poly, { + line, + face: 0x0099cc, + lopacity, + height + }); }); } else if (slice.tops) { slice.tops.forEach(top => { let poly = top.poly;//.clone(true).move(widget.track.pos); - render - .setLayer("layers", { line, face: 0xfcba03, lopacity }) - .addAreas([poly], { outline: true }); + renderSlicePoly(render, "layers", poly, { + line, + face: 0xfcba03, + lopacity, + height + }); }); } if (slice.supports) { slice.supports.forEach(poly => { - render - .setLayer("support", { line, face: 0xfcba03, lopacity }) - .addAreas([poly], { outline: true }); + renderSlicePoly(render, "support", poly, { + line, + face: 0xfcba03, + lopacity, + height + }); }); } }); } +function renderSlicePoly(render, layer, poly, options) { + let { line, face, lopacity, height } = options; + + render.setLayer(layer, { + line, + face, + lopacity, + opacity: SLA_RENDER_THICK ? 1 : undefined + }); + + if (SLA_RENDER_THICK) { + render.addZVolume([poly], { height, outline: false }); + } else { + render.addAreas([poly], { outline: true }); + } +} + function computeSupports(widget, process, progress) { let slices = widget.slices, baseIndex = widget.lastraft ? widget.lastraft.index : 0,