Root/
Source at commit ea11b88bee941efb5a451b1509b752913c9b9ec6 created 8 years 6 months ago. By Werner Almesberger, gp2rml/gp2rml.c (main): use getopt | |
---|---|
1 | /* |
2 | * gp2rml.c - Convert from gnuplot to RML |
3 | * |
4 | * Written 2010-2013, 2015 by Werner Almesberger |
5 | * Copyright 2010-2013, 2015 Werner Almesberger |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | */ |
12 | |
13 | /* |
14 | * When setting the pen up and pen down positions (!PZ), pen up must be a |
15 | * zero or positive coordinate relative to Z0 and pen down must be negative |
16 | * or zero. |
17 | * |
18 | * Our pen up position is defined as the maximum pen down height plus some |
19 | * clearance. All Z coordinates are relative to Z0. |
20 | * |
21 | * All this means that we have to set Z0 (with !ZO, that's zet-oh) such that |
22 | * it is between the maximum pen down and pen up. Since pen up is also defined |
23 | * via the maximum pen down, we have to read all coordinates before we can |
24 | * calculate Z0 and then emit the correct positioning commands. Yuck. |
25 | * |
26 | * We choose Z0 = maximum pen down. |
27 | */ |
28 | |
29 | /* |
30 | * @@@ speed selection anomaly: |
31 | * |
32 | * Seems that !ZZ movements of the MDX-15 only use !VZ and VS is completely |
33 | * ignored. |
34 | */ |
35 | |
36 | #include <stdlib.h> |
37 | #include <stdio.h> |
38 | #include <unistd.h> |
39 | #include <string.h> |
40 | #include <math.h> |
41 | |
42 | |
43 | #define V_MAX 15.0 |
44 | |
45 | |
46 | struct segment { |
47 | double x, y, z; |
48 | struct segment *next; |
49 | }; |
50 | |
51 | struct path { |
52 | struct segment *segments; |
53 | struct path *next; |
54 | }; |
55 | |
56 | |
57 | static struct path *paths = NULL, *curr_path; |
58 | static struct segment **next_seg; |
59 | static double z_max = -1e6; /* -1 km :) */ |
60 | static double z_scale = 1; /* < 1: compress; > 1: stretch */ |
61 | |
62 | |
63 | #define units(mm) ((int) round((mm)*40.0)) |
64 | |
65 | |
66 | #define alloc_type(t) \ |
67 | ({ t *alloc_tmp = malloc(sizeof(t)); \ |
68 | if (!alloc_tmp) \ |
69 | abort(); \ |
70 | alloc_tmp; }) |
71 | |
72 | |
73 | static void new_path(void) |
74 | { |
75 | struct path *new; |
76 | |
77 | new = alloc_type(struct path); |
78 | if (paths) |
79 | curr_path->next = new; |
80 | else |
81 | paths = new; |
82 | curr_path = new; |
83 | new->segments = NULL; |
84 | new->next = NULL; |
85 | next_seg = &new->segments; |
86 | } |
87 | |
88 | |
89 | static void process_file(FILE *file) |
90 | { |
91 | int lineno = 0; |
92 | char buf[1024]; |
93 | double x, y, z; |
94 | int n; |
95 | struct segment *seg; |
96 | |
97 | new_path(); |
98 | while (fgets(buf, sizeof(buf), file)) { |
99 | lineno++; |
100 | if (*buf == '!' || *buf == '#') |
101 | continue; |
102 | n = sscanf(buf, "%lf %lf %lf\n", &x, &y, &z); |
103 | switch (n) { |
104 | case -1: |
105 | if (curr_path->segments) |
106 | new_path(); |
107 | continue; |
108 | case 2: |
109 | z = 0; |
110 | /* fall through */ |
111 | case 3: |
112 | break; |
113 | default: |
114 | fprintf(stderr, "invalid data at line %d\n", lineno); |
115 | exit(1); |
116 | } |
117 | seg = alloc_type(struct segment); |
118 | seg->x = x; |
119 | seg->y = y; |
120 | seg->z = z; |
121 | seg->next = NULL; |
122 | *next_seg = seg; |
123 | next_seg = &seg->next; |
124 | if (z_max < z) |
125 | z_max = z; |
126 | } |
127 | } |
128 | |
129 | |
130 | static void output_paths(double z_clear, double xy_speed, double z_speed) |
131 | { |
132 | const struct path *path; |
133 | const struct segment *seg; |
134 | double x = 0, y = 0, z = 0; |
135 | double d, s = 0, t = 0; |
136 | double txy, tz; |
137 | |
138 | printf("IN;!MC1;PA\n"); |
139 | printf("!ZO%d;!PZ0,%d;PU\n", units(z_max), units(z_clear)); |
140 | printf("VS%f;!VZ%f\n", xy_speed, z_speed); |
141 | |
142 | for (path = paths; path; path = path->next) { |
143 | seg = path->segments; |
144 | if (!seg) |
145 | continue; |
146 | printf("!PZ%d,%d;PA%d,%d;PD\n", |
147 | units(seg->z-z_max), units(z_clear), |
148 | units(seg->x), units(seg->y)); |
149 | d = hypot(x-seg->x, y-seg->y); |
150 | s += d; |
151 | s += z-z_max; |
152 | t += d/V_MAX; |
153 | t += (z-z_max)/z_speed; |
154 | x = seg->x; |
155 | y = seg->y; |
156 | z = seg->z; |
157 | seg = seg->next; |
158 | while (seg) { |
159 | printf("!ZZ%d,%d,%d\n", units(seg->x), units(seg->y), |
160 | units((seg->z-z_max)*z_scale)); |
161 | d = hypot(x-seg->x, y-seg->y); |
162 | txy = d/xy_speed; |
163 | tz = fabs(z-seg->z)/z_speed; |
164 | t += txy > tz ? txy : tz; |
165 | d = hypot(d, z-seg->z); |
166 | s += d; |
167 | x = seg->x; |
168 | y = seg->y; |
169 | z = seg->z; |
170 | seg = seg->next; |
171 | } |
172 | printf("PU\n"); |
173 | d = z_max+z_clear-z; |
174 | s += d; |
175 | t += d/V_MAX; |
176 | z = z_max+z_clear; |
177 | } |
178 | |
179 | printf("!MC0;!ZO0;!PZ0,0;PU0,0;IN\n"); |
180 | d = -z+hypot(x, y); |
181 | s += d; |
182 | t += d/V_MAX; |
183 | fprintf(stderr, "Distance %.1f mm, time %.1f s\n", s, t); |
184 | } |
185 | |
186 | |
187 | static void usage(const char *name) |
188 | { |
189 | fprintf(stderr, |
190 | "usage: %s [-s z_scale] z_clear[mm] xy_speed z_speed [file]\n\n" |
191 | " -s z_scale scale Z axis by z_scale (default: 1.0)\n" |
192 | " z_clear clearance above the highest peak, in mm (must be > 0)\n" |
193 | " Unit (\"mm\") can optionally be specified\n" |
194 | " xy_speed cutting speed, in mm/s\n" |
195 | " z_speed vertical speed when lowering the pen, in mm/s\n" |
196 | , name); |
197 | exit(1); |
198 | } |
199 | |
200 | |
201 | int main(int argc, char *const *argv) |
202 | { |
203 | FILE *file; |
204 | char *end; |
205 | int i; |
206 | double p[3]; |
207 | int c; |
208 | |
209 | while ((c = getopt(argc, argv, "s:")) != EOF) |
210 | switch (c) { |
211 | case 's': |
212 | z_scale = strtod(optarg &end); |
213 | if (*end) |
214 | usage(*argv); |
215 | break; |
216 | default: |
217 | usage(*argv); |
218 | } |
219 | |
220 | switch (argc - optind) { |
221 | case 3: |
222 | file = stdin; |
223 | break; |
224 | case 4: |
225 | file = fopen(argv[optind + 3], "r"); |
226 | if (!file) { |
227 | perror(argv[optind + 3]); |
228 | return 1; |
229 | } |
230 | break; |
231 | default: |
232 | usage(*argv); |
233 | } |
234 | for (i = 0; i != 3; i++) { |
235 | p[i] = strtod(argv[optind + i], &end); |
236 | /* |
237 | * Allow the clearance to have a unit, for consistency in |
238 | * mkmk-simple |
239 | */ |
240 | if (!i && *end && !strcmp(end, "mm")) |
241 | continue; |
242 | if (*end || p[i] <= 0) |
243 | usage(*argv); |
244 | } |
245 | |
246 | process_file(file); |
247 | output_paths(p[0], p[1], p[2]); |
248 | |
249 | if (ferror(stdout)) |
250 | perror("stdout"); |
251 | if (fclose(stdout) == EOF) |
252 | perror("stdout"); |
253 | |
254 | return 0; |
255 | } |
256 |
Branches:
master