use anyhow::{anyhow, Result};
use crate::instructions::*;
use crate::program::*;
use crate::types::*;
use crate::utils::*;
#[derive(Debug, Clone)]
pub struct Area {
pub start: Vector3,
pub size: Vector2,
pub end_z: f64,
#[deprecated(
since = "0.1.0",
note = "Only works in one direction, will likely be removed in future releases."
)]
pub end_z_stop: f64,
pub max_step_z: f64,
pub compensation: ToolPathCompensation,
}
#[allow(deprecated)]
impl Area {
#[must_use]
pub fn new(
start: Vector3,
size: Vector2,
end_z: f64,
max_step_z: f64,
compensation: ToolPathCompensation,
) -> Self {
Self {
start,
size,
end_z,
end_z_stop: end_z,
max_step_z,
compensation,
}
}
#[deprecated(
since = "0.1.0",
note = "Only works in one direction, will likely be removed in future releases."
)]
#[must_use]
pub fn new_with_slope(
start: Vector3,
size: Vector2,
end_z: f64,
end_z_stop: f64,
max_step_z: f64,
compensation: ToolPathCompensation,
) -> Self {
Self {
start,
size,
end_z,
end_z_stop,
max_step_z,
compensation,
}
}
#[must_use]
pub fn bounds(&self) -> Bounds {
Bounds {
min: Vector3::new(self.start.x, self.start.y, self.end_z.min(self.end_z_stop)),
max: Vector3::new(
self.start.x + self.size.x,
self.start.y + self.size.y,
self.start.z,
),
}
}
pub fn to_instructions(&self, context: InnerContext) -> Result<Vec<Instruction>> {
let tool_radius = context.tool().radius();
let tool_diameter = context.tool().diameter();
let tool_units = context.tool().units();
if self.size.x < tool_diameter {
return Err(anyhow!("Unable to plane area, tool is {:.2} {} wider than x dimension (tool diameter is {:.2} {})", tool_diameter - self.size.x, tool_units, tool_diameter, tool_units));
}
if self.size.y < tool_diameter {
return Err(anyhow!("Unable to plane area, tool is {:.2} {} wider than y dimension (tool diameter is {:.2} {})", tool_diameter - self.size.y, tool_units, tool_diameter, tool_units));
}
let start = match self.compensation {
ToolPathCompensation::None => self.start,
ToolPathCompensation::Inner => self.start.add_x(tool_radius).add_y(tool_radius),
ToolPathCompensation::Outer => self.start.add_x(-tool_radius).add_y(-tool_radius),
};
let size = match self.compensation {
ToolPathCompensation::None => self.size,
ToolPathCompensation::Inner => self
.size
.add_x(-tool_radius * 2.0)
.add_y(-tool_radius * 2.0),
ToolPathCompensation::Outer => {
self.size.add_x(tool_radius * 2.0).add_y(tool_radius * 2.0)
}
};
let mut instructions = Vec::new();
instructions.append(&mut vec![
Instruction::Empty(Empty {}),
Instruction::Comment(Comment {
text: format!(
"Do planing at: x = {}, y = {}, size = {}",
round_precision(start.x),
round_precision(start.y),
size
),
}),
Instruction::G0(G0 {
x: None,
y: None,
z: Some(context.z_safe()),
}),
Instruction::G0(G0 {
x: Some(start.x),
y: Some(start.y),
z: None,
}),
Instruction::G1(G1 {
x: None,
y: None,
z: Some(start.z),
f: Some(context.tool().feed_rate()),
}),
]);
let delta_z = self.end_z_stop - self.end_z;
let max_step_z = self.max_step_z.abs();
let layers = if (self.end_z - self.end_z_stop).abs() < 0.01 {
((self.end_z - start.z).abs() / max_step_z).ceil() as u32
} else {
(delta_z.abs() / max_step_z).ceil() as u32
};
let start_z = if delta_z < 0.0 {
start.z - delta_z
} else {
start.z
};
let mut end_z = start_z;
let mut end_z_stop = start_z + delta_z;
for _layer in 1..layers {
end_z -= max_step_z;
end_z_stop -= max_step_z;
instructions.append(&mut self.generate_layer_instructions(
start,
size,
end_z.min(context.z_safe()),
end_z_stop.min(context.z_safe()),
tool_radius,
));
}
instructions.append(&mut self.generate_layer_instructions(
start,
size,
self.end_z.min(context.z_safe()),
self.end_z_stop.min(context.z_safe()),
tool_radius,
));
instructions.push(Instruction::G0(G0 {
x: None,
y: None,
z: Some(context.z_safe()),
}));
Ok(instructions)
}
fn generate_layer_instructions(
&self,
start: Vector3,
size: Vector2,
end_z: f64,
end_z_stop: f64,
tool_radius: f64,
) -> Vec<Instruction> {
let mut instructions = Vec::new();
let size_y = size.y;
let passes = (size_y / (tool_radius * 1.8)).ceil() as i32;
let pass_y = size_y / passes as f64;
instructions.push(Instruction::G1(G1 {
x: Some(start.x + size.x),
y: None,
z: Some(end_z_stop),
f: None,
}));
instructions.push(Instruction::G1(G1 {
x: None,
y: Some(start.y + size.y),
z: None,
f: None,
}));
instructions.push(Instruction::G1(G1 {
x: Some(start.x),
y: None,
z: Some(end_z),
f: None,
}));
instructions.push(Instruction::G1(G1 {
x: None,
y: Some(start.y),
z: None,
f: None,
}));
let mut end_at_start = true;
if size.x > tool_radius * 2.0 {
for index in 0..passes {
instructions.push(Instruction::G1(G1 {
x: None,
y: Some(start.y + index as f64 * pass_y),
z: None,
f: None,
}));
if index % 2 == 0 {
instructions.push(Instruction::G1(G1 {
x: Some(start.x + size.x),
y: None,
z: Some(end_z_stop),
f: None,
}));
end_at_start = false;
} else {
instructions.push(Instruction::G1(G1 {
x: Some(start.x),
y: None,
z: Some(end_z),
f: None,
}));
end_at_start = true;
}
}
}
instructions.push(Instruction::G0(G0 {
x: None,
y: None,
z: Some(if end_at_start { end_z } else { end_z_stop } + 0.5),
}));
instructions.push(Instruction::G0(G0 {
x: Some(start.x),
y: Some(start.y),
z: Some(end_z + 0.5),
}));
instructions.push(Instruction::G1(G1 {
x: None,
y: None,
z: Some(end_z),
f: None,
}));
instructions
}
}