@ankhzet/goo
Version:
Elegoo .goo file format reader/writer
151 lines (150 loc) • 6.25 kB
JavaScript
import { formatDate, rgb565Buffer } from './utils.js';
import { rleEncode } from './rle/index.js';
import { GOO_DELIMITER, GOO_MAGIC, GOO_TAIL, GOO_VERSION } from './magics.js';
import { loadPreview, loadSlice } from './loaders.js';
import { CRC8 } from './CRC8.js';
const GENERATOR = {
description: 'AnkhZetGoo',
version: '0.0.2',
};
export class GooWriter {
constructor(writer) {
this.writer = writer;
}
async write(goo, consumer) {
let offset = 0;
const consume = async (buffer) => {
offset += buffer.length;
return consumer(buffer);
};
for await (const buffer of this.writeData(goo)) {
await consume((buffer instanceof Buffer)
? buffer
: buffer(offset));
}
return offset;
}
async *writeData(goo) {
yield* this.writer.string(GOO_VERSION, 4);
yield* this.writer.binary(GOO_MAGIC);
yield* this.writeHeader(goo.header);
for (const layer of goo.layers) {
yield* this.writeLayer(layer, goo.header.printer);
}
yield* this.writer.u24(GOO_TAIL);
yield* this.writer.binary(GOO_MAGIC);
yield* this.writer.flush();
}
async *writeHeader(header) {
const generator = header.generator || GENERATOR;
yield* this.writer.string(generator.description, 32);
yield* this.writer.string(generator.version, 24);
yield* this.writer.string(formatDate(header.date), 24);
yield* this.writer.string(header.printer.name, 32);
yield* this.writer.string(header.printer.type, 32);
yield* this.writer.string(header.printer.resinProfile, 32);
yield* this.writer.u16(header.printer.antialiasing);
yield* this.writer.u16(header.printer.gray);
yield* this.writer.u16(header.printer.blur);
for (const preview of header.previews) {
yield* this.writePreview(preview);
}
yield* this.writer.flush();
yield* this.writer.u32(header.layers);
yield* this.writer.u16(header.printer.resolution.x);
yield* this.writer.u16(header.printer.resolution.y);
yield* this.writer.bool(header.printer.mirror.x);
yield* this.writer.bool(header.printer.mirror.y);
yield* this.writer.f32(header.printer.platform.x);
yield* this.writer.f32(header.printer.platform.y);
yield* this.writer.f32(header.printer.platform.z);
yield* this.writer.flush();
yield* this.writeLayerConfig(header.layerConfig);
yield* this.writer.flush();
yield* this.writeSummary(header.summary);
yield* this.mark(7);
yield* this.writer.bool(header.printer.grayscale);
yield* this.writer.u16(header.layerConfig.transitionLayers);
yield* this.writer.flush();
}
*mark(offset) {
yield* this.writer.flush();
yield (pos) => Buffer.from(Uint32Array.of(pos + offset).buffer).reverse();
}
*writeLayerConfig(config) {
yield* this.writer.f32(config.thickness);
yield* this.writer.f32(config.commonExposure);
yield* this.writer.bool(config.exposureDelay);
yield* this.writer.f32(config.turnOffTime);
yield* this.writeGlobalConfig(config.timings, (i) => this.writeMotionTimes(i));
yield* this.writer.f32(config.bottomExposure);
yield* this.writer.u32(config.bottomLayers);
yield* this.writeMotions(config.motions, (motion) => (this.writeLiftRetract(motion, (lr) => (this.writeGlobalConfig(lr, (c) => this.writeMotionConfig(c))))));
yield* this.writer.u16(config.pwm.bottom);
yield* this.writer.u16(config.pwm.common);
yield* this.writer.bool(config.advance);
}
*writeSummary(summary) {
yield* this.writer.u32(summary.time);
yield* this.writer.f32(summary.volume);
yield* this.writer.f32(summary.weight);
yield* this.writer.f32(summary.price);
yield* this.writer.string(summary.currency, 8);
}
*writeGlobalConfig(config, map) {
yield* map(config.bottom);
yield* map(config.common);
}
*writeMotions(config, map) {
yield* map(config.first);
yield* map(config.second);
}
*writeLiftRetract(config, map) {
yield* map(config.lift);
yield* map(config.retract);
}
*writeMotionTimes(times) {
yield* this.writer.f32(times.before.lift);
yield* this.writer.f32(times.after.lift);
yield* this.writer.f32(times.after.retract);
}
*writeMotionConfig(config) {
yield* this.writer.f32(config.distance);
yield* this.writer.f32(config.speed);
}
async *writePreview(preview) {
yield* this.writer.flush();
const { buffer, channels } = (typeof preview.input === 'string'
? await loadPreview(preview.input, preview.dimensions)
: preview.input);
yield rgb565Buffer(buffer, channels);
yield* this.writeDelimiter();
}
*writeDelimiter() {
yield* this.writer.u16(GOO_DELIMITER);
}
async *writeLayer(layer, printer) {
const slice = (typeof layer.slice === 'string'
? await loadSlice(layer.slice, layer.transform, printer)
: layer.slice);
const buffer = rleEncode(slice.buffer, slice.channels);
yield* this.writeLayerDefinition(layer.definition);
yield* this.writer.u32(buffer.length + 2);
yield* this.writer.u8(0x55);
yield* this.writer.flush();
yield buffer;
yield* this.writer.u8((new CRC8()).checksum(buffer));
yield* this.writeDelimiter();
}
*writeLayerDefinition(definition) {
yield* this.writer.u16(definition.pause.mode);
yield* this.writer.f32(definition.pause.z);
yield* this.writer.f32(definition.z);
yield* this.writer.f32(definition.exposure);
yield* this.writer.f32(definition.offTime);
yield* this.writeMotionTimes(definition.times);
yield* this.writeLiftRetract(definition.motions, (m) => (this.writeMotions(m, (lr) => (this.writeMotionConfig(lr)))));
yield* this.writer.u16(definition.pwm);
yield* this.writeDelimiter();
}
}