1#![deny(clippy::all)]
4#![warn(clippy::cargo)]
5#![warn(clippy::complexity)]
6#![warn(clippy::pedantic)]
7#![warn(clippy::nursery)]
8#![warn(clippy::perf)]
9#![warn(missing_debug_implementations)]
10#![warn(missing_docs)]
11#![allow(clippy::many_single_char_names)]
12
13use core::f64;
14
15use crate::arc::ArcVal;
16use crate::arc::Form as ArcForm;
17
18pub static MM_PER_ARC_SEGMENT: f64 = 1_f64;
21
22pub mod binary;
24pub mod command;
26mod double;
27pub mod params;
29
30pub mod arc;
32
33#[derive(Default, Debug, Eq, PartialEq)]
35pub enum PositionMode {
36 #[default]
39 Absolute,
40 Relative,
42}
43
44#[derive(Debug)]
50pub struct ArcParams {
51 pub center: (f64, f64),
53 pub radius: f64,
55 pub theta_start: f64,
57 pub theta_end: f64,
59}
60
61#[must_use] pub fn compute_arc(current_x: f64, current_y: f64, form: &ArcForm) -> ArcParams {
66 let mut i: f64 = 0_f64;
68 let mut j: f64 = 0_f64;
69
70 let mut x: f64 = f64::NAN;
71 let mut y: f64 = f64::NAN;
72
73 let radius: f64;
74 let center: (f64, f64);
75 let mut theta_start: f64;
76 let mut theta_end: f64;
77
78 match form {
79 ArcForm::IJ(arc_values) => {
80 for val in arc_values {
82 match val {
83 ArcVal::X(val) => x = *val,
84 ArcVal::Y(val) => y = *val,
85 ArcVal::I(val) => i = *val,
86 ArcVal::J(val) => j = *val,
87 _ => {}
88 }
89 }
90
91 debug_assert!(x.is_finite());
93 debug_assert!(y.is_finite());
94
95 radius = i.hypot(j);
96 center = (current_x + i, current_y + j);
97
98 let delta_start_x = current_x - center.0;
99 let delta_start_y = current_y - center.1;
100
101 theta_start = (delta_start_y).atan2(delta_start_x);
102 if theta_start < 0_f64 {
105 theta_start += 2_f64 * f64::consts::PI;
106 }
107
108 let delta_end_x = x - center.0;
109 let delta_end_y = y - center.1;
110 theta_end = (delta_end_y).atan2(delta_end_x);
111 if theta_end < 0_f64 {
114 theta_end += 2_f64 * f64::consts::PI;
115 }
116 }
117 ArcForm::R(arc_values) => {
118 let mut radius: f64 = f64::NAN;
119 for val in arc_values {
122 match val {
123 ArcVal::X(val) => x = *val,
124 ArcVal::Y(val) => y = *val,
125 ArcVal::R(val) => radius = *val,
126 _ => {
127 }
129 }
130 }
131 debug_assert!(x.is_finite());
132 debug_assert!(y.is_finite());
133 debug_assert!(radius.is_finite());
135 todo!();
142 }
143 }
144 ArcParams {
145 center,
146 radius,
147 theta_start,
148 theta_end,
149 }
150}
151
152#[cfg(test)]
158mod tests {
159 use super::*;
160
161 fn round_to_two_decimals(x: f64) -> f64 {
162 (x * 100.0).round() / 100.0
163 }
164
165 #[test]
166 fn compute_arc_ij() {
167 let arc = compute_arc(
168 9.0,
169 6.0,
170 &ArcForm::IJ(
171 [
172 ArcVal::X(2.0),
173 ArcVal::Y(7.0),
174 ArcVal::I(-4.0),
175 ArcVal::J(-3.0),
176 ]
177 .into(),
178 ),
179 );
180 assert_eq!(arc.center, (5.0, 3.0));
181 assert_eq!(arc.radius, 5.0);
182 assert_eq!(
183 round_to_two_decimals(arc.theta_start.to_degrees()),
184 36.87_f64
185 );
186 assert_eq!(
187 round_to_two_decimals(arc.theta_end.to_degrees()),
188 126.87_f64
189 );
190 }
191
192 #[test]
193 fn troublesome_arc_ij() {
194 let arc = compute_arc(
195 0.0,
196 5.0,
197 &ArcForm::IJ(
198 [
199 ArcVal::X(5.0),
200 ArcVal::Y(0.0),
201 ArcVal::I(5.0),
202 ArcVal::J(0.0),
203 ]
204 .into(),
205 ),
206 );
207 assert_eq!(arc.center, (5.0, 5.0));
208 assert_eq!(arc.radius, 5.0);
209 assert_eq!(round_to_two_decimals(arc.theta_start.to_degrees()), 180_f64);
210 assert_eq!(round_to_two_decimals(arc.theta_end.to_degrees()), 270_f64);
211 }
212
213 #[ignore]
214 #[test]
215 fn compute_arc_r() {
217 let arc = compute_arc(
218 9.0,
219 6.0,
220 &ArcForm::R([ArcVal::X(2.0), ArcVal::Y(7.0), ArcVal::R(5.0)].into()),
221 );
222 assert_eq!(arc.center, (5.0, 3.0));
223 assert_eq!(arc.radius, 5.0);
224 }
225}