add .rsla and .vsla generic raster and vector sla output formats
This commit is contained in:
parent
61d977e169
commit
95eadc60df
12 changed files with 427 additions and 35 deletions
|
|
@ -48,6 +48,8 @@
|
||||||
"kiri/mode/sla/driver",
|
"kiri/mode/sla/driver",
|
||||||
"kiri/mode/sla/x_cxdlp",
|
"kiri/mode/sla/x_cxdlp",
|
||||||
"kiri/mode/sla/x_photon",
|
"kiri/mode/sla/x_photon",
|
||||||
|
"kiri/mode/sla/x_rsla",
|
||||||
|
"kiri/mode/sla/x_vsla",
|
||||||
"kiri/mode/sla/export",
|
"kiri/mode/sla/export",
|
||||||
"kiri/mode/sla/slice",
|
"kiri/mode/sla/slice",
|
||||||
"kiri/core/codec",
|
"kiri/core/codec",
|
||||||
|
|
@ -56,4 +58,4 @@
|
||||||
"kiri/run/worker",
|
"kiri/run/worker",
|
||||||
"kiri/run/engine",
|
"kiri/run/engine",
|
||||||
"main/kiri"
|
"main/kiri"
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,9 @@ function device_from_code(code,mode) {
|
||||||
bedBelt: valueOf(set.bed_belt, false),
|
bedBelt: valueOf(set.bed_belt, false),
|
||||||
resolutionX: valueOf(set.resolution_x, 1600),
|
resolutionX: valueOf(set.resolution_x, 1600),
|
||||||
resolutionY: valueOf(set.resolution_y, 900),
|
resolutionY: valueOf(set.resolution_y, 900),
|
||||||
|
slaFormat: valueOf(set.sla_format, ''),
|
||||||
|
slaFileExt: valueOf(set.sla_file_ext, ''),
|
||||||
|
slaFormatVersion: valueOf(set.sla_format_version, 1),
|
||||||
deviceZMax: valueOf(set.z_move_max, 0),
|
deviceZMax: valueOf(set.z_move_max, 0),
|
||||||
gcodeTime: valueOf(set.time_factor, 1),
|
gcodeTime: valueOf(set.time_factor, 1),
|
||||||
maxHeight: valueOf(set.build_height, 150),
|
maxHeight: valueOf(set.build_height, 150),
|
||||||
|
|
@ -367,7 +370,10 @@ export const conf = {
|
||||||
bedHeight: 1.5,
|
bedHeight: 1.5,
|
||||||
maxHeight: 150,
|
maxHeight: 150,
|
||||||
resolutionX: 1600,
|
resolutionX: 1600,
|
||||||
resolutionY: 900
|
resolutionY: 900,
|
||||||
|
slaFormat: "",
|
||||||
|
slaFileExt: "",
|
||||||
|
slaFormatVersion: 1
|
||||||
},
|
},
|
||||||
// process defaults SLA:Process
|
// process defaults SLA:Process
|
||||||
p:{
|
p:{
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,8 @@ function setDeviceCode(code, devicename) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let allowSLADeviceEdit = mode !== 'SLA' || dev.slaFormat === 'vsla' || dev.slaFormat === 'rsla';
|
||||||
|
|
||||||
// disable editing for non-local devices
|
// disable editing for non-local devices
|
||||||
[
|
[
|
||||||
// ui.deviceName,
|
// ui.deviceName,
|
||||||
|
|
@ -226,14 +228,14 @@ function setDeviceCode(code, devicename) {
|
||||||
ui.extOffsetX,
|
ui.extOffsetX,
|
||||||
ui.extOffsetY
|
ui.extOffsetY
|
||||||
].forEach(function(e) {
|
].forEach(function(e) {
|
||||||
e.disabled = !local;
|
e.disabled = !local || !allowSLADeviceEdit;
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.devices.save.disabled = !local;
|
ui.devices.save.disabled = !local || !allowSLADeviceEdit;
|
||||||
ui.devices.delete.disabled = !local;
|
ui.devices.delete.disabled = !local;
|
||||||
ui.devices.rename.disabled = !local;
|
ui.devices.rename.disabled = !local;
|
||||||
ui.devices.export.disabled = !local;
|
ui.devices.export.disabled = !local;
|
||||||
ui.devices.add.style.display = mode === 'SLA' ? 'none' : '';
|
ui.devices.add.style.display = allowSLADeviceEdit ? '' : 'none';
|
||||||
|
|
||||||
if (local) {
|
if (local) {
|
||||||
ui.devices.add.innerText = "copy";
|
ui.devices.add.innerText = "copy";
|
||||||
|
|
@ -426,4 +428,4 @@ function updateDeviceSelector(devices, selector, selected) {
|
||||||
api.platform.layout();
|
api.platform.layout();
|
||||||
updateDeviceList();
|
updateDeviceList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
src/kiri/dev/sla/Any.Generic.Raster.SLA.json
Normal file
15
src/kiri/dev/sla/Any.Generic.Raster.SLA.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"no_clone": false,
|
||||||
|
"cmd": {},
|
||||||
|
"settings": {
|
||||||
|
"bed_width": 120,
|
||||||
|
"bed_depth": 68,
|
||||||
|
"bed_height": 1,
|
||||||
|
"max_height": 150,
|
||||||
|
"resolution_x": 2560,
|
||||||
|
"resolution_y": 1440,
|
||||||
|
"sla_format": "rsla",
|
||||||
|
"sla_file_ext": "rsla",
|
||||||
|
"sla_format_version": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/kiri/dev/sla/Any.Generic.Vector.SLA.json
Normal file
15
src/kiri/dev/sla/Any.Generic.Vector.SLA.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"no_clone": false,
|
||||||
|
"cmd": {},
|
||||||
|
"settings": {
|
||||||
|
"bed_width": 120,
|
||||||
|
"bed_depth": 68,
|
||||||
|
"bed_height": 1,
|
||||||
|
"max_height": 150,
|
||||||
|
"resolution_x": 2560,
|
||||||
|
"resolution_y": 1440,
|
||||||
|
"sla_format": "vsla",
|
||||||
|
"sla_file_ext": "vsla",
|
||||||
|
"sla_format_version": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -63,21 +63,14 @@ export function printDownload(output, api, names) {
|
||||||
$('print-layers').value = layers;
|
$('print-layers').value = layers;
|
||||||
$('print-time').value = `${print_hrs}:${print_min}:${print_sec}`;
|
$('print-time').value = `${print_hrs}:${print_min}:${print_sec}`;
|
||||||
|
|
||||||
switch (device.deviceName) {
|
let ext = device.slaFileExt || ({
|
||||||
case 'Anycubic.Photon':
|
'Anycubic.Photon': 'photon',
|
||||||
download.innerText += " .photon";
|
'Anycubic.Photon.S': 'photons'
|
||||||
download.onclick = () => { saveFile(api, file, ".photon") };
|
}[device.deviceName]) || 'cxdlp';
|
||||||
break;
|
|
||||||
case 'Anycubic.Photon.S':
|
ext = ext.startsWith(".") ? ext : `.${ext}`;
|
||||||
download.innerText += " .photons";
|
download.innerText = `${api.language.current.ex_dwnl || 'download'} ${ext}`;
|
||||||
download.onclick = () => { saveFile(api, file, ".photons") };
|
download.onclick = () => { saveFile(api, file, ext) };
|
||||||
break;
|
|
||||||
case 'Creality.Halot.Sky':
|
|
||||||
default:
|
|
||||||
download.innerText += " .cxdlp";
|
|
||||||
download.onclick = () => { saveFile(api, file, ".cxdlp") };
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveFile(api, file, ext) {
|
function saveFile(api, file, ext) {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
import { CXDLP } from './x_cxdlp.js';
|
import { CXDLP } from './x_cxdlp.js';
|
||||||
import { photon } from './x_photon.js';
|
import { photon } from './x_photon.js';
|
||||||
import { SLA } from './init-work.js';
|
import { SLA } from './init-work.js';
|
||||||
|
import { RSLA } from './x_rsla.js';
|
||||||
|
import { VSLA } from './x_vsla.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DRIVER CONTRACT - runs in worker
|
* DRIVER CONTRACT - runs in worker
|
||||||
|
|
@ -33,16 +35,30 @@ export function sla_export(print, online, ondone) {
|
||||||
layermax = Math.max(widget.slices.length);
|
layermax = Math.max(widget.slices.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
let isPhoton = false;
|
let format = device.slaFormat || ({
|
||||||
|
'Anycubic.Photon': 'photon',
|
||||||
|
'Anycubic.Photon.S': 'photons',
|
||||||
|
}[device.deviceName]) || 'cxdlp';
|
||||||
|
|
||||||
switch (device.deviceName) {
|
if (format === 'vsla') {
|
||||||
case 'Anycubic.Photon':
|
return VSLA.encode(print, (progress, message) => {
|
||||||
case 'Anycubic.Photon.S':
|
online({ progress, message });
|
||||||
isPhoton = true;
|
}).then(output => {
|
||||||
break;
|
let { file, layers, volume } = output;
|
||||||
|
ondone({ width, height, file, layers, volume }, [file]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPhoton) {
|
if (format === 'rsla') {
|
||||||
|
return RSLA.encode(print, (progress, message) => {
|
||||||
|
online({ progress, message });
|
||||||
|
}).then(output => {
|
||||||
|
let { file, layers, volume } = output;
|
||||||
|
ondone({ width, height, file, layers, volume }, [file]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format === 'photon' || format === 'photons') {
|
||||||
let legacyMode = SLA.legacy || alias > 1,
|
let legacyMode = SLA.legacy || alias > 1,
|
||||||
part1 = legacyMode ? 0.25 : 0.85,
|
part1 = legacyMode ? 0.25 : 0.85,
|
||||||
part2 = (1 - part1),
|
part2 = (1 - part1),
|
||||||
|
|
@ -73,9 +89,9 @@ export function sla_export(print, online, ondone) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let exp_func = {
|
let exp_func = {
|
||||||
'Anycubic.Photon': photon.generatePhoton,
|
photon: photon.generatePhoton,
|
||||||
'Anycubic.Photon.S': photon.generatePhotons,
|
photons: photon.generatePhotons,
|
||||||
}[device.deviceName] || photon.generatePhoton;
|
}[format] || photon.generatePhoton;
|
||||||
|
|
||||||
let file = exp_func(print, {
|
let file = exp_func(print, {
|
||||||
width: width,
|
width: width,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ function init(worker) {
|
||||||
async function sla_prepare(widgets, settings, update) {
|
async function sla_prepare(widgets, settings, update) {
|
||||||
self.kiri_worker.current.print = newPrint(settings, widgets);
|
self.kiri_worker.current.print = newPrint(settings, widgets);
|
||||||
if (!SLA.wasm) {
|
if (!SLA.wasm) {
|
||||||
fetch('/wasm/kiri-sla.wasm')
|
SLA.wasmPromise = SLA.wasmPromise || fetch('/wasm/kiri-sla.wasm')
|
||||||
.then(response => response.arrayBuffer())
|
.then(response => response.arrayBuffer())
|
||||||
.then(bytes => WebAssembly.instantiate(bytes, {
|
.then(bytes => WebAssembly.instantiate(bytes, {
|
||||||
env: {
|
env: {
|
||||||
|
|
@ -44,6 +44,7 @@ async function sla_prepare(widgets, settings, update) {
|
||||||
rle_encode: exports.rle_encode
|
rle_encode: exports.rle_encode
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
await SLA.wasmPromise;
|
||||||
}
|
}
|
||||||
update(1);
|
update(1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
|
import { SLA } from './init-work.js';
|
||||||
|
|
||||||
const default_values = {
|
const default_values = {
|
||||||
magic1: 'CXSW3DV2',
|
magic1: 'CXSW3DV2',
|
||||||
magic2: 'CXSW3DV2',
|
magic2: 'CXSW3DV2',
|
||||||
|
|
@ -392,7 +394,7 @@ CXDLP.render = function(params) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let wasm = kiri.driver.SLA.wasm;
|
let wasm = SLA.wasm;
|
||||||
let imagelen = width * height;
|
let imagelen = width * height;
|
||||||
let writer = new self.DataWriter(new DataView(wasm.memory.buffer), imagelen);
|
let writer = new self.DataWriter(new DataView(wasm.memory.buffer), imagelen);
|
||||||
writer.writeU16(width, true);
|
writer.writeU16(width, true);
|
||||||
|
|
|
||||||
144
src/kiri/mode/sla/work/x_rsla.js
Normal file
144
src/kiri/mode/sla/work/x_rsla.js
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
|
import { JSZip } from '../../../../ext/jszip-esm.js';
|
||||||
|
import { PNG } from '../../../../ext/pngjs.esm.js';
|
||||||
|
import { photon } from './x_photon.js';
|
||||||
|
|
||||||
|
const FORMAT = "rsla";
|
||||||
|
const MIME = "application/vnd.gridspace.rsla+zip";
|
||||||
|
|
||||||
|
function encode(print, progress) {
|
||||||
|
let { settings, widgets } = print;
|
||||||
|
let { device, process } = settings;
|
||||||
|
let width = device.resolutionX;
|
||||||
|
let height = device.resolutionY;
|
||||||
|
let scaleX = width / device.bedWidth;
|
||||||
|
let scaleY = height / device.bedDepth;
|
||||||
|
let layermax = 0;
|
||||||
|
|
||||||
|
widgets = widgets.filter(w => !w.track.ignore && !w.meta.disabled);
|
||||||
|
widgets.forEach(widget => {
|
||||||
|
layermax = Math.max(layermax, widget.slices.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
let layers = [];
|
||||||
|
let volume = 0;
|
||||||
|
let zip = new JSZip();
|
||||||
|
|
||||||
|
for (let index=0; index<layermax; index++) {
|
||||||
|
let rendered = photon.renderLayerWasm({
|
||||||
|
index,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
widgets,
|
||||||
|
scaleX,
|
||||||
|
scaleY,
|
||||||
|
masks: []
|
||||||
|
});
|
||||||
|
if (rendered.end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = `layers/${index.toString().padStart(6, "0")}.png`;
|
||||||
|
let layer = {
|
||||||
|
index,
|
||||||
|
z: round(process.slaFirstOffset + process.slaSlice * index),
|
||||||
|
area: round(rendered.area),
|
||||||
|
file
|
||||||
|
};
|
||||||
|
|
||||||
|
volume += rendered.area * process.slaSlice;
|
||||||
|
layers.push(layer);
|
||||||
|
zip.file(file, imageToPNG(rendered.image, width, height));
|
||||||
|
|
||||||
|
if (progress) progress((index / layermax) * 0.85, "raster_gen");
|
||||||
|
}
|
||||||
|
|
||||||
|
let manifest = createManifest({ device, process, layers, volume });
|
||||||
|
zip.file("manifest.json", JSON.stringify(manifest, null, 2));
|
||||||
|
|
||||||
|
return zip.generateAsync({
|
||||||
|
type: "arraybuffer",
|
||||||
|
compression: "DEFLATE",
|
||||||
|
compressionOptions: { level: 6 },
|
||||||
|
streamFiles: true,
|
||||||
|
mimeType: MIME
|
||||||
|
}, meta => {
|
||||||
|
if (progress) progress(0.85 + (meta.percent / 100) * 0.15, "zip_gen");
|
||||||
|
}).then(file => {
|
||||||
|
return { file, layers: layers.length, volume };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createManifest(params) {
|
||||||
|
let { device, process, layers, volume } = params;
|
||||||
|
|
||||||
|
return {
|
||||||
|
format: FORMAT,
|
||||||
|
version: device.slaFormatVersion || 1,
|
||||||
|
units: "mm",
|
||||||
|
coordinateSystem: {
|
||||||
|
layerImage: "top-left",
|
||||||
|
model: "bed-center",
|
||||||
|
x: "right",
|
||||||
|
y: "back",
|
||||||
|
z: "up"
|
||||||
|
},
|
||||||
|
bed: {
|
||||||
|
width: device.bedWidth,
|
||||||
|
depth: device.bedDepth,
|
||||||
|
height: device.maxHeight || device.bedHeight
|
||||||
|
},
|
||||||
|
resolution: {
|
||||||
|
x: device.resolutionX,
|
||||||
|
y: device.resolutionY
|
||||||
|
},
|
||||||
|
layerHeight: process.slaSlice,
|
||||||
|
layerCount: layers.length,
|
||||||
|
volume: round(volume),
|
||||||
|
raster: {
|
||||||
|
encoding: "png",
|
||||||
|
color: "grayscale",
|
||||||
|
cure: "nonzero",
|
||||||
|
white: "cure",
|
||||||
|
black: "off"
|
||||||
|
},
|
||||||
|
process: {
|
||||||
|
bottomLayers: process.slaBaseLayers,
|
||||||
|
exposure: process.slaLayerOn,
|
||||||
|
bottomExposure: process.slaBaseOn,
|
||||||
|
lightOffDelay: process.slaLayerOff,
|
||||||
|
bottomLightOffDelay: process.slaBaseOff,
|
||||||
|
liftHeight: process.slaPeelDist,
|
||||||
|
bottomLiftHeight: process.slaBasePeelDist,
|
||||||
|
liftSpeed: process.slaPeelLiftRate * 60,
|
||||||
|
bottomLiftSpeed: process.slaBasePeelLiftRate * 60,
|
||||||
|
retractSpeed: process.slaPeelDropRate * 60,
|
||||||
|
firstLayerOffset: process.slaFirstOffset,
|
||||||
|
antiAlias: process.slaAntiAlias
|
||||||
|
},
|
||||||
|
layers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function imageToPNG(image, width, height) {
|
||||||
|
let data = new Uint8Array(width * height);
|
||||||
|
for (let x=0; x<width; x++) {
|
||||||
|
for (let y=0; y<height; y++) {
|
||||||
|
data[y * width + x] = image[x * height + y] ? 255 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PNG.sync.write({ width, height, data }, {
|
||||||
|
colorType: 0,
|
||||||
|
inputColorType: 0,
|
||||||
|
inputHasAlpha: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function round(value) {
|
||||||
|
return Number.parseFloat(Number(value || 0).toFixed(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RSLA = {
|
||||||
|
encode
|
||||||
|
};
|
||||||
193
src/kiri/mode/sla/work/x_vsla.js
Normal file
193
src/kiri/mode/sla/work/x_vsla.js
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
|
import { JSZip } from '../../../../ext/jszip-esm.js';
|
||||||
|
|
||||||
|
const FORMAT = "vsla";
|
||||||
|
const MIME = "application/vnd.gridspace.vsla+zip";
|
||||||
|
|
||||||
|
function encode(print, progress) {
|
||||||
|
let { settings, widgets } = print;
|
||||||
|
let { device, process } = settings;
|
||||||
|
let layermax = 0;
|
||||||
|
|
||||||
|
widgets = widgets.filter(w => !w.track.ignore && !w.meta.disabled);
|
||||||
|
widgets.forEach(widget => {
|
||||||
|
layermax = Math.max(layermax, widget.slices.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
let layers = [];
|
||||||
|
let volume = 0;
|
||||||
|
for (let index=0; index<layermax; index++) {
|
||||||
|
let layer = collectLayer({ index, widgets, process });
|
||||||
|
volume += layer.area * process.slaSlice;
|
||||||
|
layers.push(layer);
|
||||||
|
if (progress) progress((index / layermax) * 0.7, "vector_gen");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (layers.length && layers.last().polygons.length === 0) {
|
||||||
|
layers.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
let manifest = createManifest({ device, process, layers, volume });
|
||||||
|
let zip = new JSZip();
|
||||||
|
zip.file("manifest.json", JSON.stringify(manifest, null, 2));
|
||||||
|
layers.forEach(layer => {
|
||||||
|
zip.file(layer.file, createLayerSVG({ device, layer }));
|
||||||
|
});
|
||||||
|
|
||||||
|
return zip.generateAsync({
|
||||||
|
type: "arraybuffer",
|
||||||
|
compression: "DEFLATE",
|
||||||
|
compressionOptions: { level: 6 },
|
||||||
|
streamFiles: true,
|
||||||
|
mimeType: MIME
|
||||||
|
}, meta => {
|
||||||
|
if (progress) progress(0.7 + (meta.percent / 100) * 0.3, "zip_gen");
|
||||||
|
}).then(file => {
|
||||||
|
return { file, layers: layers.length, volume };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectLayer(params) {
|
||||||
|
let { index, widgets, process } = params;
|
||||||
|
let polygons = [];
|
||||||
|
let area = 0;
|
||||||
|
let z = process.slaFirstOffset + process.slaSlice * index;
|
||||||
|
|
||||||
|
widgets.forEach(widget => {
|
||||||
|
let slice = widget.slices[index];
|
||||||
|
if (!slice) return;
|
||||||
|
|
||||||
|
let polys = slice.unioned;
|
||||||
|
if (!polys) polys = slice.tops.map(t => t.poly);
|
||||||
|
polys = polys ? polys.slice() : [];
|
||||||
|
if (slice.supports) polys.appendAll(slice.supports);
|
||||||
|
|
||||||
|
polygons.appendAll(polys.map(poly => {
|
||||||
|
let moved = poly.clone(true).move(widget.track.pos);
|
||||||
|
area += moved.areaDeep();
|
||||||
|
return moved;
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (slice.z !== undefined) {
|
||||||
|
z = slice.z;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
index,
|
||||||
|
z: round(z),
|
||||||
|
area: round(area),
|
||||||
|
file: `layers/${index.toString().padStart(6, "0")}.svg`,
|
||||||
|
polygons
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createManifest(params) {
|
||||||
|
let { device, process, layers, volume } = params;
|
||||||
|
let layerList = layers.map(layer => {
|
||||||
|
return {
|
||||||
|
index: layer.index,
|
||||||
|
z: layer.z,
|
||||||
|
area: layer.area,
|
||||||
|
file: layer.file,
|
||||||
|
empty: layer.polygons.length === 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
format: FORMAT,
|
||||||
|
version: device.slaFormatVersion || 1,
|
||||||
|
units: "mm",
|
||||||
|
coordinateSystem: {
|
||||||
|
layerSvg: "top-left",
|
||||||
|
model: "bed-center",
|
||||||
|
x: "right",
|
||||||
|
y: "back",
|
||||||
|
z: "up"
|
||||||
|
},
|
||||||
|
cure: {
|
||||||
|
geometry: "filled-svg-paths",
|
||||||
|
fill: "#000",
|
||||||
|
fillRule: "evenodd",
|
||||||
|
background: "transparent"
|
||||||
|
},
|
||||||
|
bed: {
|
||||||
|
width: device.bedWidth,
|
||||||
|
depth: device.bedDepth,
|
||||||
|
height: device.maxHeight || device.bedHeight
|
||||||
|
},
|
||||||
|
resolution: {
|
||||||
|
x: device.resolutionX,
|
||||||
|
y: device.resolutionY
|
||||||
|
},
|
||||||
|
layerHeight: process.slaSlice,
|
||||||
|
layerCount: layers.length,
|
||||||
|
volume: round(volume),
|
||||||
|
process: {
|
||||||
|
bottomLayers: process.slaBaseLayers,
|
||||||
|
exposure: process.slaLayerOn,
|
||||||
|
bottomExposure: process.slaBaseOn,
|
||||||
|
lightOffDelay: process.slaLayerOff,
|
||||||
|
bottomLightOffDelay: process.slaBaseOff,
|
||||||
|
liftHeight: process.slaPeelDist,
|
||||||
|
bottomLiftHeight: process.slaBasePeelDist,
|
||||||
|
liftSpeed: process.slaPeelLiftRate * 60,
|
||||||
|
bottomLiftSpeed: process.slaBasePeelLiftRate * 60,
|
||||||
|
retractSpeed: process.slaPeelDropRate * 60,
|
||||||
|
firstLayerOffset: process.slaFirstOffset,
|
||||||
|
antiAlias: process.slaAntiAlias
|
||||||
|
},
|
||||||
|
layers: layerList
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLayerSVG(params) {
|
||||||
|
let { device, layer } = params;
|
||||||
|
let width = round(device.bedWidth);
|
||||||
|
let depth = round(device.bedDepth);
|
||||||
|
let body = layer.polygons.map(poly => {
|
||||||
|
return ` <path fill="#000" fill-rule="evenodd" d="${polyToPath(poly, device)}"/>`;
|
||||||
|
}).join("\n");
|
||||||
|
|
||||||
|
return [
|
||||||
|
`<?xml version="1.0" encoding="UTF-8"?>`,
|
||||||
|
`<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="${width}mm" height="${depth}mm" viewBox="0 0 ${width} ${depth}" data-layer="${layer.index}" data-z="${layer.z}">`,
|
||||||
|
body,
|
||||||
|
`</svg>`,
|
||||||
|
``
|
||||||
|
].join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function polyToPath(poly, device) {
|
||||||
|
let parts = [];
|
||||||
|
appendPoly(parts, poly, device);
|
||||||
|
return parts.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendPoly(parts, poly, device) {
|
||||||
|
appendPath(parts, poly, device);
|
||||||
|
if (poly.inner) {
|
||||||
|
poly.inner.forEach(inner => appendPoly(parts, inner, device));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendPath(parts, poly, device) {
|
||||||
|
let points = poly.points || [];
|
||||||
|
if (points.length === 0) return;
|
||||||
|
|
||||||
|
points.forEach((point, index) => {
|
||||||
|
let x = round(point.x + device.bedWidth / 2);
|
||||||
|
let y = round(device.bedDepth - (point.y + device.bedDepth / 2));
|
||||||
|
parts.push(`${index === 0 ? "M" : "L"}${x},${y}`);
|
||||||
|
});
|
||||||
|
parts.push("Z");
|
||||||
|
}
|
||||||
|
|
||||||
|
function round(value) {
|
||||||
|
return Number.parseFloat(Number(value || 0).toFixed(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const VSLA = {
|
||||||
|
encode
|
||||||
|
};
|
||||||
|
|
@ -556,7 +556,7 @@ const dispatch = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
export(data, send) {
|
async export(data, send) {
|
||||||
const mode = data.settings.mode;
|
const mode = data.settings.mode;
|
||||||
const driver = drivers[mode];
|
const driver = drivers[mode];
|
||||||
|
|
||||||
|
|
@ -566,7 +566,7 @@ const dispatch = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let output;
|
let output;
|
||||||
driver.export(current.print, (line, direct) => {
|
let result = driver.export(current.print, (line, direct) => {
|
||||||
send.data({line}, direct);
|
send.data({line}, direct);
|
||||||
}, (done) => {
|
}, (done) => {
|
||||||
// SLA workaround
|
// SLA workaround
|
||||||
|
|
@ -574,6 +574,9 @@ const dispatch = {
|
||||||
}, (debug) => {
|
}, (debug) => {
|
||||||
send.data({debug});
|
send.data({debug});
|
||||||
});
|
});
|
||||||
|
if (result && result.then) {
|
||||||
|
await result;
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
bounds,
|
bounds,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue