UNPKG

@ankhzet/goo

Version:

Elegoo .goo file format reader/writer

151 lines (150 loc) 6.25 kB
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(); } }