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 r: f64 = f64::NAN;
119 for val in arc_values {
121 match val {
122 ArcVal::X(val) => x = *val,
123 ArcVal::Y(val) => y = *val,
124 ArcVal::R(val) => r = *val,
125 _ => {
126 }
128 }
129 }
130 debug_assert!(r.is_finite());
132 radius = r;
133 todo!();
139 }
140 }
141 ArcParams {
142 center,
143 radius,
144 theta_start,
145 theta_end,
146 }
147}
148
149#[cfg(test)]
155mod tests {
156 use super::*;
157
158 fn round_to_two_decimals(x: f64) -> f64 {
159 (x * 100.0).round() / 100.0
160 }
161
162 #[test]
163 fn compute_arc_ij() {
164 let arc = compute_arc(
165 9.0,
166 6.0,
167 &ArcForm::IJ(
168 [
169 ArcVal::X(2.0),
170 ArcVal::Y(7.0),
171 ArcVal::I(-4.0),
172 ArcVal::J(-3.0),
173 ]
174 .into(),
175 ),
176 );
177 assert_eq!(arc.center, (5.0, 3.0));
178 assert_eq!(arc.radius, 5.0);
179 assert_eq!(
180 round_to_two_decimals(arc.theta_start.to_degrees()),
181 36.87_f64
182 );
183 assert_eq!(
184 round_to_two_decimals(arc.theta_end.to_degrees()),
185 126.87_f64
186 );
187 }
188
189 #[test]
190 fn troublesome_arc_ij() {
191 let arc = compute_arc(
192 0.0,
193 5.0,
194 &ArcForm::IJ(
195 [
196 ArcVal::X(5.0),
197 ArcVal::Y(0.0),
198 ArcVal::I(5.0),
199 ArcVal::J(0.0),
200 ]
201 .into(),
202 ),
203 );
204 assert_eq!(arc.center, (5.0, 5.0));
205 assert_eq!(arc.radius, 5.0);
206 assert_eq!(round_to_two_decimals(arc.theta_start.to_degrees()), 180_f64);
207 assert_eq!(round_to_two_decimals(arc.theta_end.to_degrees()), 270_f64);
208 }
209
210 #[ignore]
211 #[test]
212 fn compute_arc_r() {
214 let arc = compute_arc(
215 9.0,
216 6.0,
217 &ArcForm::R([ArcVal::X(2.0), ArcVal::Y(7.0), ArcVal::R(5.0)].into()),
218 );
219 assert_eq!(arc.center, (5.0, 3.0));
220 assert_eq!(arc.radius, 5.0);
221 }
222}