mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 11:13:55 +00:00
* craft-gcode: bare output for line-by-line senders, and Grbl replies
Writer::bare writes the same moves as Writer::new with every line a
command: no comments, coordinates at up to three decimals without
trailing zeros, M5 at the start, and the return to the origin at the
end. A sender that waits for an ok after each line can stream it as is.
Writer::set_feed changes the feed rate between contours.
The new grbl module has the real-time command bytes (status query, feed
hold, cycle start, soft reset) and the parsers of the controller's
answers: the state of a status report (read up to MAX_REPLY_LEN bytes)
and the ok / error / ALARM acknowledgement of a line. No I/O.
* Add craft-plotter: HP-GL, DMPL and GP-GL from a job in millimetres
One crate for the command languages of vinyl cutters and pen plotters,
one module per language: hpgl, dmpl, gpgl (plain) and gpgl::silhouette
(the Silhouette dialect in its three generations, with registration
marks, the device queries and the parsers of their replies).
A Job is ordered operations: polylines in millimetres plus tool
settings. Each encode gives a Program: the commands, their framing, and
where a sender waits for the registration marks. The crate does no I/O
and builds for wasm.
A job is refused past 64 operations, 2,000,000 points, 10 passes or a
2 mm overcut, or when a value is not finite. Replies are read up to 256
bytes and report at most 8 tool holders. Every limit has a test, and
tests/golden.rs fixes the exact commands of each language.
The README records where the protocol facts come from.
* craft-plotter: optional serde for Mat, as its short name
A caller that keeps a mat in a settings file or a machine table needs to
read and write it. The serde feature serializes Mat as its short name
("12x24") and refuses an unknown name. Off by default; the crate still
has thiserror as its only required dependency.
41 lines
1.6 KiB
Rust
41 lines
1.6 KiB
Rust
//! Houston Instrument DMPL, as sign cutters take it.
|
|
//!
|
|
//! The stream is `;:` and `EC1` (0.001 in resolution), then per path `Ux,y` to its start and one
|
|
//! `Dx,y,…` through the rest, then `U0,0` and `@`. One command per line. The origin is
|
|
//! bottom-left and y points up, so the design is flipped about its bottom edge. Tool holders,
|
|
//! force and speed are not addressed.
|
|
//!
|
|
//! This framing was not checked against a published manual: treat it as experimental.
|
|
|
|
use crate::{Command, Error, Framing, Job, Program, y_up};
|
|
|
|
/// Device units per mm under `EC1`: 0.001 in.
|
|
pub const UNITS_PER_MM: f64 = 1.0 / 0.0254;
|
|
|
|
/// Encodes `job` as DMPL. A job with no geometry gives an empty program.
|
|
pub fn encode(job: &Job) -> Result<Program, Error> {
|
|
let Some(prepared) = job.prepare()? else {
|
|
return Ok(Program::default());
|
|
};
|
|
let p = y_up(job, prepared.bbox_mm.2, UNITS_PER_MM);
|
|
let mut c = vec![Command::Raw(";:".to_string()), Command::Raw("EC1".to_string())];
|
|
for path in prepared.operations.iter().flatten() {
|
|
let Some((first, rest)) = path.split_first() else {
|
|
continue;
|
|
};
|
|
let (x, y) = p(first);
|
|
c.push(Command::Raw(format!("U{x},{y}")));
|
|
let pts: Vec<String> = rest
|
|
.iter()
|
|
.map(|q| {
|
|
let (x, y) = p(q);
|
|
format!("{x},{y}")
|
|
})
|
|
.collect();
|
|
c.push(Command::Raw(format!("D{}", pts.join(","))));
|
|
}
|
|
c.push(Command::Raw("U0,0".to_string()));
|
|
c.push(Command::Raw("@".to_string()));
|
|
Ok(Program { commands: c, bbox_mm: Some(prepared.bbox_mm), framing: Framing::Newline, registration_wait: None })
|
|
}
|