#![allow(dead_code)]
#[derive(Debug, Clone)]
pub struct GcodeCommand {
pub line: String,
}
#[derive(Debug, Clone, Default)]
pub struct GcodeDocument {
pub commands: Vec<GcodeCommand>,
pub layer_count: usize,
}
impl GcodeDocument {
pub fn new() -> Self {
Self::default()
}
}
pub fn new_gcode_document(feedrate_mm_min: f32) -> GcodeDocument {
let mut doc = GcodeDocument::new();
doc.commands.push(GcodeCommand { line: "; Generated by OxiHuman".into() });
doc.commands.push(GcodeCommand { line: "G21 ; mm units".into() });
doc.commands.push(GcodeCommand { line: "G90 ; absolute positioning".into() });
doc.commands.push(GcodeCommand { line: format!("G1 F{:.1}", feedrate_mm_min) });
doc
}
pub fn add_move(doc: &mut GcodeDocument, x: f32, y: f32, z: f32, extrude: f32) {
let cmd = format!("G1 X{:.3} Y{:.3} Z{:.3} E{:.4}", x, y, z, extrude);
doc.commands.push(GcodeCommand { line: cmd });
}
pub fn add_rapid(doc: &mut GcodeDocument, x: f32, y: f32, z: f32) {
doc.commands.push(GcodeCommand { line: format!("G0 X{:.3} Y{:.3} Z{:.3}", x, y, z) });
}
pub fn add_comment(doc: &mut GcodeDocument, comment: &str) {
doc.commands.push(GcodeCommand { line: format!("; {}", comment) });
}
pub fn export_gcode(doc: &GcodeDocument) -> String {
let mut out = String::new();
for cmd in &doc.commands {
out.push_str(&cmd.line);
out.push('\n');
}
out
}
pub fn estimate_print_time_min(total_mm: f32, feedrate_mm_min: f32) -> f32 {
if feedrate_mm_min <= 0.0 { return 0.0; }
total_mm / feedrate_mm_min
}
pub fn move_command_count(doc: &GcodeDocument) -> usize {
doc.commands.iter().filter(|c| c.line.starts_with("G1 X")).count()
}
pub fn command_count(doc: &GcodeDocument) -> usize {
doc.commands.len()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_document_has_header() {
let doc = new_gcode_document(3000.0);
assert!(command_count(&doc) >= 3);
}
#[test]
fn test_add_move_increases_count() {
let mut doc = new_gcode_document(1500.0);
let before = command_count(&doc);
add_move(&mut doc, 10.0, 20.0, 0.5, 1.0);
assert_eq!(command_count(&doc), before + 1);
}
#[test]
fn test_add_rapid_increases_count() {
let mut doc = new_gcode_document(1500.0);
let before = command_count(&doc);
add_rapid(&mut doc, 0.0, 0.0, 10.0);
assert_eq!(command_count(&doc), before + 1);
}
#[test]
fn test_export_gcode_contains_moves() {
let mut doc = new_gcode_document(1500.0);
add_move(&mut doc, 5.0, 5.0, 0.0, 0.5);
let s = export_gcode(&doc);
assert!(s.contains("G1 X"));
}
#[test]
fn test_move_command_count() {
let mut doc = new_gcode_document(1500.0);
add_move(&mut doc, 1.0, 2.0, 0.0, 0.1);
add_move(&mut doc, 3.0, 4.0, 0.0, 0.2);
assert_eq!(move_command_count(&doc), 2);
}
#[test]
fn test_add_comment() {
let mut doc = GcodeDocument::new();
add_comment(&mut doc, "layer 1");
let s = export_gcode(&doc);
assert!(s.contains("; layer 1"));
}
#[test]
fn test_estimate_print_time() {
let t = estimate_print_time_min(600.0, 3000.0);
assert!((t - 0.2).abs() < 1e-5);
}
#[test]
fn test_estimate_print_time_zero_feedrate() {
assert!((estimate_print_time_min(100.0, 0.0)).abs() < 1e-6);
}
#[test]
fn test_export_ends_with_newline() {
let doc = new_gcode_document(1200.0);
let s = export_gcode(&doc);
assert!(s.ends_with('\n'));
}
#[test]
fn test_empty_document_export() {
let doc = GcodeDocument::new();
assert_eq!(export_gcode(&doc), "");
}
}