register photon extensions. add more .ctb target machines
This commit is contained in:
parent
a1af70b496
commit
304a2a4e24
10 changed files with 329 additions and 89 deletions
15
src/kiri/dev/sla/Creality.LD-002H.CTB.json
Normal file
15
src/kiri/dev/sla/Creality.LD-002H.CTB.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"no_clone": true,
|
||||||
|
"cmd": {},
|
||||||
|
"settings": {
|
||||||
|
"bed_width": 127,
|
||||||
|
"bed_depth": 80,
|
||||||
|
"bed_height": 1,
|
||||||
|
"max_height": 160,
|
||||||
|
"resolution_x": 2560,
|
||||||
|
"resolution_y": 1620,
|
||||||
|
"sla_format": "ctb",
|
||||||
|
"sla_file_ext": "ctb",
|
||||||
|
"sla_format_version": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/kiri/dev/sla/Creality.LD-002R.CTB.json
Normal file
15
src/kiri/dev/sla/Creality.LD-002R.CTB.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"no_clone": true,
|
||||||
|
"cmd": {},
|
||||||
|
"settings": {
|
||||||
|
"bed_width": 119,
|
||||||
|
"bed_depth": 65,
|
||||||
|
"bed_height": 1,
|
||||||
|
"max_height": 160,
|
||||||
|
"resolution_x": 2560,
|
||||||
|
"resolution_y": 1440,
|
||||||
|
"sla_format": "ctb",
|
||||||
|
"sla_file_ext": "ctb",
|
||||||
|
"sla_format_version": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/kiri/dev/sla/Elegoo.Mars.2.Pro.CTB.json
Normal file
15
src/kiri/dev/sla/Elegoo.Mars.2.Pro.CTB.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"no_clone": true,
|
||||||
|
"cmd": {},
|
||||||
|
"settings": {
|
||||||
|
"bed_width": 129,
|
||||||
|
"bed_depth": 80,
|
||||||
|
"bed_height": 1,
|
||||||
|
"max_height": 160,
|
||||||
|
"resolution_x": 1620,
|
||||||
|
"resolution_y": 2560,
|
||||||
|
"sla_format": "ctb",
|
||||||
|
"sla_file_ext": "ctb",
|
||||||
|
"sla_format_version": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/kiri/dev/sla/Elegoo.Saturn.CTB.json
Normal file
15
src/kiri/dev/sla/Elegoo.Saturn.CTB.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"no_clone": true,
|
||||||
|
"cmd": {},
|
||||||
|
"settings": {
|
||||||
|
"bed_width": 192,
|
||||||
|
"bed_depth": 120,
|
||||||
|
"bed_height": 1,
|
||||||
|
"max_height": 200,
|
||||||
|
"resolution_x": 3840,
|
||||||
|
"resolution_y": 2400,
|
||||||
|
"sla_format": "ctb",
|
||||||
|
"sla_file_ext": "ctb",
|
||||||
|
"sla_format_version": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -45,6 +45,41 @@ export const SLA_FORMATS = {
|
||||||
available: true,
|
available: true,
|
||||||
priority: 1,
|
priority: 1,
|
||||||
family: 'chitubox'
|
family: 'chitubox'
|
||||||
|
},
|
||||||
|
pwmo: {
|
||||||
|
id: 'pwmo',
|
||||||
|
ext: 'pwmo',
|
||||||
|
proprietary: true,
|
||||||
|
available: false,
|
||||||
|
family: 'photon-workshop'
|
||||||
|
},
|
||||||
|
pwma: {
|
||||||
|
id: 'pwma',
|
||||||
|
ext: 'pwma',
|
||||||
|
proprietary: true,
|
||||||
|
available: false,
|
||||||
|
family: 'photon-workshop'
|
||||||
|
},
|
||||||
|
pwmb: {
|
||||||
|
id: 'pwmb',
|
||||||
|
ext: 'pwmb',
|
||||||
|
proprietary: true,
|
||||||
|
available: false,
|
||||||
|
family: 'photon-workshop'
|
||||||
|
},
|
||||||
|
pm3n: {
|
||||||
|
id: 'pm3n',
|
||||||
|
ext: 'pm3n',
|
||||||
|
proprietary: true,
|
||||||
|
available: false,
|
||||||
|
family: 'photon-workshop'
|
||||||
|
},
|
||||||
|
pm4n: {
|
||||||
|
id: 'pm4n',
|
||||||
|
ext: 'pm4n',
|
||||||
|
proprietary: true,
|
||||||
|
available: false,
|
||||||
|
family: 'photon-workshop'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
import { CXDLP } from './x_cxdlp.js';
|
import { CXDLP } from './x_cxdlp.js';
|
||||||
import { CTB } from './x_ctb.js';
|
import { CTB } from './x_ctb.js';
|
||||||
import { photon } from './x_photon.js';
|
import { photon } from './x_photon.js';
|
||||||
|
import { PW } from './x_pw.js';
|
||||||
import { SLA } from './init-work.js';
|
import { SLA } from './init-work.js';
|
||||||
import { RSLA } from './x_rsla.js';
|
import { RSLA } from './x_rsla.js';
|
||||||
import { VSLA } from './x_vsla.js';
|
import { VSLA } from './x_vsla.js';
|
||||||
|
|
@ -66,6 +67,11 @@ export function sla_export(print, online, ondone) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (format === 'pwmo' || format === 'pwma' || format === 'pwmb' ||
|
||||||
|
format === 'pm3n' || format === 'pm4n') {
|
||||||
|
return PW.encode(format);
|
||||||
|
}
|
||||||
|
|
||||||
if (format === 'photon' || format === 'photons') {
|
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,
|
||||||
|
|
|
||||||
76
src/kiri/mode/sla/work/raster.js
Normal file
76
src/kiri/mode/sla/work/raster.js
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
|
export function createRasterContext(print) {
|
||||||
|
let { settings, widgets } = print;
|
||||||
|
let { device, process } = settings;
|
||||||
|
let width = device.resolutionX;
|
||||||
|
let height = device.resolutionY;
|
||||||
|
let layermax = 0;
|
||||||
|
|
||||||
|
widgets = widgets.filter(w => !w.track.ignore && !w.meta.disabled);
|
||||||
|
widgets.forEach(widget => {
|
||||||
|
layermax = Math.max(layermax, widget.slices.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
settings,
|
||||||
|
device,
|
||||||
|
process,
|
||||||
|
widgets,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
scaleX: width / device.bedWidth,
|
||||||
|
scaleY: height / device.bedDepth,
|
||||||
|
layermax
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderRasterLayers(print, renderer, onlayer, progress) {
|
||||||
|
let ctx = createRasterContext(print);
|
||||||
|
let { process, widgets, width, height, scaleX, scaleY, layermax } = ctx;
|
||||||
|
let volume = 0;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (let index=0; index<layermax; index++) {
|
||||||
|
let rendered = renderer.renderLayerWasm({
|
||||||
|
index,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
widgets,
|
||||||
|
scaleX,
|
||||||
|
scaleY,
|
||||||
|
masks: []
|
||||||
|
});
|
||||||
|
if (rendered.end) break;
|
||||||
|
|
||||||
|
volume += rendered.area * process.slaSlice;
|
||||||
|
onlayer({
|
||||||
|
ctx,
|
||||||
|
index,
|
||||||
|
rendered,
|
||||||
|
bottom: index < process.slaBaseLayers,
|
||||||
|
z: process.slaFirstOffset + process.slaSlice * (index + 1)
|
||||||
|
});
|
||||||
|
count++;
|
||||||
|
if (progress) progress(index / layermax);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ctx, layers: count, volume };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function imageToRowMajor(image, width, height, map) {
|
||||||
|
let data = new Uint8Array(width * height);
|
||||||
|
|
||||||
|
for (let x=0; x<width; x++) {
|
||||||
|
for (let y=0; y<height; y++) {
|
||||||
|
let value = image[x * height + y] || 0;
|
||||||
|
data[y * width + x] = map ? map(value) : value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function round(value) {
|
||||||
|
return Number.parseFloat(Number(value || 0).toFixed(5));
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { JSZip } from '../../../../ext/jszip-esm.js';
|
import { JSZip } from '../../../../ext/jszip-esm.js';
|
||||||
import { PNG } from '../../../../ext/pngjs.esm.js';
|
import { PNG } from '../../../../ext/pngjs.esm.js';
|
||||||
|
import { imageToRowMajor, renderRasterLayers, round } from './raster.js';
|
||||||
|
|
||||||
const CTB_V3_MAGIC = 0x12fd0086;
|
const CTB_V3_MAGIC = 0x12fd0086;
|
||||||
const HEADER_SIZE = 0x70;
|
const HEADER_SIZE = 0x70;
|
||||||
|
|
@ -20,55 +21,34 @@ function encode(print, progress, renderer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeWithPhoton(print, progress, photon) {
|
function encodeWithPhoton(print, progress, photon) {
|
||||||
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;
|
|
||||||
let layers = [];
|
let layers = [];
|
||||||
let volume = 0;
|
|
||||||
let seed = makeSeed(print);
|
let seed = makeSeed(print);
|
||||||
|
let previews = [
|
||||||
|
createPreview(400, 300),
|
||||||
|
createPreview(200, 125)
|
||||||
|
];
|
||||||
|
|
||||||
widgets = widgets.filter(w => !w.track.ignore && !w.meta.disabled);
|
let { ctx, volume } = renderRasterLayers(print, photon, params => {
|
||||||
widgets.forEach(widget => {
|
let { index, rendered, bottom, z, ctx } = params;
|
||||||
layermax = Math.max(layermax, widget.slices.length);
|
let { process, width, height } = ctx;
|
||||||
});
|
|
||||||
|
|
||||||
for (let index=0; index<layermax; index++) {
|
|
||||||
let rendered = photon.renderLayerWasm({
|
|
||||||
index,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
widgets,
|
|
||||||
scaleX,
|
|
||||||
scaleY,
|
|
||||||
masks: []
|
|
||||||
});
|
|
||||||
if (rendered.end) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pixels = renderToCTBPixels(rendered.image, width, height);
|
let pixels = renderToCTBPixels(rendered.image, width, height);
|
||||||
let encoded = encipher(seed, index, encodeRLE(pixels));
|
let encoded = encipher(seed, index, encodeRLE(pixels));
|
||||||
let bottom = index < process.slaBaseLayers;
|
previews.forEach(preview => accumulatePreview(preview, rendered.image, width, height));
|
||||||
|
|
||||||
volume += rendered.area * process.slaSlice;
|
|
||||||
layers.push({
|
layers.push({
|
||||||
index,
|
index,
|
||||||
z: process.slaFirstOffset + process.slaSlice * (index + 1),
|
z,
|
||||||
exposure: bottom ? process.slaBaseOn : process.slaLayerOn,
|
exposure: bottom ? process.slaBaseOn : process.slaLayerOn,
|
||||||
lightOffDelay: bottom ? process.slaBaseOff : process.slaLayerOff,
|
lightOffDelay: bottom ? process.slaBaseOff : process.slaLayerOff,
|
||||||
liftDistance: bottom ? process.slaBasePeelDist : process.slaPeelDist,
|
liftDistance: bottom ? process.slaBasePeelDist : process.slaPeelDist,
|
||||||
liftSpeed: (bottom ? process.slaBasePeelLiftRate : process.slaPeelLiftRate) * 60,
|
liftSpeed: (bottom ? process.slaBasePeelLiftRate : process.slaPeelLiftRate) * 60,
|
||||||
data: encoded
|
data: encoded
|
||||||
});
|
});
|
||||||
|
}, progress ? (value) => progress(value * 0.85, "ctb_encode") : null);
|
||||||
|
|
||||||
if (progress) progress((index / layermax) * 0.85, "ctb_encode");
|
let { device, process } = ctx;
|
||||||
}
|
let file = writeCTB({ device, process, layers, volume, seed, previews });
|
||||||
|
|
||||||
let file = writeCTB({ device, process, layers, volume, seed });
|
|
||||||
if (progress) progress(1, "ctb_write");
|
if (progress) progress(1, "ctb_write");
|
||||||
|
|
||||||
return { file, layers: layers.length, volume };
|
return { file, layers: layers.length, volume };
|
||||||
|
|
@ -86,6 +66,7 @@ function read(input) {
|
||||||
header,
|
header,
|
||||||
print: readPrintParams(view, header),
|
print: readPrintParams(view, header),
|
||||||
machine: readMachineName(view, header),
|
machine: readMachineName(view, header),
|
||||||
|
previews: readPreviews(view, header),
|
||||||
layers,
|
layers,
|
||||||
layout: inferLayout(header, layers)
|
layout: inferLayout(header, layers)
|
||||||
};
|
};
|
||||||
|
|
@ -141,12 +122,17 @@ function decodeLayer(view, header, layer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeCTB(params) {
|
function writeCTB(params) {
|
||||||
let { device, process, layers, volume, seed } = params;
|
let { device, process, layers, volume, seed, previews } = params;
|
||||||
let machine = device.deviceName || "Generic CTB";
|
let machine = device.deviceName || "Generic CTB";
|
||||||
let machineBytes = new TextEncoder().encode(machine);
|
let machineBytes = new TextEncoder().encode(machine);
|
||||||
|
let previewData = previews.map(encodePreview);
|
||||||
let printTime = Math.round((process.slaBaseLayers * process.slaBaseOn) +
|
let printTime = Math.round((process.slaBaseLayers * process.slaBaseOn) +
|
||||||
Math.max(0, layers.length - process.slaBaseLayers) * process.slaLayerOn);
|
Math.max(0, layers.length - process.slaBaseLayers) * process.slaLayerOn);
|
||||||
let printParamsOffset = HEADER_SIZE;
|
let previewLargeOffset = HEADER_SIZE;
|
||||||
|
let previewLargeDataOffset = previewLargeOffset + 32;
|
||||||
|
let previewSmallOffset = previewLargeDataOffset + previewData[0].length;
|
||||||
|
let previewSmallDataOffset = previewSmallOffset + 32;
|
||||||
|
let printParamsOffset = previewSmallDataOffset + previewData[1].length;
|
||||||
let machineInfoOffset = printParamsOffset + PRINT_PARAMS_SIZE;
|
let machineInfoOffset = printParamsOffset + PRINT_PARAMS_SIZE;
|
||||||
let nameOffset = machineInfoOffset + MACHINE_INFO_SIZE;
|
let nameOffset = machineInfoOffset + MACHINE_INFO_SIZE;
|
||||||
let layerTableOffset = nameOffset + machineBytes.length;
|
let layerTableOffset = nameOffset + machineBytes.length;
|
||||||
|
|
@ -169,10 +155,20 @@ function writeCTB(params) {
|
||||||
layers,
|
layers,
|
||||||
seed,
|
seed,
|
||||||
printTime,
|
printTime,
|
||||||
|
previewLargeOffset,
|
||||||
|
previewSmallOffset,
|
||||||
printParamsOffset,
|
printParamsOffset,
|
||||||
machineInfoOffset,
|
machineInfoOffset,
|
||||||
layerTableOffset
|
layerTableOffset
|
||||||
});
|
});
|
||||||
|
writer.seek(previewLargeOffset);
|
||||||
|
writePreviewRecord(writer, previews[0], previewLargeDataOffset, previewData[0]);
|
||||||
|
writer.seek(previewLargeDataOffset);
|
||||||
|
writer.writeBytes(previewData[0]);
|
||||||
|
writer.seek(previewSmallOffset);
|
||||||
|
writePreviewRecord(writer, previews[1], previewSmallDataOffset, previewData[1]);
|
||||||
|
writer.seek(previewSmallDataOffset);
|
||||||
|
writer.writeBytes(previewData[1]);
|
||||||
writer.seek(printParamsOffset);
|
writer.seek(printParamsOffset);
|
||||||
writePrintParams(writer, { process, volume });
|
writePrintParams(writer, { process, volume });
|
||||||
writer.seek(machineInfoOffset);
|
writer.seek(machineInfoOffset);
|
||||||
|
|
@ -193,6 +189,7 @@ function writeCTB(params) {
|
||||||
function writeHeader(writer, params) {
|
function writeHeader(writer, params) {
|
||||||
let {
|
let {
|
||||||
device, process, layers, seed, printTime,
|
device, process, layers, seed, printTime,
|
||||||
|
previewLargeOffset, previewSmallOffset,
|
||||||
printParamsOffset, machineInfoOffset, layerTableOffset
|
printParamsOffset, machineInfoOffset, layerTableOffset
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
|
|
@ -209,10 +206,10 @@ function writeHeader(writer, params) {
|
||||||
writer.writeU32(process.slaBaseLayers);
|
writer.writeU32(process.slaBaseLayers);
|
||||||
writer.writeU32(device.resolutionX);
|
writer.writeU32(device.resolutionX);
|
||||||
writer.writeU32(device.resolutionY);
|
writer.writeU32(device.resolutionY);
|
||||||
writer.writeU32(0);
|
writer.writeU32(previewLargeOffset);
|
||||||
writer.writeU32(layerTableOffset);
|
writer.writeU32(layerTableOffset);
|
||||||
writer.writeU32(layers.length);
|
writer.writeU32(layers.length);
|
||||||
writer.writeU32(0);
|
writer.writeU32(previewSmallOffset);
|
||||||
writer.writeU32(printTime);
|
writer.writeU32(printTime);
|
||||||
writer.writeU32(1);
|
writer.writeU32(1);
|
||||||
writer.writeU32(printParamsOffset);
|
writer.writeU32(printParamsOffset);
|
||||||
|
|
@ -225,6 +222,17 @@ function writeHeader(writer, params) {
|
||||||
writer.writeU32(MACHINE_INFO_SIZE);
|
writer.writeU32(MACHINE_INFO_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function writePreviewRecord(writer, preview, dataOffset, data) {
|
||||||
|
writer.writeU32(preview.width);
|
||||||
|
writer.writeU32(preview.height);
|
||||||
|
writer.writeU32(dataOffset);
|
||||||
|
writer.writeU32(data.length);
|
||||||
|
writer.writeU32(0);
|
||||||
|
writer.writeU32(0);
|
||||||
|
writer.writeU32(0);
|
||||||
|
writer.writeU32(0);
|
||||||
|
}
|
||||||
|
|
||||||
function writePrintParams(writer, params) {
|
function writePrintParams(writer, params) {
|
||||||
let { process, volume } = params;
|
let { process, volume } = params;
|
||||||
|
|
||||||
|
|
@ -316,9 +324,10 @@ function readHeader(view) {
|
||||||
resolutionX: view.getUint32(0x34, true),
|
resolutionX: view.getUint32(0x34, true),
|
||||||
resolutionY: view.getUint32(0x38, true),
|
resolutionY: view.getUint32(0x38, true),
|
||||||
previewOffset: view.getUint32(0x3c, true),
|
previewOffset: view.getUint32(0x3c, true),
|
||||||
|
previewLargeOffset: view.getUint32(0x3c, true),
|
||||||
layerTableOffset: view.getUint32(0x40, true),
|
layerTableOffset: view.getUint32(0x40, true),
|
||||||
layerCount: view.getUint32(0x44, true),
|
layerCount: view.getUint32(0x44, true),
|
||||||
previewOneLength: view.getUint32(0x48, true),
|
previewSmallOffset: view.getUint32(0x48, true),
|
||||||
printParamsOffset: view.getUint32(0x54, true),
|
printParamsOffset: view.getUint32(0x54, true),
|
||||||
printParamsLength: view.getUint32(0x58, true),
|
printParamsLength: view.getUint32(0x58, true),
|
||||||
antiAliasLevel: view.getUint32(0x5c, true),
|
antiAliasLevel: view.getUint32(0x5c, true),
|
||||||
|
|
@ -330,6 +339,27 @@ function readHeader(view) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readPreviews(view, header) {
|
||||||
|
return [header.previewLargeOffset, header.previewSmallOffset]
|
||||||
|
.filter(offset => offset > 0)
|
||||||
|
.map((offset, index) => readPreviewRecord(view, offset, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPreviewRecord(view, offset, index) {
|
||||||
|
return {
|
||||||
|
index,
|
||||||
|
offset,
|
||||||
|
width: view.getUint32(offset + 0, true),
|
||||||
|
height: view.getUint32(offset + 4, true),
|
||||||
|
imageOffset: view.getUint32(offset + 8, true),
|
||||||
|
imageLength: view.getUint32(offset + 12, true),
|
||||||
|
unknown16: view.getUint32(offset + 16, true),
|
||||||
|
unknown20: view.getUint32(offset + 20, true),
|
||||||
|
unknown24: view.getUint32(offset + 24, true),
|
||||||
|
unknown28: view.getUint32(offset + 28, true)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function readLayerTable(view, header) {
|
function readLayerTable(view, header) {
|
||||||
let layers = [];
|
let layers = [];
|
||||||
let offset = header.layerTableOffset;
|
let offset = header.layerTableOffset;
|
||||||
|
|
@ -500,14 +530,71 @@ function createRSLAManifest(ctb, layers) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderToCTBPixels(image, width, height) {
|
function createPreview(width, height) {
|
||||||
let data = new Uint8Array(width * height);
|
return {
|
||||||
for (let x=0; x<width; x++) {
|
width,
|
||||||
for (let y=0; y<height; y++) {
|
height,
|
||||||
data[y * width + x] = image[x * height + y] || 0;
|
image: new Uint8Array(width * height)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function accumulatePreview(preview, image, width, height) {
|
||||||
|
let { image: target } = preview;
|
||||||
|
let xscale = width / preview.width;
|
||||||
|
let yscale = height / preview.height;
|
||||||
|
|
||||||
|
for (let y=0; y<preview.height; y++) {
|
||||||
|
let sy = Math.min(height - 1, Math.floor(y * yscale));
|
||||||
|
for (let x=0; x<preview.width; x++) {
|
||||||
|
let sx = Math.min(width - 1, Math.floor(x * xscale));
|
||||||
|
if (image[sx * height + sy]) {
|
||||||
|
target[y * preview.width + x] = 255;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return data;
|
}
|
||||||
|
|
||||||
|
function encodePreview(preview) {
|
||||||
|
let output = [];
|
||||||
|
let data = preview.image;
|
||||||
|
let color = previewColor(data[0]);
|
||||||
|
let run = 1;
|
||||||
|
|
||||||
|
for (let i=1; i<data.length; i++) {
|
||||||
|
let next = previewColor(data[i]);
|
||||||
|
if (next === color && run < 0xfff) {
|
||||||
|
run++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
writePreviewRun(output, color, run);
|
||||||
|
color = next;
|
||||||
|
run = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
writePreviewRun(output, color, run);
|
||||||
|
return new Uint8Array(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writePreviewRun(output, color, length) {
|
||||||
|
if (length === 1) {
|
||||||
|
writePreviewU16(output, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writePreviewU16(output, color | 0x0020);
|
||||||
|
writePreviewU16(output, 0x3000 | (length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function previewColor(value) {
|
||||||
|
return value ? 0xffdf : 0x0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writePreviewU16(output, value) {
|
||||||
|
output.push(value & 0xff, (value >> 8) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderToCTBPixels(image, width, height) {
|
||||||
|
return imageToRowMajor(image, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeRLE(input) {
|
function encodeRLE(input) {
|
||||||
|
|
@ -675,10 +762,6 @@ function toDataView(input) {
|
||||||
: new DataView(input.buffer || input, input.byteOffset || 0, input.byteLength);
|
: new DataView(input.buffer || input, input.byteOffset || 0, input.byteLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
function round(value) {
|
|
||||||
return Number.parseFloat(Number(value || 0).toFixed(5));
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeSeed(print) {
|
function makeSeed(print) {
|
||||||
let { device, process } = print.settings;
|
let { device, process } = print.settings;
|
||||||
let hash = 0x811c9dc5;
|
let hash = 0x811c9dc5;
|
||||||
|
|
|
||||||
14
src/kiri/mode/sla/work/x_pw.js
Normal file
14
src/kiri/mode/sla/work/x_pw.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
|
const FAMILY = "Photon Workshop";
|
||||||
|
|
||||||
|
function encode(format) {
|
||||||
|
return Promise.reject(new Error([
|
||||||
|
`${FAMILY} format .${format} is registered but not implemented.`,
|
||||||
|
"Add a fixture sliced by Photon Workshop before enabling this output target."
|
||||||
|
].join(" ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PW = {
|
||||||
|
encode
|
||||||
|
};
|
||||||
|
|
@ -3,41 +3,18 @@
|
||||||
import { JSZip } from '../../../../ext/jszip-esm.js';
|
import { JSZip } from '../../../../ext/jszip-esm.js';
|
||||||
import { PNG } from '../../../../ext/pngjs.esm.js';
|
import { PNG } from '../../../../ext/pngjs.esm.js';
|
||||||
import { photon } from './x_photon.js';
|
import { photon } from './x_photon.js';
|
||||||
|
import { imageToRowMajor, renderRasterLayers, round } from './raster.js';
|
||||||
|
|
||||||
const FORMAT = "rsla";
|
const FORMAT = "rsla";
|
||||||
const MIME = "application/vnd.gridspace.rsla+zip";
|
const MIME = "application/vnd.gridspace.rsla+zip";
|
||||||
|
|
||||||
function encode(print, progress) {
|
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 layers = [];
|
||||||
let volume = 0;
|
|
||||||
let zip = new JSZip();
|
let zip = new JSZip();
|
||||||
|
|
||||||
for (let index=0; index<layermax; index++) {
|
let { ctx, volume } = renderRasterLayers(print, photon, params => {
|
||||||
let rendered = photon.renderLayerWasm({
|
let { index, rendered, ctx } = params;
|
||||||
index,
|
let { process, width, height } = ctx;
|
||||||
width,
|
|
||||||
height,
|
|
||||||
widgets,
|
|
||||||
scaleX,
|
|
||||||
scaleY,
|
|
||||||
masks: []
|
|
||||||
});
|
|
||||||
if (rendered.end) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = `layers/${index.toString().padStart(6, "0")}.png`;
|
let file = `layers/${index.toString().padStart(6, "0")}.png`;
|
||||||
let layer = {
|
let layer = {
|
||||||
|
|
@ -47,13 +24,11 @@ function encode(print, progress) {
|
||||||
file
|
file
|
||||||
};
|
};
|
||||||
|
|
||||||
volume += rendered.area * process.slaSlice;
|
|
||||||
layers.push(layer);
|
layers.push(layer);
|
||||||
zip.file(file, imageToPNG(rendered.image, width, height));
|
zip.file(file, imageToPNG(rendered.image, width, height));
|
||||||
|
}, progress ? (value) => progress(value * 0.85, "raster_gen") : null);
|
||||||
|
|
||||||
if (progress) progress((index / layermax) * 0.85, "raster_gen");
|
let { device, process } = ctx;
|
||||||
}
|
|
||||||
|
|
||||||
let manifest = createManifest({ device, process, layers, volume });
|
let manifest = createManifest({ device, process, layers, volume });
|
||||||
zip.file("manifest.json", JSON.stringify(manifest, null, 2));
|
zip.file("manifest.json", JSON.stringify(manifest, null, 2));
|
||||||
|
|
||||||
|
|
@ -122,12 +97,7 @@ function createManifest(params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function imageToPNG(image, width, height) {
|
function imageToPNG(image, width, height) {
|
||||||
let data = new Uint8Array(width * height);
|
let data = imageToRowMajor(image, width, height, value => value ? 255 : 0);
|
||||||
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 }, {
|
return PNG.sync.write({ width, height, data }, {
|
||||||
colorType: 0,
|
colorType: 0,
|
||||||
inputColorType: 0,
|
inputColorType: 0,
|
||||||
|
|
@ -135,10 +105,6 @@ function imageToPNG(image, width, height) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function round(value) {
|
|
||||||
return Number.parseFloat(Number(value || 0).toFixed(5));
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RSLA = {
|
export const RSLA = {
|
||||||
encode
|
encode
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue