///|
pub struct ArcGeometry {
center_x_value : Double
center_y_value : Double
radius_value : Double
chord_value : Double
clockwise_value : Bool
} derive(Eq, Debug)
///|
pub fn ArcGeometry::center_x(self : ArcGeometry) -> Double {
self.center_x_value
}
///|
pub fn ArcGeometry::center_y(self : ArcGeometry) -> Double {
self.center_y_value
}
///|
pub fn ArcGeometry::radius(self : ArcGeometry) -> Double {
self.radius_value
}
///|
pub fn ArcGeometry::chord(self : ArcGeometry) -> Double {
self.chord_value
}
///|
pub fn ArcGeometry::clockwise(self : ArcGeometry) -> Bool {
self.clockwise_value
}
///|
fn absolute(value : Double) -> Double {
if value < 0.0 {
-value
} else {
value
}
}
///|
/// Derive stable geometric facts for an I/J offset arc.
pub fn arc_geometry(motion : Motion) -> Result[ArcGeometry, GCodeError] {
if motion.kind() != ClockwiseArc && motion.kind() != CounterclockwiseArc {
return Err({
code_value: InvalidArc,
message_value: "motion is not an arc",
location_value: SourceLocation::new(motion.source_line(), 1),
})
}
let center_x = motion.start().x() + motion.i()
let center_y = motion.start().y() + motion.j()
let start_dx = motion.start().x() - center_x
let start_dy = motion.start().y() - center_y
let end_dx = motion.end().x() - center_x
let end_dy = motion.end().y() - center_y
let start_radius = (start_dx * start_dx + start_dy * start_dy).sqrt()
let end_radius = (end_dx * end_dx + end_dy * end_dy).sqrt()
if start_radius == 0.0 || absolute(start_radius - end_radius) > 0.000001 {
return Err({
code_value: InvalidArc,
message_value: "arc endpoints do not share a radius",
location_value: SourceLocation::new(motion.source_line(), 1),
})
}
let chord_dx = motion.end().x() - motion.start().x()
let chord_dy = motion.end().y() - motion.start().y()
Ok({
center_x_value: center_x,
center_y_value: center_y,
radius_value: start_radius,
chord_value: (chord_dx * chord_dx + chord_dy * chord_dy).sqrt(),
clockwise_value: motion.kind() == ClockwiseArc,
})
}
///|
pub fn arc_chord_sum(program : GCodeProgram) -> Double {
let mut total = 0.0
for motion in program.motions() {
match arc_geometry(motion) {
Ok(arc) => total += arc.chord()
Err(_) => ()
}
}
total
}
///|
/// Calculate the swept XY arc length, including simultaneous Z travel.
pub fn arc_path_length(motion : Motion) -> Result[Double, GCodeError] {
let geometry = match arc_geometry(motion) {
Ok(value) => value
Err(error) => return Err(error)
}
let start_angle = @math.atan2(
motion.start().y() - geometry.center_y(),
motion.start().x() - geometry.center_x(),
)
let end_angle = @math.atan2(
motion.end().y() - geometry.center_y(),
motion.end().x() - geometry.center_x(),
)
let mut sweep = if motion.kind() == ClockwiseArc {
start_angle - end_angle
} else {
end_angle - start_angle
}
while sweep <= 0.0 {
sweep += 2.0 * @math.PI
}
let planar = geometry.radius() * sweep
let dz = motion.end().z() - motion.start().z()
Ok((planar * planar + dz * dz).sqrt())
}
///|
fn straight_path_length(motion : Motion) -> Double {
let dx = motion.end().x() - motion.start().x()
let dy = motion.end().y() - motion.start().y()
let dz = motion.end().z() - motion.start().z()
(dx * dx + dy * dy + dz * dz).sqrt()
}
///|
fn motion_path_length(motion : Motion) -> Double {
match motion.kind() {
ClockwiseArc | CounterclockwiseArc =>
match arc_path_length(motion) {
Ok(value) => value
Err(_) => straight_path_length(motion)
}
Rapid | Linear => straight_path_length(motion)
}
}