complete basic export for cxdlp
This commit is contained in:
parent
ed9ddfd83e
commit
3158276984
7 changed files with 875 additions and 575 deletions
2
app.js
2
app.js
|
|
@ -309,6 +309,8 @@ const script = {
|
||||||
"mode/sla/driver",
|
"mode/sla/driver",
|
||||||
"mode/sla/slice",
|
"mode/sla/slice",
|
||||||
"mode/sla/export",
|
"mode/sla/export",
|
||||||
|
"mode/sla/x_halot",
|
||||||
|
"mode/sla/x_photon",
|
||||||
"mode/cam/driver",
|
"mode/cam/driver",
|
||||||
"mode/cam/ops",
|
"mode/cam/ops",
|
||||||
"mode/cam/tool",
|
"mode/cam/tool",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,41 @@
|
||||||
|
self.ArrayWriter = class ArrayWriter {
|
||||||
|
constructor() {
|
||||||
|
this.pos = 0;
|
||||||
|
this.array = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
seek(pos) {
|
||||||
|
let last = this.pos;
|
||||||
|
this.pos = pos;
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip(len) {
|
||||||
|
this.pos += len;
|
||||||
|
return this.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeU8(v) {
|
||||||
|
this.array[this.pos++] = v & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeU16(v) {
|
||||||
|
this.array[this.pos++] = (v >> 8) & 0xff;
|
||||||
|
this.array[this.pos++] = v & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeU32(v) {
|
||||||
|
this.array[this.pos++] = (v >> 24) & 0xff;
|
||||||
|
this.array[this.pos++] = (v >> 16) & 0xff;
|
||||||
|
this.array[this.pos++] = (v >> 8) & 0xff;
|
||||||
|
this.array[this.pos++] = v & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
toBuffer() {
|
||||||
|
return new Uint8ClampedArray(this.array).buffer;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
self.DataWriter = class DataWriter {
|
self.DataWriter = class DataWriter {
|
||||||
constructor(view, pos) {
|
constructor(view, pos) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
|
|
|
||||||
|
|
@ -395,7 +395,7 @@
|
||||||
pos.z = zpos;
|
pos.z = zpos;
|
||||||
if (peelGuard && bmax > peelGuard && blast < peelGuard) {
|
if (peelGuard && bmax > peelGuard && blast < peelGuard) {
|
||||||
peelGuard += 50;
|
peelGuard += 50;
|
||||||
append(`G0 Z${(blast + 55).round(decimals)} F200 ; peel guard`);
|
append(`G0 Z${(blast + 100).round(decimals)} F200 ; peel guard (100)`);
|
||||||
append(`G0 Z${blastz.round(decimals)} F200 ; unpeel`);
|
append(`G0 Z${blastz.round(decimals)} F200 ; unpeel`);
|
||||||
}
|
}
|
||||||
blast = bmax;
|
blast = bmax;
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,17 @@
|
||||||
download.innerText += " .photons";
|
download.innerText += " .photons";
|
||||||
download.onclick = () => { saveFile(API, file, ".photons") };
|
download.onclick = () => { saveFile(API, file, ".photons") };
|
||||||
break;
|
break;
|
||||||
|
case 'Creality.Halot.Sky':
|
||||||
|
default:
|
||||||
|
download.innerText += " .cxdlp";
|
||||||
|
download.onclick = () => { saveFile(API, file, ".cxdlp") };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let canvas = $('print-canvas');
|
let canvas = $('print-canvas');
|
||||||
|
// rotate 90 degrees for export view
|
||||||
|
canvas.width = height;
|
||||||
|
canvas.height = width;
|
||||||
let ctx = canvas.getContext('2d');
|
let ctx = canvas.getContext('2d');
|
||||||
let img = ctx.createImageData(height, width);
|
let img = ctx.createImageData(height, width);
|
||||||
let imgDV = new DataView(img.data.buffer);
|
let imgDV = new DataView(img.data.buffer);
|
||||||
|
|
|
||||||
|
|
@ -20,81 +20,112 @@
|
||||||
settings = print.settings,
|
settings = print.settings,
|
||||||
device = settings.device,
|
device = settings.device,
|
||||||
process = settings.process,
|
process = settings.process,
|
||||||
output = print.output,
|
|
||||||
layermax = 0,
|
|
||||||
width = device.resolutionX,
|
width = device.resolutionX,
|
||||||
height = device.resolutionY,
|
height = device.resolutionY,
|
||||||
width2 = width/2,
|
|
||||||
height2 = height/2,
|
|
||||||
scaleX = width / device.bedWidth,
|
scaleX = width / device.bedWidth,
|
||||||
scaleY = height / device.bedDepth,
|
scaleY = height / device.bedDepth,
|
||||||
|
alias = process.slaAntiAlias || 1,
|
||||||
mark = Date.now(),
|
mark = Date.now(),
|
||||||
layers = process.slaAntiAlias || 1,
|
layermax = 0;
|
||||||
masks = [],
|
|
||||||
images = [],
|
|
||||||
slices = [],
|
|
||||||
legacyMode = SLA.legacy || layers > 1,
|
|
||||||
part1 = legacyMode ? 0.25 : 0.85,
|
|
||||||
part2 = legacyMode ? 0.75 : 0.15;
|
|
||||||
|
|
||||||
let d = 8 / layers;
|
|
||||||
for (let i=0; i<layers; i++) {
|
|
||||||
masks.push((1 << (8 - i * d)) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// find max layer count
|
// find max layer count
|
||||||
widgets.forEach(widget => {
|
widgets.forEach(widget => {
|
||||||
layermax = Math.max(widget.slices.length);
|
layermax = Math.max(widget.slices.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
let render = legacyMode ? renderLayer : renderLayerWasm;
|
let isPhoton = false;
|
||||||
|
|
||||||
// generate layer bitmaps
|
|
||||||
// in wasm mode, rle layers generated here, too
|
|
||||||
for (let index=0; index < layermax; index++) {
|
|
||||||
let param = { index, width, height, widgets, scaleX, scaleY, masks };
|
|
||||||
let {image, layers, end} = render(param);
|
|
||||||
images.push(image);
|
|
||||||
slices.push(layers);
|
|
||||||
// transfer images to browser main
|
|
||||||
image = image.buffer;
|
|
||||||
online({
|
|
||||||
progress: (index / layermax) * part1,
|
|
||||||
message: "image_gen",
|
|
||||||
data: image
|
|
||||||
},[image]);
|
|
||||||
if (end) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let exp_func;
|
|
||||||
|
|
||||||
switch (device.deviceName) {
|
switch (device.deviceName) {
|
||||||
case 'Anycubic.Photon':
|
case 'Anycubic.Photon':
|
||||||
exp_func = generatePhoton;
|
|
||||||
break;
|
|
||||||
case 'Anycubic.Photon.S':
|
case 'Anycubic.Photon.S':
|
||||||
exp_func = generatePhotons;
|
isPhoton = true;
|
||||||
break;
|
|
||||||
case 'Creality.Halot.Sky':
|
|
||||||
exp_func = generateCXDLP;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = exp_func(print, {
|
if (isPhoton) {
|
||||||
width: width,
|
let legacyMode = SLA.legacy || alias > 1,
|
||||||
height: height,
|
part1 = legacyMode ? 0.25 : 0.85,
|
||||||
small: SLA.previewSmall.data,
|
part2 = (1 - part1),
|
||||||
large: SLA.previewLarge.data,
|
images = [],
|
||||||
lines: images,
|
slices = [];
|
||||||
slices: slices
|
|
||||||
}, (progress, message) => {
|
// generate layer bitmaps
|
||||||
online({progress: progress * part2 + part1, message});
|
// in wasm mode, rle layers generated here, too
|
||||||
});
|
let d = 8 / alias;
|
||||||
ondone({
|
let masks = [];
|
||||||
width: width,
|
for (let i=0; i<alias; i++) {
|
||||||
height: height,
|
masks.push((1 << (8 - i * d)) - 1);
|
||||||
file: file
|
}
|
||||||
},[file]);
|
|
||||||
|
let render = legacyMode ? photon.renderLayer : photon.renderLayerWasm;
|
||||||
|
|
||||||
|
for (let index=0; index < layermax; index++) {
|
||||||
|
let param = { index, width, height, widgets, scaleX, scaleY, masks };
|
||||||
|
let {image, layers, end} = render(param);
|
||||||
|
images.push(image);
|
||||||
|
slices.push(layers);
|
||||||
|
// transfer image memory to browser main
|
||||||
|
image = image.buffer;
|
||||||
|
online({
|
||||||
|
progress: (index / layermax) * part1,
|
||||||
|
message: "image_gen",
|
||||||
|
data: image
|
||||||
|
}, [image]);
|
||||||
|
if (end) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let exp_func = {
|
||||||
|
'Anycubic.Photon': photon.generatePhoton,
|
||||||
|
'Anycubic.Photon.S': photon.generatePhotons,
|
||||||
|
}[device.deviceName] || photon.generatePhoton;
|
||||||
|
|
||||||
|
let file = exp_func(print, {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
small: SLA.previewSmall.data,
|
||||||
|
large: SLA.previewLarge.data,
|
||||||
|
lines: images,
|
||||||
|
slices: slices
|
||||||
|
}, (progress, message) => {
|
||||||
|
online({progress: progress * part2 + part1, message});
|
||||||
|
});
|
||||||
|
ondone({
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
file: file
|
||||||
|
},[file]);
|
||||||
|
} else {
|
||||||
|
let part1 = 0.25;
|
||||||
|
let part2 = 1 - part1;
|
||||||
|
let images = [];
|
||||||
|
let slices = [];
|
||||||
|
|
||||||
|
for (let index=0; index < layermax; index++) {
|
||||||
|
let param = { index, width, height, widgets, scaleX, scaleY };
|
||||||
|
let {image, lines} = CXDLP.render(param);
|
||||||
|
images.push(image);
|
||||||
|
slices.push(lines);
|
||||||
|
// transfer image memory to browser main
|
||||||
|
// it *should* be sampled to save memory
|
||||||
|
image = image.buffer;
|
||||||
|
online({
|
||||||
|
progress: (index / layermax) * part1,
|
||||||
|
message: "image_gen",
|
||||||
|
data: image
|
||||||
|
}, [image]);
|
||||||
|
// bail on an empty layer
|
||||||
|
if (lines.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = CXDLP.export({settings, width, height, slices});
|
||||||
|
ondone({
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
file: file
|
||||||
|
}, [file]);
|
||||||
|
}
|
||||||
|
|
||||||
console.log('print.export', Date.now() - mark);
|
console.log('print.export', Date.now() - mark);
|
||||||
};
|
};
|
||||||
|
|
@ -103,524 +134,4 @@
|
||||||
console.log({generateCXDLP: print, conf, progress});
|
console.log({generateCXDLP: print, conf, progress});
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePhoton(print, conf, progress) {
|
|
||||||
let printset = print.settings,
|
|
||||||
process = printset.process,
|
|
||||||
device = printset.device,
|
|
||||||
width = conf.width,
|
|
||||||
height = conf.height,
|
|
||||||
layerCount = conf.lines.length,
|
|
||||||
layerBytes = width * height,
|
|
||||||
small = conf.small,
|
|
||||||
large = conf.large,
|
|
||||||
slices = conf.slices,
|
|
||||||
subcount = process.slaAntiAlias || 1,
|
|
||||||
masks = [],
|
|
||||||
coded;
|
|
||||||
|
|
||||||
if (SLA.legacy || subcount > 1) {
|
|
||||||
let d = 8 / subcount;
|
|
||||||
for (let i=0; i<subcount; i++) {
|
|
||||||
masks.push((1 << (8 - i * d)) - 1);
|
|
||||||
}
|
|
||||||
let ccl = 0;
|
|
||||||
let tcl = conf.lines.length * subcount;
|
|
||||||
let converted = conf.lines.map((line, index) => {
|
|
||||||
let count = line.length;
|
|
||||||
let lineDV = new DataView(line.buffer);
|
|
||||||
let bits = new Uint8Array(line.length);
|
|
||||||
let bitsDV = new DataView(bits.buffer);
|
|
||||||
let subs = [{ data: bits, view: bitsDV }];
|
|
||||||
for (let sl=1; sl<subcount; sl++) {
|
|
||||||
bits = bits.slice();
|
|
||||||
bitsDV = new DataView(bits.buffer);
|
|
||||||
subs.push({ data: bits, view: bitsDV });
|
|
||||||
}
|
|
||||||
// use R from RGB since that was painted on the canvas
|
|
||||||
for (let s=0; s<subcount; s++) {
|
|
||||||
let view = subs[s].view;
|
|
||||||
let mask = masks[s];
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
let dv = lineDV.getUint8(i);
|
|
||||||
view.setUint8(i, (dv / subcount) & mask ? 1 : 0);
|
|
||||||
}
|
|
||||||
progress((ccl++/tcl) * 0.4, `layer_convert`);
|
|
||||||
}
|
|
||||||
return { subs };
|
|
||||||
});
|
|
||||||
|
|
||||||
coded = encodeLayers(converted, "photon", (pro => {
|
|
||||||
progress(pro * 0.4 + 0.4, "layer_encode");
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
let codedlen = slices.reduce((t,l) => {
|
|
||||||
return t + l.reduce((t,a) => {
|
|
||||||
return t + a.length
|
|
||||||
}, 0);
|
|
||||||
}, 0);
|
|
||||||
coded = {
|
|
||||||
layers: slices.map(slice => { return { sublayers: slice }}),
|
|
||||||
length: codedlen
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let codelen = coded.layers.length;
|
|
||||||
let buflen = 3000 + coded.length + (codelen * subcount * 28) + small.byteLength + large.byteLength;
|
|
||||||
let filebuf = new ArrayBuffer(buflen);
|
|
||||||
let filedat = new self.DataWriter(new DataView(filebuf));
|
|
||||||
let printtime = (process.slaBaseLayers * process.slaBaseOn) +
|
|
||||||
(coded.layers.length - process.slaBaseLayers) * process.slaLayerOn;
|
|
||||||
|
|
||||||
filedat.writeU32(0x1900fd12); // header
|
|
||||||
filedat.writeU32(2,true); // version
|
|
||||||
filedat.writeF32(68.04, true); // bed x
|
|
||||||
filedat.writeF32(120.96, true); // bed y
|
|
||||||
filedat.writeF32(150.0, true); // bed z
|
|
||||||
filedat.skip(12); // padding
|
|
||||||
filedat.writeF32(process.slaSlice, true); // layer height
|
|
||||||
filedat.writeF32(process.slaLayerOn, true); // default lamp on
|
|
||||||
filedat.writeF32(process.slaBaseOn, true); // base lamp on
|
|
||||||
filedat.writeF32(process.slaLayerOff, true); // lamp off
|
|
||||||
filedat.writeU32(process.slaBaseLayers, true); // base layers
|
|
||||||
filedat.writeU32(1440, true); // device x
|
|
||||||
filedat.writeU32(2560, true); // device y
|
|
||||||
let hirez = filedat.skip(4); // hirez preview address filled pater
|
|
||||||
let layerpos = filedat.skip(4); // layer data address filled later
|
|
||||||
filedat.writeU32(codelen, true);
|
|
||||||
let lorez = filedat.skip(4); // hirez preview address filled later
|
|
||||||
filedat.writeU32(printtime, true); // print time seconds
|
|
||||||
filedat.writeU32(1, true); // projection type (1=lcd, 0=cast)
|
|
||||||
let proppos = filedat.skip(4); // print properties address filled later
|
|
||||||
let proplen = filedat.skip(4); // print properties length filled later
|
|
||||||
filedat.writeU32(subcount, true); // AA level (sub layers)
|
|
||||||
filedat.writeU16(0x00ff, true); // light pwm (TODO);
|
|
||||||
filedat.writeU16(0x00ff, true); // light pwm bottom (TODO);
|
|
||||||
|
|
||||||
let propstart = filedat.pos;
|
|
||||||
filedat.view.setUint32(proppos, filedat.pos, true);
|
|
||||||
// write print properties
|
|
||||||
filedat.writeF32(process.slaBasePeelDist, true);
|
|
||||||
filedat.writeF32(process.slaBasePeelLiftRate * 60 , true);
|
|
||||||
filedat.writeF32(process.slaPeelDist, true);
|
|
||||||
filedat.writeF32(process.slaPeelLiftRate * 60 , true);
|
|
||||||
filedat.writeF32(process.slaPeelDropRate * 60, true);
|
|
||||||
filedat.writeF32(0, true); // volume of used
|
|
||||||
filedat.writeF32(0, true); // weight of used
|
|
||||||
filedat.writeF32(0, true); // cost of used
|
|
||||||
filedat.writeF32(0, true); // bottom off delay time
|
|
||||||
filedat.writeF32(0, true); // light off delay time
|
|
||||||
filedat.writeU32(process.slaBaseLayers, true);
|
|
||||||
filedat.writeF32(0, true); // p1 ?
|
|
||||||
filedat.writeF32(0, true); // p2 ?
|
|
||||||
filedat.writeF32(0, true); // p3 ?
|
|
||||||
filedat.writeF32(0, true); // p4 ?
|
|
||||||
filedat.view.setUint32(proplen, filedat.pos - propstart, true);
|
|
||||||
|
|
||||||
filedat.view.setUint32(layerpos, filedat.pos, true);
|
|
||||||
// write layer headers
|
|
||||||
let layers = coded.layers;
|
|
||||||
let layerat = [];
|
|
||||||
|
|
||||||
for (let sc=0; sc<subcount; sc++)
|
|
||||||
for (let l=0; l<layers.length; l++) {
|
|
||||||
let layer = layers[l].sublayers[sc];
|
|
||||||
filedat.writeF32(process.slaFirstOffset + process.slaSlice * l, true); // layer height
|
|
||||||
filedat.writeF32(l < process.slaBaseLayers ? process.slaBaseOn : process.slaLayerOn, true);
|
|
||||||
filedat.writeF32(l < process.slaBaseLayers ? process.slaBaseOff : process.slaLayerOff, true);
|
|
||||||
layerat.push(layer.repos = filedat.skip(4)); // rewrite later
|
|
||||||
filedat.writeU32(layer.length, true);
|
|
||||||
filedat.skip(16); // padding
|
|
||||||
}
|
|
||||||
|
|
||||||
// write layer data
|
|
||||||
let clo = 0;
|
|
||||||
let tlo = layers.length * subcount;
|
|
||||||
for (let sc=0; sc<subcount; sc++)
|
|
||||||
for (let l=0; l<layers.length; l++) {
|
|
||||||
let layer = layers[l].sublayers[sc];
|
|
||||||
filedat.view.setUint32(layer.repos, filedat.pos, true);
|
|
||||||
for (let j=0; j<layer.length; j++) {
|
|
||||||
filedat.writeU8(layer[j], false);
|
|
||||||
}
|
|
||||||
progress(((clo++/tlo) * 0.1) + 0.9, "layer_write");
|
|
||||||
}
|
|
||||||
|
|
||||||
filedat.view.setUint32(hirez, filedat.pos, true);
|
|
||||||
writePhotonImage({
|
|
||||||
width: 400,
|
|
||||||
height: 300,
|
|
||||||
data: conf.large
|
|
||||||
}, filedat);
|
|
||||||
|
|
||||||
filedat.view.setUint32(lorez, filedat.pos, true);
|
|
||||||
writePhotonImage({
|
|
||||||
width: 200,
|
|
||||||
height: 125,
|
|
||||||
data: conf.small
|
|
||||||
}, filedat);
|
|
||||||
|
|
||||||
return filebuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generatePhotons(print, conf, progress) {
|
|
||||||
let printset = print.settings,
|
|
||||||
process = printset.process,
|
|
||||||
device = printset.device,
|
|
||||||
width = conf.width,
|
|
||||||
height = conf.height,
|
|
||||||
slices = conf.slices,
|
|
||||||
layerCount = conf.lines.length,
|
|
||||||
layerBytes = width * height,
|
|
||||||
coded;
|
|
||||||
|
|
||||||
if (SLA.legacy) {
|
|
||||||
let converted = conf.lines.map((line, index) => {
|
|
||||||
let count = line.length / 4;
|
|
||||||
let bits = new Uint8Array(line.length / 4);
|
|
||||||
let bitsDV = new DataView(bits.buffer);
|
|
||||||
let lineDV = new DataView(line.buffer);
|
|
||||||
// reduce RGB to R = 0||1
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
// defeat anti-aliasing for the moment
|
|
||||||
bitsDV.setUint8(i, lineDV.getUint8(i * 4) > 0 ? 1 : 0);
|
|
||||||
}
|
|
||||||
progress(index / conf.lines.length);
|
|
||||||
return { subs: [{
|
|
||||||
exposureTime: process.slaLayerOn,
|
|
||||||
data: bits
|
|
||||||
}] };
|
|
||||||
});
|
|
||||||
coded = encodeLayers(converted, "photons");
|
|
||||||
} else {
|
|
||||||
let codedlen = slices.reduce((t,l) => {
|
|
||||||
return t + l.reduce((t,a) => {
|
|
||||||
return t + a.length
|
|
||||||
}, 0);
|
|
||||||
}, 0);
|
|
||||||
coded = {
|
|
||||||
layers: slices.map(slice => { return { sublayers: slice }}),
|
|
||||||
length: codedlen
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let filebuf = new ArrayBuffer(75366 + coded.length + 28 * layerCount);
|
|
||||||
let filedat = new DataView(filebuf);
|
|
||||||
let filePos = 0;
|
|
||||||
|
|
||||||
filedat.setUint32 (0, 2, false);
|
|
||||||
filedat.setUint32 (4, 3227560, false);
|
|
||||||
filedat.setUint32 (8, 824633720, false);
|
|
||||||
filedat.setUint16 (12, 10, false);
|
|
||||||
filedat.setFloat64(14, process.slaSlice, false);
|
|
||||||
filedat.setFloat64(22, process.slaLayerOn, false);
|
|
||||||
filedat.setFloat64(30, process.slaLayerOff, false);
|
|
||||||
filedat.setFloat64(38, process.slaBaseOn, false);
|
|
||||||
filedat.setUint32 (46, process.slaBaseLayers, false);
|
|
||||||
filedat.setFloat64(50, process.slaPeelDist, false);
|
|
||||||
filedat.setFloat64(58, process.slaPeelLift, false);
|
|
||||||
filedat.setFloat64(66, process.slaPeelDrop, false);
|
|
||||||
filedat.setFloat64(74, 69420, false);
|
|
||||||
filedat.setUint32 (82, 224, false);
|
|
||||||
filedat.setUint32 (86, 42, false);
|
|
||||||
filedat.setUint32 (90, 168, false);
|
|
||||||
filedat.setUint32 (94, 10, false);
|
|
||||||
filedat.setUint32 (75362, layerCount, false);
|
|
||||||
|
|
||||||
filePos = 75366;
|
|
||||||
for (let i = 0; i < layerCount; i++) {
|
|
||||||
let layer = coded.layers[i],
|
|
||||||
sublayer = layer.sublayers[0],
|
|
||||||
numbytes = sublayer.length;
|
|
||||||
|
|
||||||
filedat.setUint32 (filePos + 0, 69420, false);
|
|
||||||
filedat.setFloat64(filePos + 4, 0);
|
|
||||||
filedat.setUint32 (filePos + 12, height, false);
|
|
||||||
filedat.setUint32 (filePos + 16, width, false);
|
|
||||||
filedat.setUint32 (filePos + 20, numbytes * 8 + 32, false);
|
|
||||||
filedat.setUint32 (filePos + 24, 2684702720, false);
|
|
||||||
filePos += 28;
|
|
||||||
for (let j = 0; j < numbytes; j++) {
|
|
||||||
filedat.setUint8(filePos + j, sublayer[j], false);
|
|
||||||
}
|
|
||||||
filePos += numbytes;
|
|
||||||
progress((i / layerCount) / 2 + 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
return filebuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeLayers(input, type, progress) {
|
|
||||||
let layers = [], length = 0, total = 0, count = 0;
|
|
||||||
input.forEach(layer => {
|
|
||||||
layer.subs.forEach(sub => total++);
|
|
||||||
});
|
|
||||||
for (let index = 0; index < input.length; index++) {
|
|
||||||
let subs = input[index].subs,
|
|
||||||
sublayers = [],
|
|
||||||
sublength = 0;
|
|
||||||
for (let subindex = 0; subindex < subs.length; subindex++) {
|
|
||||||
let data = subs[subindex].data;
|
|
||||||
let encoded = rleEncode(data, type);
|
|
||||||
sublength += encoded.length;
|
|
||||||
sublayers.push(encoded);
|
|
||||||
if (progress) progress(count++/total);
|
|
||||||
if (type == "photons") break;
|
|
||||||
}
|
|
||||||
length += sublength;
|
|
||||||
layers.push({
|
|
||||||
sublength,
|
|
||||||
sublayers
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return { length, layers };
|
|
||||||
}
|
|
||||||
|
|
||||||
function rleEncode(data, type) {
|
|
||||||
let maxlen = (type === 'photons') ? 128 : 125,
|
|
||||||
color = data[0],
|
|
||||||
runlen = 1,
|
|
||||||
output = [];
|
|
||||||
for (let index = 1; index < data.length; index++) {
|
|
||||||
let newColor = data[index];
|
|
||||||
if (newColor !== color) {
|
|
||||||
output.push(rleByte(color, runlen, type));
|
|
||||||
color = newColor;
|
|
||||||
runlen = 1;
|
|
||||||
} else {
|
|
||||||
if (runlen === maxlen) {
|
|
||||||
output.push(rleByte(color, runlen, type));
|
|
||||||
runlen = 1;
|
|
||||||
} else {
|
|
||||||
runlen++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (runlen > 0) {
|
|
||||||
output.push(rleByte(color, runlen, type));
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
function rleByte(color, length, type) {
|
|
||||||
switch (type) {
|
|
||||||
case 'photon':
|
|
||||||
return (length & 0x7f) | ((color << 7) & 0x80);
|
|
||||||
case 'photons':
|
|
||||||
length--;
|
|
||||||
return (length & 1 ? 128 : 0) |
|
|
||||||
(length & 2 ? 64 : 0) |
|
|
||||||
(length & 4 ? 32 : 0) |
|
|
||||||
(length & 8 ? 16 : 0) |
|
|
||||||
(length & 16 ? 8 : 0) |
|
|
||||||
(length & 32 ? 4 : 0) |
|
|
||||||
(length & 64 ? 2 : 0) | color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function rleDecode(data, type) {
|
|
||||||
let bytes = [];
|
|
||||||
if (type === 'photon') {
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
let val = data[i],
|
|
||||||
color = val >> 7,
|
|
||||||
count = val & 0x7f;
|
|
||||||
for (let j = 0; j < count; j++) {
|
|
||||||
bytes.push(color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
let val = data[i],
|
|
||||||
color = val & 1,
|
|
||||||
count =
|
|
||||||
((val & 128 ? 1 : 0) |
|
|
||||||
(val & 64 ? 2 : 0) |
|
|
||||||
(val & 32 ? 4 : 0) |
|
|
||||||
(val & 16 ? 8 : 0) |
|
|
||||||
(val & 8 ? 16 : 0) |
|
|
||||||
(val & 4 ? 32 : 0) |
|
|
||||||
(val & 2 ? 64 : 0)) + 1;
|
|
||||||
for (let j = 0; j < count; j++) {
|
|
||||||
bytes.push(color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write out a thumbnail image
|
|
||||||
function writePhotonImage(preview, writer) {
|
|
||||||
let data = new Uint8Array(preview.data), len = data.byteLength;
|
|
||||||
writer.writeU32(preview.width, true);
|
|
||||||
writer.writeU32(preview.height, true);
|
|
||||||
let hpos = writer.skip(4);
|
|
||||||
writer.writeU32(len/2, true);
|
|
||||||
writer.view.setUint32(hpos, writer.pos, true);
|
|
||||||
let pos = 0;
|
|
||||||
while (pos < len) {
|
|
||||||
let r = data[pos++],
|
|
||||||
g = data[pos++],
|
|
||||||
b = data[pos++],
|
|
||||||
a = data[pos++],
|
|
||||||
v = (((r/4)&0x1f) << 11) |
|
|
||||||
(((g/4)&0x1f) << 6) |
|
|
||||||
(((b/4)&0x1f) << 0) ;
|
|
||||||
writer.writeU16(v, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for unbound workers
|
|
||||||
// if (self.WASM) {
|
|
||||||
// let {exports} = wasmInstance;
|
|
||||||
// let heap = new Uint8Array(exports.memory.buffer);
|
|
||||||
// self.wasm = {
|
|
||||||
// heap,
|
|
||||||
// memory: exports.memory,
|
|
||||||
// // heap: wasmMemory,
|
|
||||||
// // memory: memoryBytes,
|
|
||||||
// render: exports.render,
|
|
||||||
// rle_encode: exports.rle_encode
|
|
||||||
// };
|
|
||||||
// } else
|
|
||||||
|
|
||||||
// new WebAssembly rasterizer
|
|
||||||
function renderLayerWasm(params) {
|
|
||||||
let { width, height, index, widgets, scaleX, scaleY, masks } = params;
|
|
||||||
let width2 = width / 2, height2 = height / 2;
|
|
||||||
let array = [];
|
|
||||||
let count = 0;
|
|
||||||
|
|
||||||
function scaleMovePoly(poly) {
|
|
||||||
let points = poly.points;
|
|
||||||
poly._bounds = undefined;
|
|
||||||
for (let i=0, il=points.length; i<il; i++) {
|
|
||||||
let p = points[i];
|
|
||||||
p.y = height - (p.y * scaleY + height2);
|
|
||||||
p.x = p.x * scaleX + width2;
|
|
||||||
}
|
|
||||||
if (poly.inner) {
|
|
||||||
for (let i=0, ia=poly.inner, il=poly.inner.length; i<il; i++) {
|
|
||||||
scaleMovePoly(ia[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// serialize poly into wasm heap memory
|
|
||||||
function writePoly(writer, poly) {
|
|
||||||
let pos = writer.skip(2);
|
|
||||||
let inner = poly.inner;
|
|
||||||
writer.writeU16(inner ? inner.length : 0, true);
|
|
||||||
let points = poly.points;
|
|
||||||
let bounds = poly.bounds;
|
|
||||||
writer.writeU16(points.length, true);
|
|
||||||
writer.writeU16(bounds.minx, true);
|
|
||||||
writer.writeU16(bounds.maxx, true);
|
|
||||||
writer.writeU16(bounds.miny, true);
|
|
||||||
writer.writeU16(bounds.maxy, true);
|
|
||||||
for (let j=0, jl=points.length; j<jl; j++) {
|
|
||||||
let point = points[j];
|
|
||||||
writer.writeF32(point.x, true);
|
|
||||||
writer.writeF32(point.y, true);
|
|
||||||
}
|
|
||||||
if (inner && inner.length) {
|
|
||||||
for (let i=0, il=inner.length; i<il; i++) {
|
|
||||||
writePoly(writer, inner[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// write total struct length at struct head
|
|
||||||
writer.view.setUint16(pos, writer.pos - pos, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
widgets.forEach(widget => {
|
|
||||||
let slice = widget.slices[index];
|
|
||||||
if (slice) {
|
|
||||||
if (slice.synth) count++;
|
|
||||||
let polys = slice.unioned;
|
|
||||||
if (!polys) polys = slice.tops.map(t => t.poly);
|
|
||||||
if (slice.supports) polys.appendAll(slice.supports);
|
|
||||||
array.appendAll(polys.map(poly => {
|
|
||||||
return poly.clone(true).move(widget.track.pos);
|
|
||||||
}));
|
|
||||||
count += polys.length;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let wasm = SLA.wasm;
|
|
||||||
let imagelen = width * height;
|
|
||||||
let writer = new self.DataWriter(new DataView(wasm.memory.buffer), imagelen);
|
|
||||||
writer.writeU16(width, true);
|
|
||||||
writer.writeU16(height, true);
|
|
||||||
writer.writeU16(array.length, true);
|
|
||||||
|
|
||||||
// scale and move all polys to fit in rendered platform coordinates
|
|
||||||
for (let i=0, il=array.length; i<il; i++) {
|
|
||||||
let poly = array[i];
|
|
||||||
scaleMovePoly(poly);
|
|
||||||
writePoly(writer, poly);
|
|
||||||
}
|
|
||||||
wasm.render(0, imagelen, 0);
|
|
||||||
let image = wasm.heap.slice(0, imagelen), layers = [];
|
|
||||||
// one rle encoded bitstream for each mash (anti-alias sublayer)
|
|
||||||
for (let l=0; l<masks.length; l++) {
|
|
||||||
// while the image is still in wasm heap memory, rle encode it
|
|
||||||
let rlelen = wasm.rle_encode(0, 0, imagelen, masks[l], imagelen, 0);
|
|
||||||
layers.push(wasm.heap.slice(imagelen, imagelen + rlelen));
|
|
||||||
}
|
|
||||||
|
|
||||||
return { image, layers, end: count === 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
// legacy JS-only rasterizer uses OffscreenCanvas
|
|
||||||
function renderLayer(params) {
|
|
||||||
let {width, height, index, widgets, scaleX, scaleY} = params;
|
|
||||||
let layer = new OffscreenCanvas(height,width);
|
|
||||||
let opt = { scaleX, scaleY, width, height, width2: width/2, height2: height/2 };
|
|
||||||
let ctx = layer.getContext('2d');
|
|
||||||
ctx.fillStyle = 'rgb(200, 0, 0)';
|
|
||||||
let count = 0;
|
|
||||||
widgets.forEach(widget => {
|
|
||||||
let slice = widget.slices[index];
|
|
||||||
if (slice) {
|
|
||||||
// prevent premature exit on empty synth slice
|
|
||||||
if (slice.synth) count++;
|
|
||||||
let polys = slice.unioned;
|
|
||||||
if (!polys) polys = slice.tops.map(t => t.poly);
|
|
||||||
if (slice.supports) polys.appendAll(slice.supports);
|
|
||||||
polys.forEach(poly => {
|
|
||||||
poly.move(widget.track.pos);
|
|
||||||
ctx.beginPath();
|
|
||||||
polyout(poly.setClockwise(), ctx, opt);
|
|
||||||
if (poly.inner) {
|
|
||||||
poly.inner.forEach(inner => {
|
|
||||||
polyout(inner.setCounterClockwise(), ctx, opt);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ctx.fill();
|
|
||||||
count++;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// console.log({no_slice_at: index})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let data = ctx.getImageData(0,0,height,width).data;
|
|
||||||
// reduce RGBA to R
|
|
||||||
let red = new Uint8ClampedArray(data.length / 4);
|
|
||||||
for (let i=0; i<red.length; i++) {
|
|
||||||
red[i] = data[i*4];
|
|
||||||
}
|
|
||||||
return { image: red, end: count === 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
function polyout(poly, ctx, opt) {
|
|
||||||
let { scaleX, scaleY, width, height, width2, height2 } = opt;
|
|
||||||
poly.forEachPoint((p,i) => {
|
|
||||||
if (i === 0) {
|
|
||||||
ctx.moveTo(height - (p.y * scaleY + height2), p.x * scaleX + width2);
|
|
||||||
} else {
|
|
||||||
ctx.lineTo(height - (p.y * scaleY + height2), p.x * scaleX + width2);
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
ctx.closePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
const default_values = {
|
const default_values = {
|
||||||
magic1: 'CXSW3DV2',
|
magic1: 'CXSW3DV2',
|
||||||
magic2: '',
|
magic2: 'CXSW3DV2',
|
||||||
model: 'CL-89',
|
model: 'CL-89',
|
||||||
version: 1,
|
version: 1,
|
||||||
layer_count: 0,
|
layer_count: 0,
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
lift_dist: read.readU16(),
|
lift_dist: read.readU16(),
|
||||||
lift_speed: read.readU16(),
|
lift_speed: read.readU16(),
|
||||||
down_speed: read.readU16(),
|
down_speed: read.readU16(),
|
||||||
light_pwm: read.readU16(),
|
base_light_pwm: read.readU16(),
|
||||||
light_pwm: read.readU16()
|
light_pwm: read.readU16()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -96,7 +96,7 @@
|
||||||
for (let i=0; i<meta.layer_count; i++) {
|
for (let i=0; i<meta.layer_count; i++) {
|
||||||
let size = read.readU32();
|
let size = read.readU32();
|
||||||
if (size !== layers[i].length) {
|
if (size !== layers[i].length) {
|
||||||
throw `layer length mismatch: ${size} != ${layer_sizes[i]}}`;
|
throw `layer length mismatch: ${size} != ${layers[i].length} @ i=${i}`;
|
||||||
}
|
}
|
||||||
let lines = read.readU32();
|
let lines = read.readU32();
|
||||||
layers[i].lines = lines;
|
layers[i].lines = lines;
|
||||||
|
|
@ -154,10 +154,216 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
write() {
|
write() {
|
||||||
// TODO
|
let output = new ArrayWriter();
|
||||||
|
|
||||||
|
output.write_string = function write_string(str, dbl = false) {
|
||||||
|
let len = str.length;
|
||||||
|
output.writeU32(dbl ? len * 2 : len + 1);
|
||||||
|
let pos = 0;
|
||||||
|
while (pos < len) {
|
||||||
|
if (dbl) {
|
||||||
|
output.writeU8(0);
|
||||||
|
}
|
||||||
|
output.writeU8(str.charCodeAt(pos++));
|
||||||
|
}
|
||||||
|
if (!dbl) {
|
||||||
|
output.writeU8(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let layers = this.layers;
|
||||||
|
this.layer_count = layers.length;
|
||||||
|
output.write_string(this.magic1);
|
||||||
|
output.writeU16(this.version);
|
||||||
|
output.write_string(this.model);
|
||||||
|
output.writeU16(this.layer_count);
|
||||||
|
output.writeU16(this.res_x);
|
||||||
|
output.writeU16(this.res_y);
|
||||||
|
output.writeU32(this.height);
|
||||||
|
output.skip(60);
|
||||||
|
output.skip(26912); // thumb
|
||||||
|
output.writeU16(data_term);
|
||||||
|
output.skip(168200); // preview1
|
||||||
|
output.writeU16(data_term);
|
||||||
|
output.skip(168200); // preview1
|
||||||
|
output.writeU16(data_term);
|
||||||
|
output.write_string(this.dim_x, true);
|
||||||
|
output.write_string(this.dim_y, true);
|
||||||
|
output.write_string(this.layer, true);
|
||||||
|
output.writeU16(this.light_on);
|
||||||
|
output.writeU16(this.light_off);
|
||||||
|
output.writeU16(this.base_light_on);
|
||||||
|
output.writeU16(this.base_layers);
|
||||||
|
output.writeU16(this.base_lift_dist);
|
||||||
|
output.writeU16(this.base_lift_speed);
|
||||||
|
output.writeU16(this.lift_dist);
|
||||||
|
output.writeU16(this.lift_speed);
|
||||||
|
output.writeU16(this.down_speed);
|
||||||
|
output.writeU16(this.base_light_pwm);
|
||||||
|
output.writeU16(this.light_pwm);
|
||||||
|
|
||||||
|
// write placeholder layer length to capture position
|
||||||
|
for (let layer of layers) {
|
||||||
|
layer.l1 = output.pos;
|
||||||
|
output.writeU32(0);
|
||||||
|
}
|
||||||
|
output.writeU16(data_term);
|
||||||
|
|
||||||
|
// write out encoded layers
|
||||||
|
for (let layer of layers) {
|
||||||
|
layer.l2 = output.pos;
|
||||||
|
// placeholder to be written post
|
||||||
|
output.writeU32(0);
|
||||||
|
output.writeU32(layer.lines.length);
|
||||||
|
let start = output.pos;
|
||||||
|
for (let line of layer.lines) {
|
||||||
|
let b1 = (line.y_start >> 5);
|
||||||
|
let b2 = ((line.y_start << 3) | (line.y_end >> 10)) & 0xff;
|
||||||
|
let b3 = (line.y_end >> 2) & 0xff;
|
||||||
|
let b4 = ((line.y_end << 6) | (line.x_end >> 8)) & 0xff;
|
||||||
|
let b5 = (line.x_end) & 0xff;
|
||||||
|
output.writeU8(b1);
|
||||||
|
output.writeU8(b2);
|
||||||
|
output.writeU8(b3);
|
||||||
|
output.writeU8(b4);
|
||||||
|
output.writeU8(b5);
|
||||||
|
output.writeU8(line.color);
|
||||||
|
}
|
||||||
|
layer.length = output.pos - start;
|
||||||
|
output.writeU16(data_term);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.write_string(this.magic2);
|
||||||
|
let ckpos = output.pos;
|
||||||
|
|
||||||
|
// retrace and write layer lengths
|
||||||
|
for (let layer of layers) {
|
||||||
|
output.seek(layer.l1);
|
||||||
|
output.writeU32(layer.length);
|
||||||
|
output.seek(layer.l2);
|
||||||
|
output.writeU32(layer.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.seek(ckpos);
|
||||||
|
// compute xor checksum
|
||||||
|
let cksum = 0;
|
||||||
|
let array = output.array;
|
||||||
|
for (let i=0; i<array.length; i++) {
|
||||||
|
cksum = cksum ^ (array[i] || 0);
|
||||||
|
}
|
||||||
|
output.writeU32(cksum);
|
||||||
|
|
||||||
|
return output.toBuffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CXDLP.export = function(params) {
|
||||||
|
let { settings, width, height, slices } = params;
|
||||||
|
|
||||||
|
let cxdlp = new CXDLP();
|
||||||
|
cxdlp.layers = slices.map(a => {return { lines: a }});
|
||||||
|
|
||||||
|
return cxdlp.write();
|
||||||
|
};
|
||||||
|
|
||||||
|
CXDLP.render = function(params) {
|
||||||
|
let { width, height, index, widgets, scaleX, scaleY } = params;
|
||||||
|
let width2 = width / 2, height2 = height / 2;
|
||||||
|
let array = [];
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function scaleMovePoly(poly) {
|
||||||
|
let points = poly.points;
|
||||||
|
poly._bounds = undefined;
|
||||||
|
for (let i=0, il=points.length; i<il; i++) {
|
||||||
|
let p = points[i];
|
||||||
|
p.y = height - (p.y * scaleY + height2);
|
||||||
|
p.x = p.x * scaleX + width2;
|
||||||
|
}
|
||||||
|
if (poly.inner) {
|
||||||
|
for (let i=0, ia=poly.inner, il=poly.inner.length; i<il; i++) {
|
||||||
|
scaleMovePoly(ia[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialize poly into wasm heap memory
|
||||||
|
function writePoly(writer, poly) {
|
||||||
|
let pos = writer.skip(2);
|
||||||
|
let inner = poly.inner;
|
||||||
|
writer.writeU16(inner ? inner.length : 0, true);
|
||||||
|
let points = poly.points;
|
||||||
|
let bounds = poly.bounds;
|
||||||
|
writer.writeU16(points.length, true);
|
||||||
|
writer.writeU16(bounds.minx, true);
|
||||||
|
writer.writeU16(bounds.maxx, true);
|
||||||
|
writer.writeU16(bounds.miny, true);
|
||||||
|
writer.writeU16(bounds.maxy, true);
|
||||||
|
for (let j=0, jl=points.length; j<jl; j++) {
|
||||||
|
let point = points[j];
|
||||||
|
writer.writeF32(point.x, true);
|
||||||
|
writer.writeF32(point.y, true);
|
||||||
|
}
|
||||||
|
if (inner && inner.length) {
|
||||||
|
for (let i=0, il=inner.length; i<il; i++) {
|
||||||
|
writePoly(writer, inner[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// write total struct length at struct head
|
||||||
|
writer.view.setUint16(pos, writer.pos - pos, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
widgets.forEach(widget => {
|
||||||
|
let slice = widget.slices[index];
|
||||||
|
if (slice) {
|
||||||
|
if (slice.synth) count++;
|
||||||
|
let polys = slice.unioned;
|
||||||
|
if (!polys) polys = slice.tops.map(t => t.poly);
|
||||||
|
if (slice.supports) polys.appendAll(slice.supports);
|
||||||
|
array.appendAll(polys.map(poly => {
|
||||||
|
return poly.clone(true).move(widget.track.pos);
|
||||||
|
}));
|
||||||
|
count += polys.length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let wasm = kiri.driver.SLA.wasm;
|
||||||
|
let imagelen = width * height;
|
||||||
|
let writer = new self.DataWriter(new DataView(wasm.memory.buffer), imagelen);
|
||||||
|
writer.writeU16(width, true);
|
||||||
|
writer.writeU16(height, true);
|
||||||
|
writer.writeU16(array.length, true);
|
||||||
|
|
||||||
|
// scale and move all polys to fit in rendered platform coordinates
|
||||||
|
for (let i=0, il=array.length; i<il; i++) {
|
||||||
|
let poly = array[i];
|
||||||
|
scaleMovePoly(poly);
|
||||||
|
writePoly(writer, poly);
|
||||||
|
}
|
||||||
|
wasm.render(0, imagelen, 0);
|
||||||
|
let image = wasm.heap.slice(0, imagelen);
|
||||||
|
let lines = [];
|
||||||
|
for (let x=0; x<width; x++) {
|
||||||
|
let y_start = 0;
|
||||||
|
let lastv = 0;
|
||||||
|
for (let y=0; y<height; y++) {
|
||||||
|
let v = image[x * height + y];
|
||||||
|
if (v !== lastv) {
|
||||||
|
if (lastv) {
|
||||||
|
// emit any non-zero sequence
|
||||||
|
lines.push({y_start, y_end: y, x_end: x, color: lastv});
|
||||||
|
}
|
||||||
|
if (v) {
|
||||||
|
// start a new sequence
|
||||||
|
y_start = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastv = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { image, lines };
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.navigator && this.process && this.process.env) {
|
if (!this.navigator && this.process && this.process.env) {
|
||||||
let fs = require('fs');
|
let fs = require('fs');
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
@ -174,6 +380,6 @@
|
||||||
lines1: cxdlp.get_layer_lines(1)
|
lines1: cxdlp.get_layer_lines(1)
|
||||||
});
|
});
|
||||||
} else if (this.navigator) {
|
} else if (this.navigator) {
|
||||||
window.CXDLP = CXDLP;
|
this.CXDLP = CXDLP;
|
||||||
}
|
}
|
||||||
}());
|
}());
|
||||||
|
|
|
||||||
535
src/mode/sla/x_photon.js
Normal file
535
src/mode/sla/x_photon.js
Normal file
|
|
@ -0,0 +1,535 @@
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
let KIRI = self.kiri,
|
||||||
|
BASE = self.base,
|
||||||
|
UTIL = BASE.util,
|
||||||
|
SLA = KIRI.driver.SLA;
|
||||||
|
|
||||||
|
function generatePhoton(print, conf, progress) {
|
||||||
|
let printset = print.settings,
|
||||||
|
process = printset.process,
|
||||||
|
device = printset.device,
|
||||||
|
width = conf.width,
|
||||||
|
height = conf.height,
|
||||||
|
layerCount = conf.lines.length,
|
||||||
|
layerBytes = width * height,
|
||||||
|
small = conf.small,
|
||||||
|
large = conf.large,
|
||||||
|
slices = conf.slices,
|
||||||
|
subcount = process.slaAntiAlias || 1,
|
||||||
|
masks = [],
|
||||||
|
coded;
|
||||||
|
|
||||||
|
if (SLA.legacy || subcount > 1) {
|
||||||
|
let d = 8 / subcount;
|
||||||
|
for (let i=0; i<subcount; i++) {
|
||||||
|
masks.push((1 << (8 - i * d)) - 1);
|
||||||
|
}
|
||||||
|
let ccl = 0;
|
||||||
|
let tcl = conf.lines.length * subcount;
|
||||||
|
let converted = conf.lines.map((line, index) => {
|
||||||
|
let count = line.length;
|
||||||
|
let lineDV = new DataView(line.buffer);
|
||||||
|
let bits = new Uint8Array(line.length);
|
||||||
|
let bitsDV = new DataView(bits.buffer);
|
||||||
|
let subs = [{ data: bits, view: bitsDV }];
|
||||||
|
for (let sl=1; sl<subcount; sl++) {
|
||||||
|
bits = bits.slice();
|
||||||
|
bitsDV = new DataView(bits.buffer);
|
||||||
|
subs.push({ data: bits, view: bitsDV });
|
||||||
|
}
|
||||||
|
// use R from RGB since that was painted on the canvas
|
||||||
|
for (let s=0; s<subcount; s++) {
|
||||||
|
let view = subs[s].view;
|
||||||
|
let mask = masks[s];
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
let dv = lineDV.getUint8(i);
|
||||||
|
view.setUint8(i, (dv / subcount) & mask ? 1 : 0);
|
||||||
|
}
|
||||||
|
progress((ccl++/tcl) * 0.4, `layer_convert`);
|
||||||
|
}
|
||||||
|
return { subs };
|
||||||
|
});
|
||||||
|
|
||||||
|
coded = encodeLayers(converted, "photon", (pro => {
|
||||||
|
progress(pro * 0.4 + 0.4, "layer_encode");
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
let codedlen = slices.reduce((t,l) => {
|
||||||
|
return t + l.reduce((t,a) => {
|
||||||
|
return t + a.length
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
coded = {
|
||||||
|
layers: slices.map(slice => { return { sublayers: slice }}),
|
||||||
|
length: codedlen
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let codelen = coded.layers.length;
|
||||||
|
let buflen = 3000 + coded.length + (codelen * subcount * 28) + small.byteLength + large.byteLength;
|
||||||
|
let filebuf = new ArrayBuffer(buflen);
|
||||||
|
let filedat = new self.DataWriter(new DataView(filebuf));
|
||||||
|
let printtime = (process.slaBaseLayers * process.slaBaseOn) +
|
||||||
|
(coded.layers.length - process.slaBaseLayers) * process.slaLayerOn;
|
||||||
|
|
||||||
|
filedat.writeU32(0x1900fd12); // header
|
||||||
|
filedat.writeU32(2,true); // version
|
||||||
|
filedat.writeF32(68.04, true); // bed x
|
||||||
|
filedat.writeF32(120.96, true); // bed y
|
||||||
|
filedat.writeF32(150.0, true); // bed z
|
||||||
|
filedat.skip(12); // padding
|
||||||
|
filedat.writeF32(process.slaSlice, true); // layer height
|
||||||
|
filedat.writeF32(process.slaLayerOn, true); // default lamp on
|
||||||
|
filedat.writeF32(process.slaBaseOn, true); // base lamp on
|
||||||
|
filedat.writeF32(process.slaLayerOff, true); // lamp off
|
||||||
|
filedat.writeU32(process.slaBaseLayers, true); // base layers
|
||||||
|
filedat.writeU32(1440, true); // device x
|
||||||
|
filedat.writeU32(2560, true); // device y
|
||||||
|
let hirez = filedat.skip(4); // hirez preview address filled pater
|
||||||
|
let layerpos = filedat.skip(4); // layer data address filled later
|
||||||
|
filedat.writeU32(codelen, true);
|
||||||
|
let lorez = filedat.skip(4); // hirez preview address filled later
|
||||||
|
filedat.writeU32(printtime, true); // print time seconds
|
||||||
|
filedat.writeU32(1, true); // projection type (1=lcd, 0=cast)
|
||||||
|
let proppos = filedat.skip(4); // print properties address filled later
|
||||||
|
let proplen = filedat.skip(4); // print properties length filled later
|
||||||
|
filedat.writeU32(subcount, true); // AA level (sub layers)
|
||||||
|
filedat.writeU16(0x00ff, true); // light pwm (TODO);
|
||||||
|
filedat.writeU16(0x00ff, true); // light pwm bottom (TODO);
|
||||||
|
|
||||||
|
let propstart = filedat.pos;
|
||||||
|
filedat.view.setUint32(proppos, filedat.pos, true);
|
||||||
|
// write print properties
|
||||||
|
filedat.writeF32(process.slaBasePeelDist, true);
|
||||||
|
filedat.writeF32(process.slaBasePeelLiftRate * 60 , true);
|
||||||
|
filedat.writeF32(process.slaPeelDist, true);
|
||||||
|
filedat.writeF32(process.slaPeelLiftRate * 60 , true);
|
||||||
|
filedat.writeF32(process.slaPeelDropRate * 60, true);
|
||||||
|
filedat.writeF32(0, true); // volume of used
|
||||||
|
filedat.writeF32(0, true); // weight of used
|
||||||
|
filedat.writeF32(0, true); // cost of used
|
||||||
|
filedat.writeF32(0, true); // bottom off delay time
|
||||||
|
filedat.writeF32(0, true); // light off delay time
|
||||||
|
filedat.writeU32(process.slaBaseLayers, true);
|
||||||
|
filedat.writeF32(0, true); // p1 ?
|
||||||
|
filedat.writeF32(0, true); // p2 ?
|
||||||
|
filedat.writeF32(0, true); // p3 ?
|
||||||
|
filedat.writeF32(0, true); // p4 ?
|
||||||
|
filedat.view.setUint32(proplen, filedat.pos - propstart, true);
|
||||||
|
|
||||||
|
filedat.view.setUint32(layerpos, filedat.pos, true);
|
||||||
|
// write layer headers
|
||||||
|
let layers = coded.layers;
|
||||||
|
let layerat = [];
|
||||||
|
|
||||||
|
for (let sc=0; sc<subcount; sc++)
|
||||||
|
for (let l=0; l<layers.length; l++) {
|
||||||
|
let layer = layers[l].sublayers[sc];
|
||||||
|
filedat.writeF32(process.slaFirstOffset + process.slaSlice * l, true); // layer height
|
||||||
|
filedat.writeF32(l < process.slaBaseLayers ? process.slaBaseOn : process.slaLayerOn, true);
|
||||||
|
filedat.writeF32(l < process.slaBaseLayers ? process.slaBaseOff : process.slaLayerOff, true);
|
||||||
|
layerat.push(layer.repos = filedat.skip(4)); // rewrite later
|
||||||
|
filedat.writeU32(layer.length, true);
|
||||||
|
filedat.skip(16); // padding
|
||||||
|
}
|
||||||
|
|
||||||
|
// write layer data
|
||||||
|
let clo = 0;
|
||||||
|
let tlo = layers.length * subcount;
|
||||||
|
for (let sc=0; sc<subcount; sc++)
|
||||||
|
for (let l=0; l<layers.length; l++) {
|
||||||
|
let layer = layers[l].sublayers[sc];
|
||||||
|
filedat.view.setUint32(layer.repos, filedat.pos, true);
|
||||||
|
for (let j=0; j<layer.length; j++) {
|
||||||
|
filedat.writeU8(layer[j], false);
|
||||||
|
}
|
||||||
|
progress(((clo++/tlo) * 0.1) + 0.9, "layer_write");
|
||||||
|
}
|
||||||
|
|
||||||
|
filedat.view.setUint32(hirez, filedat.pos, true);
|
||||||
|
writePhotonImage({
|
||||||
|
width: 400,
|
||||||
|
height: 300,
|
||||||
|
data: conf.large
|
||||||
|
}, filedat);
|
||||||
|
|
||||||
|
filedat.view.setUint32(lorez, filedat.pos, true);
|
||||||
|
writePhotonImage({
|
||||||
|
width: 200,
|
||||||
|
height: 125,
|
||||||
|
data: conf.small
|
||||||
|
}, filedat);
|
||||||
|
|
||||||
|
return filebuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generatePhotons(print, conf, progress) {
|
||||||
|
let printset = print.settings,
|
||||||
|
process = printset.process,
|
||||||
|
device = printset.device,
|
||||||
|
width = conf.width,
|
||||||
|
height = conf.height,
|
||||||
|
slices = conf.slices,
|
||||||
|
layerCount = conf.lines.length,
|
||||||
|
layerBytes = width * height,
|
||||||
|
coded;
|
||||||
|
|
||||||
|
if (SLA.legacy) {
|
||||||
|
let converted = conf.lines.map((line, index) => {
|
||||||
|
let count = line.length / 4;
|
||||||
|
let bits = new Uint8Array(line.length / 4);
|
||||||
|
let bitsDV = new DataView(bits.buffer);
|
||||||
|
let lineDV = new DataView(line.buffer);
|
||||||
|
// reduce RGB to R = 0||1
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
// defeat anti-aliasing for the moment
|
||||||
|
bitsDV.setUint8(i, lineDV.getUint8(i * 4) > 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
progress(index / conf.lines.length);
|
||||||
|
return { subs: [{
|
||||||
|
exposureTime: process.slaLayerOn,
|
||||||
|
data: bits
|
||||||
|
}] };
|
||||||
|
});
|
||||||
|
coded = encodeLayers(converted, "photons");
|
||||||
|
} else {
|
||||||
|
let codedlen = slices.reduce((t,l) => {
|
||||||
|
return t + l.reduce((t,a) => {
|
||||||
|
return t + a.length
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
coded = {
|
||||||
|
layers: slices.map(slice => { return { sublayers: slice }}),
|
||||||
|
length: codedlen
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let filebuf = new ArrayBuffer(75366 + coded.length + 28 * layerCount);
|
||||||
|
let filedat = new DataView(filebuf);
|
||||||
|
let filePos = 0;
|
||||||
|
|
||||||
|
filedat.setUint32 (0, 2, false);
|
||||||
|
filedat.setUint32 (4, 3227560, false);
|
||||||
|
filedat.setUint32 (8, 824633720, false);
|
||||||
|
filedat.setUint16 (12, 10, false);
|
||||||
|
filedat.setFloat64(14, process.slaSlice, false);
|
||||||
|
filedat.setFloat64(22, process.slaLayerOn, false);
|
||||||
|
filedat.setFloat64(30, process.slaLayerOff, false);
|
||||||
|
filedat.setFloat64(38, process.slaBaseOn, false);
|
||||||
|
filedat.setUint32 (46, process.slaBaseLayers, false);
|
||||||
|
filedat.setFloat64(50, process.slaPeelDist, false);
|
||||||
|
filedat.setFloat64(58, process.slaPeelLift, false);
|
||||||
|
filedat.setFloat64(66, process.slaPeelDrop, false);
|
||||||
|
filedat.setFloat64(74, 69420, false);
|
||||||
|
filedat.setUint32 (82, 224, false);
|
||||||
|
filedat.setUint32 (86, 42, false);
|
||||||
|
filedat.setUint32 (90, 168, false);
|
||||||
|
filedat.setUint32 (94, 10, false);
|
||||||
|
filedat.setUint32 (75362, layerCount, false);
|
||||||
|
|
||||||
|
filePos = 75366;
|
||||||
|
for (let i = 0; i < layerCount; i++) {
|
||||||
|
let layer = coded.layers[i],
|
||||||
|
sublayer = layer.sublayers[0],
|
||||||
|
numbytes = sublayer.length;
|
||||||
|
|
||||||
|
filedat.setUint32 (filePos + 0, 69420, false);
|
||||||
|
filedat.setFloat64(filePos + 4, 0);
|
||||||
|
filedat.setUint32 (filePos + 12, height, false);
|
||||||
|
filedat.setUint32 (filePos + 16, width, false);
|
||||||
|
filedat.setUint32 (filePos + 20, numbytes * 8 + 32, false);
|
||||||
|
filedat.setUint32 (filePos + 24, 2684702720, false);
|
||||||
|
filePos += 28;
|
||||||
|
for (let j = 0; j < numbytes; j++) {
|
||||||
|
filedat.setUint8(filePos + j, sublayer[j], false);
|
||||||
|
}
|
||||||
|
filePos += numbytes;
|
||||||
|
progress((i / layerCount) / 2 + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filebuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeLayers(input, type, progress) {
|
||||||
|
let layers = [], length = 0, total = 0, count = 0;
|
||||||
|
input.forEach(layer => {
|
||||||
|
layer.subs.forEach(sub => total++);
|
||||||
|
});
|
||||||
|
for (let index = 0; index < input.length; index++) {
|
||||||
|
let subs = input[index].subs,
|
||||||
|
sublayers = [],
|
||||||
|
sublength = 0;
|
||||||
|
for (let subindex = 0; subindex < subs.length; subindex++) {
|
||||||
|
let data = subs[subindex].data;
|
||||||
|
let encoded = rleEncode(data, type);
|
||||||
|
sublength += encoded.length;
|
||||||
|
sublayers.push(encoded);
|
||||||
|
if (progress) progress(count++/total);
|
||||||
|
if (type == "photons") break;
|
||||||
|
}
|
||||||
|
length += sublength;
|
||||||
|
layers.push({
|
||||||
|
sublength,
|
||||||
|
sublayers
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { length, layers };
|
||||||
|
}
|
||||||
|
|
||||||
|
function rleEncode(data, type) {
|
||||||
|
let maxlen = (type === 'photons') ? 128 : 125,
|
||||||
|
color = data[0],
|
||||||
|
runlen = 1,
|
||||||
|
output = [];
|
||||||
|
for (let index = 1; index < data.length; index++) {
|
||||||
|
let newColor = data[index];
|
||||||
|
if (newColor !== color) {
|
||||||
|
output.push(rleByte(color, runlen, type));
|
||||||
|
color = newColor;
|
||||||
|
runlen = 1;
|
||||||
|
} else {
|
||||||
|
if (runlen === maxlen) {
|
||||||
|
output.push(rleByte(color, runlen, type));
|
||||||
|
runlen = 1;
|
||||||
|
} else {
|
||||||
|
runlen++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (runlen > 0) {
|
||||||
|
output.push(rleByte(color, runlen, type));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rleByte(color, length, type) {
|
||||||
|
switch (type) {
|
||||||
|
case 'photon':
|
||||||
|
return (length & 0x7f) | ((color << 7) & 0x80);
|
||||||
|
case 'photons':
|
||||||
|
length--;
|
||||||
|
return (length & 1 ? 128 : 0) |
|
||||||
|
(length & 2 ? 64 : 0) |
|
||||||
|
(length & 4 ? 32 : 0) |
|
||||||
|
(length & 8 ? 16 : 0) |
|
||||||
|
(length & 16 ? 8 : 0) |
|
||||||
|
(length & 32 ? 4 : 0) |
|
||||||
|
(length & 64 ? 2 : 0) | color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rleDecode(data, type) {
|
||||||
|
let bytes = [];
|
||||||
|
if (type === 'photon') {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
let val = data[i],
|
||||||
|
color = val >> 7,
|
||||||
|
count = val & 0x7f;
|
||||||
|
for (let j = 0; j < count; j++) {
|
||||||
|
bytes.push(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
let val = data[i],
|
||||||
|
color = val & 1,
|
||||||
|
count =
|
||||||
|
((val & 128 ? 1 : 0) |
|
||||||
|
(val & 64 ? 2 : 0) |
|
||||||
|
(val & 32 ? 4 : 0) |
|
||||||
|
(val & 16 ? 8 : 0) |
|
||||||
|
(val & 8 ? 16 : 0) |
|
||||||
|
(val & 4 ? 32 : 0) |
|
||||||
|
(val & 2 ? 64 : 0)) + 1;
|
||||||
|
for (let j = 0; j < count; j++) {
|
||||||
|
bytes.push(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write out a thumbnail image
|
||||||
|
function writePhotonImage(preview, writer) {
|
||||||
|
let data = new Uint8Array(preview.data), len = data.byteLength;
|
||||||
|
writer.writeU32(preview.width, true);
|
||||||
|
writer.writeU32(preview.height, true);
|
||||||
|
let hpos = writer.skip(4);
|
||||||
|
writer.writeU32(len/2, true);
|
||||||
|
writer.view.setUint32(hpos, writer.pos, true);
|
||||||
|
let pos = 0;
|
||||||
|
while (pos < len) {
|
||||||
|
let r = data[pos++],
|
||||||
|
g = data[pos++],
|
||||||
|
b = data[pos++],
|
||||||
|
a = data[pos++],
|
||||||
|
v = (((r/4)&0x1f) << 11) |
|
||||||
|
(((g/4)&0x1f) << 6) |
|
||||||
|
(((b/4)&0x1f) << 0) ;
|
||||||
|
writer.writeU16(v, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for unbound workers
|
||||||
|
// if (self.WASM) {
|
||||||
|
// let {exports} = wasmInstance;
|
||||||
|
// let heap = new Uint8Array(exports.memory.buffer);
|
||||||
|
// self.wasm = {
|
||||||
|
// heap,
|
||||||
|
// memory: exports.memory,
|
||||||
|
// // heap: wasmMemory,
|
||||||
|
// // memory: memoryBytes,
|
||||||
|
// render: exports.render,
|
||||||
|
// rle_encode: exports.rle_encode
|
||||||
|
// };
|
||||||
|
// } else
|
||||||
|
|
||||||
|
// new WebAssembly rasterizer
|
||||||
|
function renderLayerWasm(params) {
|
||||||
|
let { width, height, index, widgets, scaleX, scaleY, masks } = params;
|
||||||
|
let width2 = width / 2, height2 = height / 2;
|
||||||
|
let array = [];
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function scaleMovePoly(poly) {
|
||||||
|
let points = poly.points;
|
||||||
|
poly._bounds = undefined;
|
||||||
|
for (let i=0, il=points.length; i<il; i++) {
|
||||||
|
let p = points[i];
|
||||||
|
p.y = height - (p.y * scaleY + height2);
|
||||||
|
p.x = p.x * scaleX + width2;
|
||||||
|
}
|
||||||
|
if (poly.inner) {
|
||||||
|
for (let i=0, ia=poly.inner, il=poly.inner.length; i<il; i++) {
|
||||||
|
scaleMovePoly(ia[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialize poly into wasm heap memory
|
||||||
|
function writePoly(writer, poly) {
|
||||||
|
let pos = writer.skip(2);
|
||||||
|
let inner = poly.inner;
|
||||||
|
writer.writeU16(inner ? inner.length : 0, true);
|
||||||
|
let points = poly.points;
|
||||||
|
let bounds = poly.bounds;
|
||||||
|
writer.writeU16(points.length, true);
|
||||||
|
writer.writeU16(bounds.minx, true);
|
||||||
|
writer.writeU16(bounds.maxx, true);
|
||||||
|
writer.writeU16(bounds.miny, true);
|
||||||
|
writer.writeU16(bounds.maxy, true);
|
||||||
|
for (let j=0, jl=points.length; j<jl; j++) {
|
||||||
|
let point = points[j];
|
||||||
|
writer.writeF32(point.x, true);
|
||||||
|
writer.writeF32(point.y, true);
|
||||||
|
}
|
||||||
|
if (inner && inner.length) {
|
||||||
|
for (let i=0, il=inner.length; i<il; i++) {
|
||||||
|
writePoly(writer, inner[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// write total struct length at struct head
|
||||||
|
writer.view.setUint16(pos, writer.pos - pos, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
widgets.forEach(widget => {
|
||||||
|
let slice = widget.slices[index];
|
||||||
|
if (slice) {
|
||||||
|
if (slice.synth) count++;
|
||||||
|
let polys = slice.unioned;
|
||||||
|
if (!polys) polys = slice.tops.map(t => t.poly);
|
||||||
|
if (slice.supports) polys.appendAll(slice.supports);
|
||||||
|
array.appendAll(polys.map(poly => {
|
||||||
|
return poly.clone(true).move(widget.track.pos);
|
||||||
|
}));
|
||||||
|
count += polys.length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let wasm = SLA.wasm;
|
||||||
|
let imagelen = width * height;
|
||||||
|
let writer = new self.DataWriter(new DataView(wasm.memory.buffer), imagelen);
|
||||||
|
writer.writeU16(width, true);
|
||||||
|
writer.writeU16(height, true);
|
||||||
|
writer.writeU16(array.length, true);
|
||||||
|
|
||||||
|
// scale and move all polys to fit in rendered platform coordinates
|
||||||
|
for (let i=0, il=array.length; i<il; i++) {
|
||||||
|
let poly = array[i];
|
||||||
|
scaleMovePoly(poly);
|
||||||
|
writePoly(writer, poly);
|
||||||
|
}
|
||||||
|
wasm.render(0, imagelen, 0);
|
||||||
|
let image = wasm.heap.slice(0, imagelen), layers = [];
|
||||||
|
// one rle encoded bitstream for each mash (anti-alias sublayer)
|
||||||
|
for (let l=0; l<masks.length; l++) {
|
||||||
|
// while the image is still in wasm heap memory, rle encode it
|
||||||
|
let rlelen = wasm.rle_encode(0, 0, imagelen, masks[l], imagelen, 0);
|
||||||
|
layers.push(wasm.heap.slice(imagelen, imagelen + rlelen));
|
||||||
|
}
|
||||||
|
|
||||||
|
return { image, layers, end: count === 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// legacy JS-only rasterizer uses OffscreenCanvas
|
||||||
|
function renderLayer(params) {
|
||||||
|
let {width, height, index, widgets, scaleX, scaleY} = params;
|
||||||
|
let layer = new OffscreenCanvas(height,width);
|
||||||
|
let opt = { scaleX, scaleY, width, height, width2: width/2, height2: height/2 };
|
||||||
|
let ctx = layer.getContext('2d');
|
||||||
|
ctx.fillStyle = 'rgb(200, 0, 0)';
|
||||||
|
let count = 0;
|
||||||
|
widgets.forEach(widget => {
|
||||||
|
let slice = widget.slices[index];
|
||||||
|
if (slice) {
|
||||||
|
// prevent premature exit on empty synth slice
|
||||||
|
if (slice.synth) count++;
|
||||||
|
let polys = slice.unioned;
|
||||||
|
if (!polys) polys = slice.tops.map(t => t.poly);
|
||||||
|
if (slice.supports) polys.appendAll(slice.supports);
|
||||||
|
polys.forEach(poly => {
|
||||||
|
poly.move(widget.track.pos);
|
||||||
|
ctx.beginPath();
|
||||||
|
polyout(poly.setClockwise(), ctx, opt);
|
||||||
|
if (poly.inner) {
|
||||||
|
poly.inner.forEach(inner => {
|
||||||
|
polyout(inner.setCounterClockwise(), ctx, opt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ctx.fill();
|
||||||
|
count++;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// console.log({no_slice_at: index})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let data = ctx.getImageData(0,0,height,width).data;
|
||||||
|
// reduce RGBA to R
|
||||||
|
let red = new Uint8ClampedArray(data.length / 4);
|
||||||
|
for (let i=0; i<red.length; i++) {
|
||||||
|
red[i] = data[i*4];
|
||||||
|
}
|
||||||
|
return { image: red, end: count === 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
function polyout(poly, ctx, opt) {
|
||||||
|
let { scaleX, scaleY, width, height, width2, height2 } = opt;
|
||||||
|
poly.forEachPoint((p,i) => {
|
||||||
|
if (i === 0) {
|
||||||
|
ctx.moveTo(height - (p.y * scaleY + height2), p.x * scaleX + width2);
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(height - (p.y * scaleY + height2), p.x * scaleX + width2);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
ctx.closePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.photon = {
|
||||||
|
generatePhoton,
|
||||||
|
generatePhotons,
|
||||||
|
renderLayer,
|
||||||
|
renderLayerWasm
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
||||||
Loading…
Reference in a new issue