1 |
|
/******************************************************************** |
2 |
|
* Description: interp_arc.cc |
3 |
|
* |
4 |
|
* Derived from a work by Thomas Kramer |
5 |
|
* |
6 |
|
* Author: |
7 |
|
* License: GPL Version 2 |
8 |
|
* System: Linux |
9 |
|
* |
10 |
|
* Copyright (c) 2004 All rights reserved. |
11 |
|
* |
12 |
|
* Last change: |
13 |
|
********************************************************************/ |
14 |
|
#ifndef _GNU_SOURCE |
15 |
|
#define _GNU_SOURCE |
16 |
|
#endif |
17 |
|
#include <unistd.h> |
18 |
|
#include <stdio.h> |
19 |
|
#include <stdlib.h> |
20 |
|
#include <math.h> |
21 |
|
#include <string.h> |
22 |
|
#include <ctype.h> |
23 |
|
#include <sys/types.h> |
24 |
|
#include <sys/stat.h> |
25 |
|
#include <libintl.h> |
26 |
|
#include <rtapi_math.h> |
27 |
|
#include "rs274ngc.hh" |
28 |
|
#include "rs274ngc_return.hh" |
29 |
|
#include "rs274ngc_interp.hh" |
30 |
|
#include "interp_internal.hh" |
31 |
|
|
32 |
|
#define _(s) gettext(s) |
33 |
|
|
34 |
535 |
char Interp::arc_axis1(int plane) { |
35 |
535 |
switch(plane) { |
36 |
|
case CANON_PLANE_XY: return 'X'; |
37 |
|
case CANON_PLANE_XZ: return 'Z'; |
38 |
|
case CANON_PLANE_YZ: return 'Y'; |
39 |
|
default: return '!'; |
40 |
|
} |
41 |
|
} |
42 |
|
|
43 |
535 |
char Interp::arc_axis2(int plane) { |
44 |
535 |
switch(plane) { |
45 |
|
case CANON_PLANE_XY: return 'Y'; |
46 |
|
case CANON_PLANE_XZ: return 'X'; |
47 |
|
case CANON_PLANE_YZ: return 'Z'; |
48 |
|
default: return '!'; |
49 |
|
} |
50 |
|
} |
51 |
|
|
52 |
|
|
53 |
|
/***********************************************************************/ |
54 |
|
|
55 |
|
/*! arc_data_comp_ijk |
56 |
|
|
57 |
|
Returned Value: int |
58 |
|
If any of the following errors occur, this returns the error code shown. |
59 |
|
Otherwise, it returns INTERP_OK. |
60 |
|
1. The two calculable values of the radius differ by more than |
61 |
|
tolerance: NCE_RADIUS_TO_END_OF_ARC_DIFFERS_FROM_RADIUS_TO_START |
62 |
|
2. move is not G_2 or G_3: NCE_BUG_CODE_NOT_G2_OR_G3 |
63 |
|
|
64 |
|
Side effects: |
65 |
|
This finds and sets the values of center_x, center_y, and turn. |
66 |
|
|
67 |
|
Called by: convert_arc_comp1 |
68 |
|
|
69 |
|
This finds the center coordinates and number of full or partial turns |
70 |
|
counterclockwise of a helical or circular arc in ijk-format in the XY |
71 |
|
plane. The center is computed easily from the current point and center |
72 |
|
offsets, which are given. It is checked that the end point lies one |
73 |
|
tool radius from the arc. |
74 |
|
|
75 |
|
|
76 |
|
*/ |
77 |
|
|
78 |
|
int Interp::arc_data_comp_ijk(int move, //!<either G_2 (cw arc) or G_3 (ccw arc) |
79 |
|
int plane, //!<active plane |
80 |
|
int side, //!<either RIGHT or LEFT |
81 |
|
double tool_radius, //!<radius of the tool |
82 |
|
double current_x, //!<first coordinate of current point |
83 |
|
double current_y, //!<second coordinate of current point |
84 |
|
double end_x, //!<first coordinate of arc end point |
85 |
|
double end_y, //!<second coordinate of arc end point |
86 |
|
int ij_absolute, //!<how to interpret i/j numbers |
87 |
|
double i_number, //!<first coordinate of center (abs or incr) |
88 |
|
double j_number, //!<second coordinate of center (abs or incr) |
89 |
|
int p_number, |
90 |
|
double *center_x, //!<pointer to first coordinate of center of arc |
91 |
|
double *center_y, //!<pointer to second coordinate of center of arc |
92 |
|
int *turn, //!<pointer to number of full or partial circles CCW |
93 |
|
double radius_tolerance, //!<minimum radius tolerance |
94 |
|
double spiral_abs_tolerance, //!<tolerance of start and end radius difference |
95 |
|
double spiral_rel_tolerance) |
96 |
|
{ |
97 |
|
double arc_radius; |
98 |
|
double radius2; |
99 |
|
char a = arc_axis1(plane), b = arc_axis2(plane); |
100 |
|
|
101 |
|
if ( ij_absolute ) { |
102 |
|
*center_x = (i_number); |
103 |
|
*center_y = (j_number); |
104 |
|
} else { |
105 |
|
*center_x = (current_x + i_number); |
106 |
|
*center_y = (current_y + j_number); |
107 |
|
} |
108 |
|
arc_radius = hypot((*center_x - current_x), (*center_y - current_y)); |
109 |
|
radius2 = hypot((*center_x - end_x), (*center_y - end_y)); |
110 |
|
CHKS(((arc_radius < radius_tolerance) || (radius2 < radius_tolerance)), |
111 |
|
_("Zero-radius arc: " |
112 |
|
"start=(%c%.4f,%c%.4f) center=(%c%.4f,%c%.4f) end=(%c%.4f,%c%.4f) r1=%.4f r2=%.4f"), |
113 |
|
a, current_x, b, current_y, |
114 |
|
a, *center_x, b, *center_y, |
115 |
|
a, end_x, b, end_y, arc_radius, radius2); |
116 |
|
|
117 |
|
double abs_err = fabs(arc_radius - radius2); |
118 |
|
double rel_err = abs_err / std::max(arc_radius, radius2); |
119 |
|
|
120 |
|
CHKS((abs_err > spiral_abs_tolerance * 100.0) || |
121 |
|
(rel_err > spiral_rel_tolerance && abs_err > spiral_abs_tolerance), |
122 |
|
_("Radius to end of arc differs from radius to start: " |
123 |
|
"start=(%c%.4f,%c%.4f) center=(%c%.4f,%c%.4f) end=(%c%.4f,%c%.4f) " |
124 |
|
"r1=%.4f r2=%.4f abs_err=%.4g rel_err=%.4f%%"), |
125 |
|
a, current_x, b, current_y, |
126 |
|
a, *center_x, b, *center_y, |
127 |
|
a, end_x, b, end_y, arc_radius, radius2, |
128 |
|
abs_err, rel_err*100); |
129 |
|
|
130 |
|
CHKS(((arc_radius <= tool_radius) && (((side == LEFT) && (move == G_3)) || |
131 |
|
((side == RIGHT) && (move == G_2)))), |
132 |
|
NCE_TOOL_RADIUS_NOT_LESS_THAN_ARC_RADIUS_WITH_COMP); |
133 |
|
|
134 |
|
/* This catches an arc too small for the tool, also */ |
135 |
|
if (move == G_2) |
136 |
|
*turn = -1 * p_number; |
137 |
|
else if (move == G_3) |
138 |
|
*turn = 1 * p_number; |
139 |
|
else |
140 |
|
ERS(NCE_BUG_CODE_NOT_G2_OR_G3); |
141 |
|
return INTERP_OK; |
142 |
|
} |
143 |
|
|
144 |
|
/****************************************************************************/ |
145 |
|
|
146 |
|
/*! arc_data_comp_r |
147 |
|
|
148 |
|
Returned Value: int |
149 |
|
If any of the following errors occur, this returns the error code shown. |
150 |
|
Otherwise, it returns INTERP_OK. |
151 |
|
1. The arc radius is too small to reach the end point: |
152 |
|
NCE_RADIUS_TOO_SMALL_TO_REACH_END_POINT |
153 |
|
2. The arc radius is not greater than the tool_radius, but should be: |
154 |
|
NCE_TOOL_RADIUS_NOT_LESS_THAN_ARC_RADIUS_WITH_COMP |
155 |
|
3. An imaginary value for offset would be found, which should never |
156 |
|
happen if the theory is correct: NCE_BUG_IN_TOOL_RADIUS_COMP |
157 |
|
|
158 |
|
Side effects: |
159 |
|
This finds and sets the values of center_x, center_y, and turn. |
160 |
|
|
161 |
|
Called by: convert_arc_comp1 |
162 |
|
|
163 |
|
This finds the center coordinates and number of full or partial turns |
164 |
|
counterclockwise of a helical or circular arc (call it arc1) in |
165 |
|
r-format in the XY plane. Arc2 is constructed so that it is tangent |
166 |
|
to a circle whose radius is tool_radius and whose center is at the |
167 |
|
point (current_x, current_y) and passes through the point (end_x, |
168 |
|
end_y). Arc1 has the same center as arc2. The radius of arc1 is one |
169 |
|
tool radius larger or smaller than the radius of arc2. |
170 |
|
|
171 |
|
If the value of the big_radius argument is negative, that means [NCMS, |
172 |
|
page 21] that an arc larger than a semicircle is to be made. |
173 |
|
Otherwise, an arc of a semicircle or less is made. |
174 |
|
|
175 |
|
The algorithm implemented here is to construct a line L from the |
176 |
|
current point to the end point, and a perpendicular to it from the |
177 |
|
center of the arc which intersects L at point P. Since the distance |
178 |
|
from the end point to the center and the distance from the current |
179 |
|
point to the center are known, two equations for the length of the |
180 |
|
perpendicular can be written. The right sides of the equations can be |
181 |
|
set equal to one another and the resulting equation solved for the |
182 |
|
length of the line from the current point to P. Then the location of |
183 |
|
P, the length of the perpendicular, the angle of the perpendicular, |
184 |
|
and the location of the center, can be found in turn. |
185 |
|
|
186 |
|
This needs to be better documented, with figures. There are eight |
187 |
|
possible arcs, since there are three binary possibilities: (1) tool |
188 |
|
inside or outside arc, (2) clockwise or counterclockwise (3) two |
189 |
|
positions for each arc (of the given radius) tangent to the tool |
190 |
|
outline and through the end point. All eight are calculated below, |
191 |
|
since theta, radius2, and turn may each have two values. |
192 |
|
|
193 |
|
To see two positions for each arc, imagine the arc is a hoop, the |
194 |
|
tool is a cylindrical pin, and the arc may rotate around the end point. |
195 |
|
The rotation covers all possible positions of the arc. It is easy to |
196 |
|
see the hoop is constrained by the pin at two different angles, whether |
197 |
|
the pin is inside or outside the hoop. |
198 |
|
|
199 |
|
*/ |
200 |
|
|
201 |
|
int Interp::arc_data_comp_r(int move, //!< either G_2 (cw arc) or G_3 (ccw arc) |
202 |
|
int plane, |
203 |
|
int side, //!< either RIGHT or LEFT |
204 |
|
double tool_radius, //!< radius of the tool |
205 |
|
double current_x, //!< first coordinate of current point |
206 |
|
double current_y, //!< second coordinate of current point |
207 |
|
double end_x, //!< first coordinate of arc end point |
208 |
|
double end_y, //!< second coordinate of arc end point |
209 |
|
double big_radius, //!< radius of arc |
210 |
|
int p_number, |
211 |
|
double *center_x, //!< pointer to first coordinate of center of arc |
212 |
|
double *center_y, //!< pointer to second coordinate of center of arc |
213 |
|
int *turn, //!< pointer to number of full or partial circles CCW |
214 |
|
double tolerance) //!< tolerance of differing radii |
215 |
|
{ |
216 |
|
double abs_radius; // absolute value of big_radius |
217 |
|
|
218 |
|
abs_radius = fabs(big_radius); |
219 |
|
CHKS(((abs_radius <= tool_radius) && (((side == LEFT) && (move == G_3)) || |
220 |
|
((side == RIGHT) && (move == G_2)))), |
221 |
|
NCE_TOOL_RADIUS_NOT_LESS_THAN_ARC_RADIUS_WITH_COMP); |
222 |
|
|
223 |
|
return arc_data_r(move, plane, current_x, current_y, end_x, end_y, big_radius, p_number, |
224 |
|
center_x, center_y, turn, tolerance); |
225 |
|
|
226 |
|
} |
227 |
|
|
228 |
|
/****************************************************************************/ |
229 |
|
|
230 |
|
/*! arc_data_ijk |
231 |
|
|
232 |
|
Returned Value: int |
233 |
|
If any of the following errors occur, this returns the error code shown. |
234 |
|
Otherwise, it returns INTERP_OK. |
235 |
|
1. The two calculable values of the radius differ by more than |
236 |
|
tolerance: NCE_RADIUS_TO_END_OF_ARC_DIFFERS_FROM_RADIUS_TO_START |
237 |
|
2. The move code is not G_2 or G_3: NCE_BUG_CODE_NOT_G2_OR_G3 |
238 |
|
3. Either of the two calculable values of the radius is zero: |
239 |
|
NCE_ZERO_RADIUS_ARC |
240 |
|
|
241 |
|
Side effects: |
242 |
|
This finds and sets the values of center_x, center_y, and turn. |
243 |
|
|
244 |
|
Called by: |
245 |
|
convert_arc2 |
246 |
|
convert_arc_comp2 |
247 |
|
|
248 |
|
This finds the center coordinates and number of full or partial turns |
249 |
|
counterclockwise of a helical or circular arc in ijk-format. This |
250 |
|
function is used by convert_arc2 for all three planes, so "x" and |
251 |
|
"y" really mean "first_coordinate" and "second_coordinate" wherever |
252 |
|
they are used here as suffixes of variable names. The i and j prefixes |
253 |
|
are handled similarly. |
254 |
|
|
255 |
|
*/ |
256 |
|
|
257 |
535 |
int Interp::arc_data_ijk(int move, //!< either G_2 (cw arc) or G_3 (ccw arc) |
258 |
|
int plane, |
259 |
|
double current_x, //!< first coordinate of current point |
260 |
|
double current_y, //!< second coordinate of current point |
261 |
|
double end_x, //!< first coordinate of arc end point |
262 |
|
double end_y, //!< second coordinate of arc end point |
263 |
|
int ij_absolute, //!<how to interpret i/j numbers |
264 |
|
double i_number, //!<first coordinate of center (abs or incr) |
265 |
|
double j_number, //!<second coordinate of center (abs or incr) |
266 |
|
int p_number, |
267 |
|
double *center_x, //!< pointer to first coordinate of center of arc |
268 |
|
double *center_y, //!< pointer to second coordinate of center of arc |
269 |
|
int *turn, //!< pointer to no. of full or partial circles CCW |
270 |
|
double radius_tolerance, //!<minimum radius tolerance |
271 |
|
double spiral_abs_tolerance, //!<tolerance of start and end radius difference |
272 |
|
double spiral_rel_tolerance) |
273 |
|
{ |
274 |
|
double radius; /* radius to current point */ |
275 |
|
double radius2; /* radius to end point */ |
276 |
535 |
char a = arc_axis1(plane), b = arc_axis2(plane); |
277 |
|
|
278 |
535 |
if ( ij_absolute ) { |
279 |
|
*center_x = (i_number); |
280 |
|
*center_y = (j_number); |
281 |
|
} else { |
282 |
535 |
*center_x = (current_x + i_number); |
283 |
535 |
*center_y = (current_y + j_number); |
284 |
|
} |
285 |
535 |
radius = hypot((*center_x - current_x), (*center_y - current_y)); |
286 |
535 |
radius2 = hypot((*center_x - end_x), (*center_y - end_y)); |
287 |
535 |
CHKS(((radius < radius_tolerance) || (radius2 < radius_tolerance)),_("Zero-radius arc: " |
288 |
|
"start=(%c%.4f,%c%.4f) center=(%c%.4f,%c%.4f) end=(%c%.4f,%c%.4f) r1=%.4f r2=%.4f"), |
289 |
|
a, current_x, b, current_y, |
290 |
|
a, *center_x, b, *center_y, |
291 |
|
a, end_x, b, end_y, radius, radius2); |
292 |
535 |
double abs_err = fabs(radius - radius2); |
293 |
535 |
double rel_err = abs_err / std::max(radius, radius2); |
294 |
535 |
CHKS((abs_err > spiral_abs_tolerance * 100.0) || |
295 |
|
(rel_err > spiral_rel_tolerance && abs_err > spiral_abs_tolerance), |
296 |
|
_("Radius to end of arc differs from radius to start: " |
297 |
|
"start=(%c%.4f,%c%.4f) center=(%c%.4f,%c%.4f) end=(%c%.4f,%c%.4f) " |
298 |
|
"r1=%.4f r2=%.4f abs_err=%.4g rel_err=%.4f%%"), |
299 |
|
a, current_x, b, current_y, |
300 |
|
a, *center_x, b, *center_y, |
301 |
|
a, end_x, b, end_y, radius, radius2, |
302 |
|
abs_err, rel_err*100); |
303 |
|
|
304 |
529 |
if (move == G_2) |
305 |
360 |
*turn = -1 * p_number; |
306 |
169 |
else if (move == G_3) |
307 |
169 |
*turn = 1 * p_number; |
308 |
|
else |
309 |
|
ERS(NCE_BUG_CODE_NOT_G2_OR_G3); |
310 |
|
return INTERP_OK; |
311 |
|
} |
312 |
|
|
313 |
|
/****************************************************************************/ |
314 |
|
|
315 |
|
/*! arc_data_r |
316 |
|
|
317 |
|
Returned Value: int |
318 |
|
If any of the following errors occur, this returns the error shown. |
319 |
|
Otherwise, it returns INTERP_OK. |
320 |
|
1. The radius is too small to reach the end point: |
321 |
|
NCE_ARC_RADIUS_TOO_SMALL_TO_REACH_END_POINT |
322 |
|
2. The current point is the same as the end point of the arc |
323 |
|
(so that it is not possible to locate the center of the circle): |
324 |
|
NCE_CURRENT_POINT_SAME_AS_END_POINT_OF_ARC |
325 |
|
|
326 |
|
Side effects: |
327 |
|
This finds and sets the values of center_x, center_y, and turn. |
328 |
|
|
329 |
|
Called by: |
330 |
|
convert_arc2 |
331 |
|
convert_arc_comp2 |
332 |
|
|
333 |
|
This finds the center coordinates and number of full or partial turns |
334 |
|
counterclockwise of a helical or circular arc in the r format. This |
335 |
|
function is used by convert_arc2 for all three planes, so "x" and |
336 |
|
"y" really mean "first_coordinate" and "second_coordinate" wherever |
337 |
|
they are used here as suffixes of variable names. |
338 |
|
|
339 |
|
If the value of the radius argument is negative, that means [NCMS, |
340 |
|
page 21] that an arc larger than a semicircle is to be made. |
341 |
|
Otherwise, an arc of a semicircle or less is made. |
342 |
|
|
343 |
|
The algorithm used here is based on finding the midpoint M of the line |
344 |
|
L between the current point and the end point of the arc. The center |
345 |
|
of the arc lies on a line through M perpendicular to L. |
346 |
|
|
347 |
|
*/ |
348 |
|
|
349 |
|
int Interp::arc_data_r(int move, //!< either G_2 (cw arc) or G_3 (ccw arc) |
350 |
|
int plane, |
351 |
|
double current_x, //!< first coordinate of current point |
352 |
|
double current_y, //!< second coordinate of current point |
353 |
|
double end_x, //!< first coordinate of arc end point |
354 |
|
double end_y, //!< second coordinate of arc end point |
355 |
|
double radius, //!< radius of arc |
356 |
|
int p_number, |
357 |
|
double *center_x, //!< pointer to first coordinate of center of arc |
358 |
|
double *center_y, //!< pointer to second coordinate of center of arc |
359 |
|
int *turn, //!< pointer to number of full or partial circles CCW |
360 |
|
double tolerance) //!< tolerance of differing radii |
361 |
|
{ |
362 |
|
double abs_radius; /* absolute value of given radius */ |
363 |
|
double half_length; /* distance from M to end point */ |
364 |
|
double mid_x; /* first coordinate of M */ |
365 |
|
double mid_y; /* second coordinate of M */ |
366 |
|
double offset; /* distance from M to center */ |
367 |
|
double theta; /* angle of line from M to center */ |
368 |
|
double turn2; /* absolute value of half of turn */ |
369 |
|
|
370 |
|
CHKS(((end_x == current_x) && (end_y == current_y)), |
371 |
|
NCE_CURRENT_POINT_SAME_AS_END_POINT_OF_ARC); |
372 |
|
abs_radius = fabs(radius); |
373 |
|
mid_x = (end_x + current_x) / 2.0; |
374 |
|
mid_y = (end_y + current_y) / 2.0; |
375 |
|
half_length = hypot((mid_x - end_x), (mid_y - end_y)); |
376 |
|
CHKS(((half_length - abs_radius) > tolerance), |
377 |
|
NCE_ARC_RADIUS_TOO_SMALL_TO_REACH_END_POINT); |
378 |
|
if ((half_length / abs_radius) > (1 - TINY)) |
379 |
|
half_length = abs_radius; /* allow a small error for semicircle */ |
380 |
|
/* check needed before calling asin */ |
381 |
|
if (((move == G_2) && (radius > 0)) || ((move == G_3) && (radius < 0))) |
382 |
|
theta = atan2((end_y - current_y), (end_x - current_x)) - M_PI_2l; |
383 |
|
else |
384 |
|
theta = atan2((end_y - current_y), (end_x - current_x)) + M_PI_2l; |
385 |
|
|
386 |
|
turn2 = asin(half_length / abs_radius); |
387 |
|
offset = abs_radius * cos(turn2); |
388 |
|
*center_x = mid_x + (offset * cos(theta)); |
389 |
|
*center_y = mid_y + (offset * sin(theta)); |
390 |
|
*turn = (move == G_2) ? -1 * p_number : 1 * p_number; |
391 |
|
|
392 |
|
return INTERP_OK; |
393 |
|
} |