Update source references
[survex.git] / src / cad3d.c
blob1baadce93b2589738072c99a3ec4eaffe2b60d98
1 /* cad3d.c
2 * Converts a .3d file to CAD-like formats (DXF, Skencil, SVG) and also Compass
3 * PLT.
5 * Also useful as an example of how to use the img code in your own programs
6 */
8 /* Copyright (C) 1994-2004,2008,2010,2011,2013,2014 Olly Betts
9 * Copyright (C) 2004 John Pybus (SVG Output code)
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 /* #define DEBUG_CAD3D */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
32 #include <float.h>
33 #include <math.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
38 #include "cmdline.h"
39 #include "debug.h"
40 #include "filename.h"
41 #include "hash.h"
42 #include "img_hosted.h"
43 #include "message.h"
44 #include "str.h"
45 #include "useful.h"
47 /* default values - can be overridden with --text-height and --marker-size
48 * respectively */
49 #define TEXT_HEIGHT 0.6
50 #define MARKER_SIZE 0.8
52 #define GRID_SPACING 100
54 #define POINTS_PER_INCH 72.0
55 #define POINTS_PER_MM (POINTS_PER_INCH / MM_PER_INCH)
57 #define SQRT_2 1.41421356237309504880168872420969
59 #define LEGS 1
60 #define STNS 2
61 #define LABELS 4
63 /* default to DXF */
64 #define FMT_DEFAULT FMT_DXF
66 static FILE *fh;
68 /* bounds */
69 static double min_x, min_y, min_z, max_x, max_y, max_z;
71 static double text_height; /* for station labels */
72 static double marker_size; /* for station markers */
73 static double grid; /* grid spacing (or 0 for no grid) */
74 static double scale = 500.0;
75 static double factor;
76 static const char *unit = "mm";
78 static img *pimg;
79 static const char *survey = NULL;
81 static void
82 dxf_header(void)
84 fprintf(fh, "0\nSECTION\n"
85 "2\nHEADER\n");
86 fprintf(fh, "9\n$EXTMIN\n"); /* lower left corner of drawing */
87 fprintf(fh, "10\n%#-.6f\n", min_x); /* x */
88 fprintf(fh, "20\n%#-.6f\n", min_y); /* y */
89 fprintf(fh, "30\n%#-.6f\n", min_z); /* min z */
90 fprintf(fh, "9\n$EXTMAX\n"); /* upper right corner of drawing */
91 fprintf(fh, "10\n%#-.6f\n", max_x); /* x */
92 fprintf(fh, "20\n%#-.6f\n", max_y); /* y */
93 fprintf(fh, "30\n%#-.6f\n", max_z); /* max z */
94 fprintf(fh, "9\n$PDMODE\n70\n3\n"); /* marker style as CROSS */
95 fprintf(fh, "9\n$PDSIZE\n40\n%6.2f\n", marker_size); /* marker size */
96 fprintf(fh, "0\nENDSEC\n");
98 fprintf(fh, "0\nSECTION\n"
99 "2\nTABLES\n");
100 fprintf(fh, "0\nTABLE\n" /* Define CONTINUOUS and DASHED line types. */
101 "2\nLTYPE\n"
102 "70\n10\n"
103 "0\nLTYPE\n"
104 "2\nCONTINUOUS\n"
105 "70\n64\n"
106 "3\nContinuous\n"
107 "72\n65\n"
108 "73\n0\n"
109 "40\n0.0\n"
110 "0\nLTYPE\n"
111 "2\nDASHED\n"
112 "70\n64\n"
113 "3\nDashed\n"
114 "72\n65\n"
115 "73\n2\n"
116 "40\n2.5\n"
117 "49\n1.25\n"
118 "49\n-1.25\n"
119 "0\nENDTAB\n");
120 fprintf(fh, "0\nTABLE\n"
121 "2\nLAYER\n");
122 fprintf(fh, "70\n10\n"); /* max # off layers in this DXF file : 10 */
123 /* First Layer: CentreLine */
124 fprintf(fh, "0\nLAYER\n2\nCentreLine\n");
125 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
126 fprintf(fh, "62\n5\n"); /* color: kept the same used by SpeleoGen */
127 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
128 /* Next Layer: Stations */
129 fprintf(fh, "0\nLAYER\n2\nStations\n");
130 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
131 fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
132 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
133 /* Next Layer: Labels */
134 fprintf(fh, "0\nLAYER\n2\nLabels\n");
135 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
136 fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
137 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
138 /* Next Layer: Surface */
139 fprintf(fh, "0\nLAYER\n2\nSurface\n");
140 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
141 fprintf(fh, "62\n5\n"); /* color */
142 fprintf(fh, "6\nDASHED\n"); /* linetype */
143 /* Next Layer: SurfaceStations */
144 fprintf(fh, "0\nLAYER\n2\nSurfaceStations\n");
145 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
146 fprintf(fh, "62\n7\n"); /* color */
147 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
148 /* Next Layer: SurfaceLabels */
149 fprintf(fh, "0\nLAYER\n2\nSurfaceLabels\n");
150 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
151 fprintf(fh, "62\n7\n"); /* color */
152 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
153 if (grid > 0) {
154 /* Next Layer: Grid */
155 fprintf(fh, "0\nLAYER\n2\nGrid\n");
156 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
157 fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
158 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
160 fprintf(fh, "0\nENDTAB\n"
161 "0\nENDSEC\n");
163 fprintf(fh, "0\nSECTION\n"
164 "2\nENTITIES\n");
166 if (grid > 0) {
167 double x, y;
168 x = floor(min_x / grid) * grid + grid;
169 y = floor(min_y / grid) * grid + grid;
170 #ifdef DEBUG_CAD3D
171 printf("x_min: %f y_min: %f\n", x, y);
172 #endif
173 while (x < max_x) {
174 /* horizontal line */
175 fprintf(fh, "0\nLINE\n");
176 fprintf(fh, "8\nGrid\n"); /* Layer */
177 fprintf(fh, "10\n%6.2f\n", x);
178 fprintf(fh, "20\n%6.2f\n", min_y);
179 fprintf(fh, "30\n0\n");
180 fprintf(fh, "11\n%6.2f\n", x);
181 fprintf(fh, "21\n%6.2f\n", max_y);
182 fprintf(fh, "31\n0\n");
183 x += grid;
185 while (y < max_y) {
186 /* vertical line */
187 fprintf(fh, "0\nLINE\n");
188 fprintf(fh, "8\nGrid\n"); /* Layer */
189 fprintf(fh, "10\n%6.2f\n", min_x);
190 fprintf(fh, "20\n%6.2f\n", y);
191 fprintf(fh, "30\n0\n");
192 fprintf(fh, "11\n%6.2f\n", max_x);
193 fprintf(fh, "21\n%6.2f\n", y);
194 fprintf(fh, "31\n0\n");
195 y += grid;
200 static void
201 dxf_start_pass(int layer)
203 (void)layer;
206 static void
207 dxf_move(const img_point *p)
209 (void)p; /* unused */
212 static void
213 dxf_line(const img_point *p1, const img_point *p, bool fSurface)
215 fprintf(fh, "0\nLINE\n");
216 fprintf(fh, fSurface ? "8\nSurface\n" : "8\nCentreLine\n"); /* Layer */
217 fprintf(fh, "10\n%6.2f\n", p1->x);
218 fprintf(fh, "20\n%6.2f\n", p1->y);
219 fprintf(fh, "30\n%6.2f\n", p1->z);
220 fprintf(fh, "11\n%6.2f\n", p->x);
221 fprintf(fh, "21\n%6.2f\n", p->y);
222 fprintf(fh, "31\n%6.2f\n", p->z);
225 static void
226 dxf_label(const img_point *p, const char *s, bool fSurface)
228 /* write station label to dxf file */
229 fprintf(fh, "0\nTEXT\n");
230 fprintf(fh, fSurface ? "8\nSurfaceLabels\n" : "8\nLabels\n"); /* Layer */
231 fprintf(fh, "10\n%6.2f\n", p->x);
232 fprintf(fh, "20\n%6.2f\n", p->y);
233 fprintf(fh, "30\n%6.2f\n", p->z);
234 fprintf(fh, "40\n%6.2f\n", text_height);
235 fprintf(fh, "1\n%s\n", s);
238 static void
239 dxf_cross(const img_point *p, bool fSurface)
241 /* write station marker to dxf file */
242 fprintf(fh, "0\nPOINT\n");
243 fprintf(fh, fSurface ? "8\nSurfaceStations\n" : "8\nStations\n"); /* Layer */
244 fprintf(fh, "10\n%6.2f\n", p->x);
245 fprintf(fh, "20\n%6.2f\n", p->y);
246 fprintf(fh, "30\n%6.2f\n", p->z);
249 static void
250 dxf_footer(void)
252 fprintf(fh, "000\nENDSEC\n");
253 fprintf(fh, "000\nEOF\n");
256 static void
257 skencil_header(void)
259 fprintf(fh, "##Sketch 1 2\n"); /* File format version */
260 fprintf(fh, "document()\n");
261 fprintf(fh, "layout((%.3f,%.3f),0)\n",
262 (max_x - min_x) * factor, (max_y - min_y) * factor);
265 static const char *layer_names[] = {
266 NULL,
267 "Legs",
268 "Stations",
269 NULL,
270 "Labels"
273 static void
274 skencil_start_pass(int layer)
276 fprintf(fh, "layer('%s',1,1,0,0,(0,0,0))\n", layer_names[layer]);
279 static void
280 skencil_move(const img_point *p)
282 fprintf(fh, "b()\n");
283 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n", p->x * factor, p->y * factor, 0.0);
286 static void
287 skencil_line(const img_point *p1, const img_point *p, bool fSurface)
289 (void)fSurface; /* unused */
290 (void)p1; /* unused */
291 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n", p->x * factor, p->y * factor, 0.0);
294 static void
295 skencil_label(const img_point *p, const char *s, bool fSurface)
297 (void)fSurface; /* unused */
298 fprintf(fh, "fp((0,0,0))\n");
299 fprintf(fh, "le()\n");
300 fprintf(fh, "Fn('Times-Roman')\n");
301 fprintf(fh, "Fs(5)\n");
302 fprintf(fh, "txt('");
303 while (*s) {
304 int ch = *s++;
305 if (ch == '\'' || ch == '\\') PUTC('\\', fh);
306 PUTC(ch, fh);
308 fprintf(fh, "',(%.3f,%.3f))\n", p->x * factor, p->y * factor);
311 static void
312 skencil_cross(const img_point *p, bool fSurface)
314 (void)fSurface; /* unused */
315 fprintf(fh, "b()\n");
316 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
317 p->x * factor - marker_size, p->y * factor - marker_size, 0.0);
318 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
319 p->x * factor + marker_size, p->y * factor + marker_size, 0.0);
320 fprintf(fh, "bn()\n");
321 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
322 p->x * factor + marker_size, p->y * factor - marker_size, 0.0);
323 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
324 p->x * factor - marker_size, p->y * factor + marker_size, 0.0);
327 static void
328 skencil_footer(void)
330 fprintf(fh, "guidelayer('Guide Lines',1,0,0,1,(0,0,1))\n");
331 if (grid) {
332 fprintf(fh, "grid((0,0,%.3f,%.3f),1,(0,0,1),'Grid')\n",
333 grid * factor, grid * factor);
337 typedef struct point {
338 img_point p;
339 const char *label;
340 struct point *next;
341 } point;
343 #define HTAB_SIZE 0x2000
345 static point **htab;
347 static void
348 set_name_(const img_point *p, const char *s, int copy)
350 int hash;
351 point *pt;
352 union {
353 char data[sizeof(int) * 3];
354 int x[3];
355 } u;
357 u.x[0] = (int)(p->x * 100);
358 u.x[1] = (int)(p->y * 100);
359 u.x[2] = (int)(p->z * 100);
360 hash = (hash_data(u.data, sizeof(int) * 3) & (HTAB_SIZE - 1));
361 for (pt = htab[hash]; pt; pt = pt->next) {
362 if (pt->p.x == p->x && pt->p.y == p->y && pt->p.z == p->z) {
363 /* already got name for these coordinates */
364 /* FIXME: what about multiple names for the same station? */
365 if (!copy)
366 osfree((char*)s);
367 return;
371 pt = osnew(point);
372 if (copy)
373 s = osstrdup(s);
374 pt->label = s;
375 pt->p = *p;
376 pt->next = htab[hash];
377 htab[hash] = pt;
380 static void
381 set_name(const img_point *p, const char *s)
383 set_name_(p, s, 0);
386 static void
387 set_name_copy(const img_point *p, const char *s)
389 set_name_(p, s, 1);
392 static const char *
393 find_name(const img_point *p)
395 int hash;
396 point *pt;
397 union {
398 char data[sizeof(int) * 3];
399 int x[3];
400 } u;
401 SVX_ASSERT(p);
403 u.x[0] = (int)(p->x * 100);
404 u.x[1] = (int)(p->y * 100);
405 u.x[2] = (int)(p->z * 100);
406 hash = (hash_data(u.data, sizeof(int) * 3) & (HTAB_SIZE - 1));
407 for (pt = htab[hash]; pt; pt = pt->next) {
408 if (pt->p.x == p->x && pt->p.y == p->y && pt->p.z == p->z)
409 return pt->label;
411 return "?";
414 static bool to_close = 0;
415 static bool close_g = 0;
417 static void
418 svg_header(void)
420 size_t i;
421 htab = osmalloc(HTAB_SIZE * ossizeof(point *));
422 for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
423 fprintf(fh, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
424 fprintf(fh, "<svg width=\"%.3f%s\" height=\"%.3f%s\""
425 " viewBox=\"0 0 %0.3f %0.3f\">\n",
426 (max_x - min_x) * factor, unit, (max_y - min_y) * factor, unit,
427 (max_x - min_x) * factor, (max_y - min_y) * factor );
428 fprintf(fh, "<g transform=\"translate(%.3f %.3f)\">\n",
429 min_x * -factor, max_y * factor);
430 to_close = 0;
431 close_g = 0;
434 static void
435 svg_start_pass(int layer)
437 if (to_close) {
438 fprintf(fh, "\"/>\n");
439 to_close = 0;
441 if (close_g) {
442 fprintf(fh, "</g>\n");
444 close_g = 1;
446 fprintf(fh, "<g id=\"%s\"", layer_names[layer]);
447 if (layer & LEGS)
448 fprintf(fh, " style=\"stroke:black;fill:none;stroke-width:0.4\"");
449 else if (layer & STNS)
450 fprintf(fh, " style=\"stroke:black;fill:none;stroke-width:0.05\"");
451 else if (layer & LABELS)
452 fprintf(fh, " style=\"font-size:%.3f\"", text_height);
453 fprintf(fh, ">\n");
456 static void
457 svg_move(const img_point *p)
459 if (to_close) {
460 fprintf(fh, "\"/>\n");
462 fprintf(fh, "<path d=\"M%.3f %.3f", p->x * factor, p->y * -factor);
463 to_close = 1;
466 static void
467 svg_line(const img_point *p1, const img_point *p, bool fSurface)
469 (void)fSurface; /* unused */
470 (void)p1; /* unused */
471 fprintf(fh, "L%.3f %.3f", p->x * factor, p->y * -factor);
472 to_close = 1;
475 static void
476 svg_label(const img_point *p, const char *s, bool fSurface)
478 (void)fSurface; /* unused */
479 fprintf(fh, "<text transform=\"translate(%.3f %.3f)\">",
480 p->x * factor, p->y * -factor);
481 fputs(s, fh);
482 fputs("</text>\n", fh);
483 set_name_copy(p, s);
486 static void
487 svg_cross(const img_point *p, bool fSurface)
489 (void)fSurface; /* unused */
490 fprintf(fh, "<circle id=\"%s\" cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\"/>\n",
491 find_name(p), p->x * factor, p->y * -factor, marker_size * SQRT_2);
492 fprintf(fh, "<path d=\"M%.3f %.3fL%.3f %.3fM%.3f %.3fL%.3f %.3f\"/>\n",
493 p->x * factor - marker_size, p->y * -factor - marker_size,
494 p->x * factor + marker_size, p->y * -factor + marker_size,
495 p->x * factor + marker_size, p->y * -factor - marker_size,
496 p->x * factor - marker_size, p->y * -factor + marker_size);
499 static void
500 svg_footer(void)
502 if (to_close) {
503 fprintf(fh, "\"/>\n");
504 to_close = 0;
506 if (close_g) {
507 fprintf(fh, "</g>\n");
509 close_g = 1;
510 fprintf(fh, "</g>\n</svg>");
513 static void
514 plt_header(void)
516 size_t i;
517 htab = osmalloc(HTAB_SIZE * ossizeof(point *));
518 for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
519 /* Survex is E, N, Alt - PLT file is N, E, Alt */
520 fprintf(fh, "Z %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
521 min_y / METRES_PER_FOOT, max_y / METRES_PER_FOOT,
522 min_x / METRES_PER_FOOT, max_x / METRES_PER_FOOT,
523 min_z / METRES_PER_FOOT, max_z / METRES_PER_FOOT);
524 fprintf(fh, "N%s D 1 1 1 C%s\r\n", survey ? survey : "X", pimg->title);
527 static void
528 plt_start_pass(int layer)
530 (void)layer;
533 static void
534 plt_move(const img_point *p)
536 /* Survex is E, N, Alt - PLT file is N, E, Alt */
537 fprintf(fh, "M %.3f %.3f %.3f ",
538 p->y / METRES_PER_FOOT, p->x / METRES_PER_FOOT, p->z / METRES_PER_FOOT);
539 /* dummy passage dimensions are required to avoid compass bug */
540 fprintf(fh, "S%s P -9 -9 -9 -9\r\n", find_name(p));
543 static void
544 plt_line(const img_point *p1, const img_point *p, bool fSurface)
546 (void)fSurface; /* unused */
547 (void)p1; /* unused */
548 /* Survex is E, N, Alt - PLT file is N, E, Alt */
549 fprintf(fh, "D %.3f %.3f %.3f ",
550 p->y / METRES_PER_FOOT, p->x / METRES_PER_FOOT, p->z / METRES_PER_FOOT);
551 /* dummy passage dimensions are required to avoid compass bug */
552 fprintf(fh, "S%s P -9 -9 -9 -9\r\n", find_name(p));
555 static void
556 plt_label(const img_point *p, const char *s, bool fSurface)
558 const char * q;
559 char * escaped = NULL;
560 int elen;
561 (void)fSurface; /* unused */
562 /* PLT format can't handle spaces or control characters, so escape them
563 * like in URLs (an arbitrary choice of escaping, but at least a familiar
564 * one and % isn't likely to occur in station names).
566 for (q = s; *q; ++q) {
567 unsigned char ch = *q;
568 if (ch <= ' ' || ch == '%') {
569 s_catlen(&escaped, &elen, s, q - s);
570 s_catchar(&escaped, &elen, '%');
571 s_catchar(&escaped, &elen, "0123456789abcdef"[ch >> 4]);
572 s_catchar(&escaped, &elen, "0123456789abcdef"[ch & 0x0f]);
573 s = q + 1;
576 if (escaped) {
577 s_catlen(&escaped, &elen, s, q - s);
578 set_name(p, escaped);
579 return;
581 set_name_copy(p, s);
584 static void
585 plt_cross(const img_point *p, bool fSurface)
587 (void)fSurface; /* unused */
588 (void)p; /* unused */
591 static void
592 plt_footer(void)
594 /* Survex is E, N, Alt - PLT file is N, E, Alt */
595 fprintf(fh, "X %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
596 min_y / METRES_PER_FOOT, max_y / METRES_PER_FOOT,
597 min_x / METRES_PER_FOOT, max_x / METRES_PER_FOOT,
598 min_z / METRES_PER_FOOT, max_z / METRES_PER_FOOT);
599 /* Yucky DOS "end of textfile" marker */
600 PUTC('\x1a', fh);
603 static int dxf_passes[] = { LEGS|STNS|LABELS, 0 };
604 static int skencil_passes[] = { LEGS, STNS, LABELS, 0 };
605 static int plt_passes[] = { LABELS, LEGS, 0 };
606 static int svg_passes[] = { LEGS, LABELS, STNS, 0 };
609 main(int argc, char **argv)
611 char *fnm_3d, *fnm_out;
612 unsigned char labels, crosses, legs;
613 int item;
614 int fSeenMove = 0;
615 img_point p, p1;
616 int elevation = 0;
617 double elev_angle = 0;
618 double s = 0, c = 0;
619 enum { FMT_DXF = 0, FMT_SK, FMT_PLT, FMT_SVG, FMT_AUTO } format;
620 static const char *extensions[] = { "dxf", "sk", "plt", "svg" };
621 int *pass;
623 void (*header)(void);
624 void (*start_pass)(int);
625 void (*move)(const img_point *);
626 void (*line)(const img_point *, const img_point *, bool);
627 void (*label)(const img_point *, const char *, bool);
628 void (*cross)(const img_point *, bool);
629 void (*footer)(void);
630 const char *mode = "w"; /* default to text output */
632 /* TRANSLATE */
633 static const struct option long_opts[] = {
634 /* const char *name; int has_arg (0 no_argument, 1 required, 2 options_*); int *flag; int val */
635 {"survey", required_argument, 0, 's'},
636 {"no-crosses", no_argument, 0, 'c'},
637 {"no-station-names", no_argument, 0, 'n'},
638 {"no-legs", no_argument, 0, 'l'},
639 {"grid", optional_argument, 0, 'g'},
640 {"text-height", required_argument, 0, 't'},
641 {"marker-size", required_argument, 0, 'm'},
642 {"elevation", required_argument, 0, 'e'},
643 {"reduction", required_argument, 0, 'r'},
644 {"dxf", no_argument, 0, 'D'},
645 {"skencil", no_argument, 0, 'S'},
646 {"plt", no_argument, 0, 'P'},
647 {"svg", no_argument, 0, 'V'},
648 {"help", no_argument, 0, HLP_HELP},
649 {"version", no_argument, 0, HLP_VERSION},
650 /* Old name for --skencil: */
651 {"sketch", no_argument, 0, 'S'},
652 {0,0,0,0}
655 #define short_opts "s:cnlg::t:m:e:r:DSPV"
657 /* TRANSLATE */
658 static struct help_msg help[] = {
659 /* <-- */
660 {HLP_ENCODELONG(0), /*only load the sub-survey with this prefix*/199, 0},
661 {HLP_ENCODELONG(1), /*do not generate station markers*/100, 0},
662 {HLP_ENCODELONG(2), /*do not generate station labels*/101, 0},
663 {HLP_ENCODELONG(3), /*do not generate survey legs*/102, 0},
664 {HLP_ENCODELONG(4), /*generate grid (default %sm)*/148, STRING(GRID_SPACING)},
665 {HLP_ENCODELONG(5), /*station labels text height (default %s)*/149, STRING(TEXT_HEIGHT)},
666 {HLP_ENCODELONG(6), /*station marker size (default %s)*/152, STRING(MARKER_SIZE)},
667 {HLP_ENCODELONG(7), /*produce an elevation view*/103, 0},
668 {HLP_ENCODELONG(8), /*factor to scale down by (default %s)*/155, "500"},
669 {HLP_ENCODELONG(9), /*produce DXF output*/156, 0},
670 /* TRANSLATORS: "Skencil" is the name of a software package, so should not be
671 * translated. */
672 {HLP_ENCODELONG(10), /*produce Skencil output*/158, 0},
673 /* TRANSLATORS: "Compass" and "Carto" are the names of software packages,
674 * so should not be translated. */
675 {HLP_ENCODELONG(11), /*produce Compass PLT output for Carto*/159, 0},
676 {HLP_ENCODELONG(12), /*produce SVG output*/160, 0},
677 {0, 0, 0}
680 msg_init(argv);
682 /* Defaults */
683 crosses = 1;
684 labels = 1;
685 legs = 1;
686 grid = 0;
687 text_height = TEXT_HEIGHT;
688 marker_size = MARKER_SIZE;
689 format = FMT_AUTO;
691 cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 1, 2);
692 while (1) {
693 int opt = cmdline_getopt();
694 if (opt == EOF) break;
695 switch (opt) {
696 case 'e': /* Elevation */
697 elevation = 1;
698 elev_angle = cmdline_double_arg();
699 break;
700 case 'c': /* Crosses */
701 crosses = 0;
702 break;
703 case 'n': /* Labels */
704 labels = 0;
705 break;
706 case 'l': /* Legs */
707 legs = 0;
708 break;
709 case 'g': /* Grid */
710 if (optarg) {
711 grid = cmdline_double_arg();
712 } else {
713 grid = (double)GRID_SPACING;
715 break;
716 case 'r': /* Reduction factor */
717 scale = cmdline_double_arg();
718 break;
719 case 't': /* Text height */
720 text_height = cmdline_double_arg();
721 #ifdef DEBUG_CAD3D
722 printf("Text Height: “%s” input, converted to %6.2f\n", optarg, text_height);
723 #endif
724 break;
725 case 'm': /* Marker size */
726 marker_size = cmdline_double_arg();
727 #ifdef DEBUG_CAD3D
728 printf("Marker Size: “%s”, converted to %6.2f\n", optarg, marker_size);
729 #endif
730 break;
731 case 'D':
732 format = FMT_DXF;
733 break;
734 case 'S':
735 format = FMT_SK;
736 break;
737 case 'P':
738 format = FMT_PLT;
739 break;
740 case 'V':
741 format = FMT_SVG;
742 break;
743 case 's':
744 survey = optarg;
745 break;
746 #ifdef DEBUG_CAD3D
747 default:
748 printf("Internal Error: 'getopt' returned '%c' %d\n", opt, opt);
749 #endif
753 fnm_3d = argv[optind++];
754 if (argv[optind]) {
755 fnm_out = argv[optind];
756 if (format == FMT_AUTO) {
757 size_t i;
758 size_t len = strlen(fnm_out);
759 format = FMT_DEFAULT;
760 for (i = 0; i < FMT_AUTO; ++i) {
761 size_t l = strlen(extensions[i]);
762 if (len > l + 1 && fnm_out[len - l - 1] == FNM_SEP_EXT &&
763 strcasecmp(fnm_out + len - l, extensions[i]) == 0) {
764 format = i;
765 break;
769 } else {
770 char *baseleaf = baseleaf_from_fnm(fnm_3d);
771 if (format == FMT_AUTO) format = FMT_DEFAULT;
772 /* note : memory allocated by fnm_out gets leaked in this case... */
773 fnm_out = add_ext(baseleaf, extensions[format]);
774 osfree(baseleaf);
777 switch (format) {
778 case FMT_DXF:
779 header = dxf_header;
780 start_pass = dxf_start_pass;
781 move = dxf_move;
782 line = dxf_line;
783 label = dxf_label;
784 cross = dxf_cross;
785 footer = dxf_footer;
786 pass = dxf_passes;
787 break;
788 case FMT_SK:
789 header = skencil_header;
790 start_pass = skencil_start_pass;
791 move = skencil_move;
792 line = skencil_line;
793 label = skencil_label;
794 cross = skencil_cross;
795 footer = skencil_footer;
796 pass = skencil_passes;
797 factor = POINTS_PER_MM * 1000.0 / scale;
798 mode = "wb"; /* Binary file output */
799 break;
800 case FMT_PLT:
801 header = plt_header;
802 start_pass = plt_start_pass;
803 move = plt_move;
804 line = plt_line;
805 label = plt_label;
806 cross = plt_cross;
807 footer = plt_footer;
808 pass = plt_passes;
809 mode = "wb"; /* Binary file output */
810 break;
811 case FMT_SVG:
812 header = svg_header;
813 start_pass = svg_start_pass;
814 move = svg_move;
815 line = svg_line;
816 label = svg_label;
817 cross = svg_cross;
818 footer = svg_footer;
819 pass = svg_passes;
820 factor = 1000.0 / scale;
821 mode = "wb"; /* Binary file output */
822 break;
823 default:
824 exit(1);
827 pimg = img_open_survey(fnm_3d, survey);
828 if (!pimg) fatalerror(img_error2msg(img_error()), fnm_3d);
830 fh = safe_fopen(fnm_out, mode);
832 if (elevation) {
833 s = sin(rad(elev_angle));
834 c = cos(rad(elev_angle));
837 /* Get drawing corners */
838 min_x = min_y = min_z = HUGE_VAL;
839 max_x = max_y = max_z = -HUGE_VAL;
840 do {
841 item = img_read_item(pimg, &p);
843 if (elevation) {
844 double xnew = p.x * c - p.y * s;
845 double znew = - p.x * s - p.y * c;
846 p.y = p.z;
847 p.z = znew;
848 p.x = xnew;
851 switch (item) {
852 case img_MOVE: case img_LINE: case img_LABEL:
853 if (p.x < min_x) min_x = p.x;
854 if (p.x > max_x) max_x = p.x;
855 if (p.y < min_y) min_y = p.y;
856 if (p.y > max_y) max_y = p.y;
857 if (p.z < min_z) min_z = p.z;
858 if (p.z > max_z) max_z = p.z;
859 break;
861 } while (item != img_STOP);
863 if (grid > 0) {
864 min_x -= grid / 2;
865 max_x += grid / 2;
866 min_y -= grid / 2;
867 max_y += grid / 2;
870 /* handle empty file gracefully */
871 if (min_x > max_x) {
872 min_x = min_y = min_z = 0;
873 max_x = max_y = max_z = 0;
876 /* Header */
877 header();
879 p1.x = p1.y = p1.z = 0; /* avoid compiler warning */
881 while (*pass) {
882 if (((*pass & LEGS) && legs) ||
883 ((*pass & STNS) && crosses) ||
884 ((*pass & LABELS) && labels)) {
885 if (!img_rewind(pimg)) {
886 fatalerror(img_error2msg(img_error()), fnm_3d);
888 start_pass(*pass);
889 do {
890 item = img_read_item(pimg, &p);
892 if (format == FMT_SK) {
893 p.x -= min_x;
894 p.y -= min_y;
895 p.z -= min_z;
898 if (elevation) {
899 double xnew = p.x * c - p.y * s;
900 double znew = - p.x * s - p.y * c;
901 p.y = p.z;
902 p.z = znew;
903 p.x = xnew;
906 switch (item) {
907 case img_BAD:
908 img_close(pimg);
909 fatalerror(/*Bad 3d image file “%s”*/106, fnm_3d);
910 break;
911 case img_LINE:
912 #ifdef DEBUG_CAD3D
913 printf("line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
914 #endif
915 if (!fSeenMove) {
916 #ifdef DEBUG_CAD3D
917 printf("Something is wrong -- img_LINE before any img_MOVE!\n");
918 #endif
919 img_close(pimg);
920 fatalerror(/*Bad 3d image file “%s”*/106, fnm_3d);
922 if ((*pass & LEGS) && legs)
923 line(&p1, &p, pimg->flags & img_FLAG_SURFACE);
924 p1 = p;
925 break;
926 case img_MOVE:
927 #ifdef DEBUG_CAD3D
928 printf("move to %9.2f %9.2f %9.2f\n",x,y,z);
929 #endif
930 if ((*pass & LEGS) && legs) move(&p);
931 fSeenMove = 1;
932 p1 = p;
933 break;
934 case img_LABEL:
935 #ifdef DEBUG_CAD3D
936 printf("label “%s” at %9.2f %9.2f %9.2f\n",pimg->label,x,y,z);
937 #endif
938 /* Use !UNDERGROUND as the criterion - we want stations where
939 * a surface and underground survey meet to be in the
940 * underground layer */
941 if ((*pass & LABELS) && labels)
942 label(&p, pimg->label,
943 !(pimg->flags & img_SFLAG_UNDERGROUND));
944 if ((*pass & STNS) && crosses)
945 cross(&p, !(pimg->flags & img_SFLAG_UNDERGROUND));
946 break;
947 #ifdef DEBUG_CAD3D
948 case img_STOP:
949 printf("stop\n");
950 break;
951 default:
952 printf("other info tag (code %d) ignored\n", item);
953 #endif
955 } while (item != img_STOP);
957 pass++;
959 img_close(pimg);
960 footer();
961 safe_fclose(fh);
962 return 0;