Show duplicate legs as dashed lines
[survex.git] / src / export.cc
blob0d6a0b8575d123588cf7fe69adc154e83ccb462c
1 /* export.cc
2 * Export to CAD-like formats (DXF, Skencil, SVG, EPS) and also Compass PLT.
3 */
5 /* Copyright (C) 1994-2004,2005,2006,2008,2010,2011,2012,2013,2014,2015,2016 Olly Betts
6 * Copyright (C) 2004 John Pybus (SVG Output code)
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 /* #define DEBUG_CAD3D */
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
29 #include "export.h"
31 #include "wx.h"
32 #include <wx/utils.h>
33 #include "exportfilter.h"
34 #include "gpx.h"
35 #include "hpgl.h"
36 #include "json.h"
37 #include "kml.h"
38 #include "mainfrm.h"
39 #include "pos.h"
41 #include <float.h>
42 #include <locale.h>
43 #include <math.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <time.h>
49 #if defined(HAVE_GETPWUID) && !defined(__DJGPP__)
50 # include <pwd.h>
51 # include <sys/types.h>
52 # include <unistd.h>
53 #endif
55 #include <utility>
56 #include <vector>
58 #include "cmdline.h"
59 #include "debug.h"
60 #include "filename.h"
61 #include "hash.h"
62 #include "img_hosted.h"
63 #include "message.h"
64 #include "useful.h"
66 #define POINTS_PER_INCH 72.0
67 #define POINTS_PER_MM (POINTS_PER_INCH / MM_PER_INCH)
69 #define SQRT_2 1.41421356237309504880168872420969
71 static void
72 html_escape(FILE *fh, const char *s)
74 while (*s) {
75 switch (*s) {
76 case '<':
77 fputs("&lt;", fh);
78 break;
79 case '>':
80 fputs("&gt;", fh);
81 break;
82 case '&':
83 fputs("&amp;", fh);
84 break;
85 default:
86 PUTC(*s, fh);
88 ++s;
92 // Used by Skencil and SVG.
93 static const char *layer_name(int mask) {
94 switch (mask) {
95 case LEGS: case LEGS|SURF:
96 return "Legs";
97 case SURF:
98 return "Surface";
99 case STNS:
100 return "Stations";
101 case LABELS:
102 return "Labels";
103 case XSECT:
104 return "Cross-sections";
105 case WALL1: case WALL2: case WALLS:
106 return "Walls";
107 case PASG:
108 return "Passages";
110 return "";
113 static double marker_size; /* for station markers */
114 static double grid; /* grid spacing (or 0 for no grid) */
116 const int *
117 ExportFilter::passes() const
119 static const int default_passes[] = { LEGS|SURF|STNS|LABELS, 0 };
120 return default_passes;
123 class DXF : public ExportFilter {
124 const char * to_close;
125 /* for station labels */
126 double text_height;
127 char pending[1024];
129 public:
130 explicit DXF(double text_height_)
131 : to_close(0), text_height(text_height_) { pending[0] = '\0'; }
132 const int * passes() const;
133 bool fopen(const wxString& fnm_out);
134 void header(const char *, const char *, time_t,
135 double min_x, double min_y, double min_z,
136 double max_x, double max_y, double max_z);
137 void line(const img_point *, const img_point *, unsigned, bool);
138 void label(const img_point *, const char *, bool, int);
139 void cross(const img_point *, bool);
140 void xsect(const img_point *, double, double, double);
141 void wall(const img_point *, double, double);
142 void passage(const img_point *, double, double, double);
143 void tube_end();
144 void footer();
147 const int *
148 DXF::passes() const
150 static const int dxf_passes[] = {
151 PASG, XSECT, WALL1, WALL2, LEGS|SURF|STNS|LABELS, 0
153 return dxf_passes;
156 bool
157 DXF::fopen(const wxString& fnm_out)
159 // DXF gets written as text rather than binary.
160 fh = wxFopen(fnm_out.fn_str(), wxT("w"));
161 return (fh != NULL);
164 void
165 DXF::header(const char *, const char *, time_t,
166 double min_x, double min_y, double min_z,
167 double max_x, double max_y, double max_z)
169 fprintf(fh, "0\nSECTION\n"
170 "2\nHEADER\n");
171 fprintf(fh, "9\n$EXTMIN\n"); /* lower left corner of drawing */
172 fprintf(fh, "10\n%#-.6f\n", min_x); /* x */
173 fprintf(fh, "20\n%#-.6f\n", min_y); /* y */
174 fprintf(fh, "30\n%#-.6f\n", min_z); /* min z */
175 fprintf(fh, "9\n$EXTMAX\n"); /* upper right corner of drawing */
176 fprintf(fh, "10\n%#-.6f\n", max_x); /* x */
177 fprintf(fh, "20\n%#-.6f\n", max_y); /* y */
178 fprintf(fh, "30\n%#-.6f\n", max_z); /* max z */
179 fprintf(fh, "9\n$PDMODE\n70\n3\n"); /* marker style as CROSS */
180 fprintf(fh, "9\n$PDSIZE\n40\n%6.2f\n", marker_size); /* marker size */
181 fprintf(fh, "0\nENDSEC\n");
183 fprintf(fh, "0\nSECTION\n"
184 "2\nTABLES\n");
185 fprintf(fh, "0\nTABLE\n" /* Define CONTINUOUS and DASHED line types. */
186 "2\nLTYPE\n"
187 "70\n10\n"
188 "0\nLTYPE\n"
189 "2\nCONTINUOUS\n"
190 "70\n64\n"
191 "3\nContinuous\n"
192 "72\n65\n"
193 "73\n0\n"
194 "40\n0.0\n"
195 "0\nLTYPE\n"
196 "2\nDASHED\n"
197 "70\n64\n"
198 "3\nDashed\n"
199 "72\n65\n"
200 "73\n2\n"
201 "40\n2.5\n"
202 "49\n1.25\n"
203 "49\n-1.25\n"
204 "0\nENDTAB\n");
205 fprintf(fh, "0\nTABLE\n"
206 "2\nLAYER\n");
207 fprintf(fh, "70\n10\n"); /* max # off layers in this DXF file : 10 */
208 /* First Layer: CentreLine */
209 fprintf(fh, "0\nLAYER\n2\nCentreLine\n");
210 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
211 fprintf(fh, "62\n5\n"); /* color: kept the same used by SpeleoGen */
212 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
213 /* Next Layer: Stations */
214 fprintf(fh, "0\nLAYER\n2\nStations\n");
215 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
216 fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
217 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
218 /* Next Layer: Labels */
219 fprintf(fh, "0\nLAYER\n2\nLabels\n");
220 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
221 fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
222 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
223 /* Next Layer: Surface */
224 fprintf(fh, "0\nLAYER\n2\nSurface\n");
225 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
226 fprintf(fh, "62\n5\n"); /* color */
227 fprintf(fh, "6\nDASHED\n"); /* linetype */
228 /* Next Layer: SurfaceStations */
229 fprintf(fh, "0\nLAYER\n2\nSurfaceStations\n");
230 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
231 fprintf(fh, "62\n7\n"); /* color */
232 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
233 /* Next Layer: SurfaceLabels */
234 fprintf(fh, "0\nLAYER\n2\nSurfaceLabels\n");
235 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
236 fprintf(fh, "62\n7\n"); /* color */
237 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
238 if (grid > 0) {
239 /* Next Layer: Grid */
240 fprintf(fh, "0\nLAYER\n2\nGrid\n");
241 fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
242 fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
243 fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
245 fprintf(fh, "0\nENDTAB\n"
246 "0\nENDSEC\n");
248 fprintf(fh, "0\nSECTION\n"
249 "2\nENTITIES\n");
251 if (grid > 0) {
252 double x, y;
253 x = floor(min_x / grid) * grid + grid;
254 y = floor(min_y / grid) * grid + grid;
255 #ifdef DEBUG_CAD3D
256 printf("x_min: %f y_min: %f\n", x, y);
257 #endif
258 while (x < max_x) {
259 /* horizontal line */
260 fprintf(fh, "0\nLINE\n");
261 fprintf(fh, "8\nGrid\n"); /* Layer */
262 fprintf(fh, "10\n%6.2f\n", x);
263 fprintf(fh, "20\n%6.2f\n", min_y);
264 fprintf(fh, "30\n0\n");
265 fprintf(fh, "11\n%6.2f\n", x);
266 fprintf(fh, "21\n%6.2f\n", max_y);
267 fprintf(fh, "31\n0\n");
268 x += grid;
270 while (y < max_y) {
271 /* vertical line */
272 fprintf(fh, "0\nLINE\n");
273 fprintf(fh, "8\nGrid\n"); /* Layer */
274 fprintf(fh, "10\n%6.2f\n", min_x);
275 fprintf(fh, "20\n%6.2f\n", y);
276 fprintf(fh, "30\n0\n");
277 fprintf(fh, "11\n%6.2f\n", max_x);
278 fprintf(fh, "21\n%6.2f\n", y);
279 fprintf(fh, "31\n0\n");
280 y += grid;
285 void
286 DXF::line(const img_point *p1, const img_point *p, unsigned flags, bool fPendingMove)
288 bool fSurface = (flags & SURF);
289 (void)fPendingMove; /* unused */
290 fprintf(fh, "0\nLINE\n");
291 fprintf(fh, fSurface ? "8\nSurface\n" : "8\nCentreLine\n"); /* Layer */
292 fprintf(fh, "10\n%6.2f\n", p1->x);
293 fprintf(fh, "20\n%6.2f\n", p1->y);
294 fprintf(fh, "30\n%6.2f\n", p1->z);
295 fprintf(fh, "11\n%6.2f\n", p->x);
296 fprintf(fh, "21\n%6.2f\n", p->y);
297 fprintf(fh, "31\n%6.2f\n", p->z);
300 void
301 DXF::label(const img_point *p, const char *s, bool fSurface, int)
303 /* write station label to dxf file */
304 fprintf(fh, "0\nTEXT\n");
305 fprintf(fh, fSurface ? "8\nSurfaceLabels\n" : "8\nLabels\n"); /* Layer */
306 fprintf(fh, "10\n%6.2f\n", p->x);
307 fprintf(fh, "20\n%6.2f\n", p->y);
308 fprintf(fh, "30\n%6.2f\n", p->z);
309 fprintf(fh, "40\n%6.2f\n", text_height);
310 fprintf(fh, "1\n%s\n", s);
313 void
314 DXF::cross(const img_point *p, bool fSurface)
316 /* write station marker to dxf file */
317 fprintf(fh, "0\nPOINT\n");
318 fprintf(fh, fSurface ? "8\nSurfaceStations\n" : "8\nStations\n"); /* Layer */
319 fprintf(fh, "10\n%6.2f\n", p->x);
320 fprintf(fh, "20\n%6.2f\n", p->y);
321 fprintf(fh, "30\n%6.2f\n", p->z);
324 void
325 DXF::xsect(const img_point *p, double angle, double d1, double d2)
327 double s = sin(rad(angle));
328 double c = cos(rad(angle));
329 fprintf(fh, "0\nLINE\n");
330 fprintf(fh, "8\nCross-sections\n"); /* Layer */
331 fprintf(fh, "10\n%6.2f\n", p->x + c * d1);
332 fprintf(fh, "20\n%6.2f\n", p->y + s * d1);
333 fprintf(fh, "30\n%6.2f\n", p->z);
334 fprintf(fh, "11\n%6.2f\n", p->x - c * d2);
335 fprintf(fh, "21\n%6.2f\n", p->y - s * d2);
336 fprintf(fh, "31\n%6.2f\n", p->z);
339 void
340 DXF::wall(const img_point *p, double angle, double d)
342 if (!to_close) {
343 fprintf(fh, "0\nPOLYLINE\n");
344 fprintf(fh, "8\nWalls\n"); /* Layer */
345 fprintf(fh, "70\n0\n"); /* bit 0 == 0 => Open polyline */
346 to_close = "0\nSEQEND\n";
348 double s = sin(rad(angle));
349 double c = cos(rad(angle));
350 fprintf(fh, "0\nVERTEX\n");
351 fprintf(fh, "8\nWalls\n"); /* Layer */
352 fprintf(fh, "10\n%6.2f\n", p->x + c * d);
353 fprintf(fh, "20\n%6.2f\n", p->y + s * d);
354 fprintf(fh, "30\n%6.2f\n", p->z);
357 void
358 DXF::passage(const img_point *p, double angle, double d1, double d2)
360 fprintf(fh, "0\nSOLID\n");
361 fprintf(fh, "8\nPassages\n"); /* Layer */
362 double s = sin(rad(angle));
363 double c = cos(rad(angle));
364 double x1 = p->x + c * d1;
365 double y1 = p->y + s * d1;
366 double x2 = p->x - c * d2;
367 double y2 = p->y - s * d2;
368 if (*pending) {
369 fputs(pending, fh);
370 fprintf(fh, "12\n%6.2f\n22\n%6.2f\n32\n%6.2f\n"
371 "13\n%6.2f\n23\n%6.2f\n33\n%6.2f\n",
372 x1, y1, p->z,
373 x2, y2, p->z);
375 sprintf(pending, "10\n%6.2f\n20\n%6.2f\n30\n%6.2f\n"
376 "11\n%6.2f\n21\n%6.2f\n31\n%6.2f\n",
377 x1, y1, p->z,
378 x2, y2, p->z);
381 void
382 DXF::tube_end()
384 *pending = '\0';
385 if (to_close) {
386 fputs(to_close, fh);
387 to_close = NULL;
391 void
392 DXF::footer()
394 fprintf(fh, "000\nENDSEC\n");
395 fprintf(fh, "000\nEOF\n");
398 class Skencil : public ExportFilter {
399 double factor;
400 public:
401 explicit Skencil(double scale)
402 : factor(POINTS_PER_MM * 1000.0 / scale) { }
403 const int * passes() const;
404 void header(const char *, const char *, time_t,
405 double min_x, double min_y, double min_z,
406 double max_x, double max_y, double max_z);
407 void start_pass(int layer);
408 void line(const img_point *, const img_point *, unsigned, bool);
409 void label(const img_point *, const char *, bool, int);
410 void cross(const img_point *, bool);
411 void footer();
414 const int *
415 Skencil::passes() const
417 static const int skencil_passes[] = { LEGS|SURF, STNS, LABELS, 0 };
418 return skencil_passes;
421 void
422 Skencil::header(const char *, const char *, time_t,
423 double min_x, double min_y, double /*min_z*/,
424 double max_x, double max_y, double /*max_z*/)
426 fprintf(fh, "##Sketch 1 2\n"); /* File format version */
427 fprintf(fh, "document()\n");
428 fprintf(fh, "layout((%.3f,%.3f),0)\n",
429 (max_x - min_x) * factor, (max_y - min_y) * factor);
432 void
433 Skencil::start_pass(int layer)
435 fprintf(fh, "layer('%s',1,1,0,0,(0,0,0))\n", layer_name(layer));
438 void
439 Skencil::line(const img_point *p1, const img_point *p, unsigned flags, bool fPendingMove)
441 (void)flags; /* unused */
442 if (fPendingMove) {
443 fprintf(fh, "b()\n");
444 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n", p1->x * factor, p1->y * factor, 0.0);
446 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n", p->x * factor, p->y * factor, 0.0);
449 void
450 Skencil::label(const img_point *p, const char *s, bool fSurface, int)
452 (void)fSurface; /* unused */
453 fprintf(fh, "fp((0,0,0))\n");
454 fprintf(fh, "le()\n");
455 fprintf(fh, "Fn('Times-Roman')\n");
456 fprintf(fh, "Fs(5)\n");
457 fprintf(fh, "txt('");
458 while (*s) {
459 int ch = *s++;
460 if (ch == '\'' || ch == '\\') PUTC('\\', fh);
461 PUTC(ch, fh);
463 fprintf(fh, "',(%.3f,%.3f))\n", p->x * factor, p->y * factor);
466 void
467 Skencil::cross(const img_point *p, bool fSurface)
469 (void)fSurface; /* unused */
470 fprintf(fh, "b()\n");
471 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
472 p->x * factor - marker_size, p->y * factor - marker_size, 0.0);
473 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
474 p->x * factor + marker_size, p->y * factor + marker_size, 0.0);
475 fprintf(fh, "bn()\n");
476 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
477 p->x * factor + marker_size, p->y * factor - marker_size, 0.0);
478 fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
479 p->x * factor - marker_size, p->y * factor + marker_size, 0.0);
482 void
483 Skencil::footer(void)
485 fprintf(fh, "guidelayer('Guide Lines',1,0,0,1,(0,0,1))\n");
486 if (grid) {
487 fprintf(fh, "grid((0,0,%.3f,%.3f),1,(0,0,1),'Grid')\n",
488 grid * factor, grid * factor);
492 typedef struct point {
493 img_point p;
494 const char *label;
495 struct point *next;
496 } point;
498 #define HTAB_SIZE 0x2000
500 static point **htab;
502 static void
503 set_name(const img_point *p, const char *s)
505 int hash;
506 point *pt;
507 union {
508 char data[sizeof(int) * 3];
509 int x[3];
510 } u;
512 u.x[0] = (int)(p->x * 100);
513 u.x[1] = (int)(p->y * 100);
514 u.x[2] = (int)(p->z * 100);
515 hash = (hash_data(u.data, sizeof(int) * 3) & (HTAB_SIZE - 1));
516 for (pt = htab[hash]; pt; pt = pt->next) {
517 if (pt->p.x == p->x && pt->p.y == p->y && pt->p.z == p->z) {
518 /* already got name for these coordinates */
519 /* FIXME: what about multiple names for the same station? */
520 return;
524 pt = osnew(point);
525 pt->label = osstrdup(s);
526 pt->p = *p;
527 pt->next = htab[hash];
528 htab[hash] = pt;
530 return;
533 static const char *
534 find_name(const img_point *p)
536 int hash;
537 point *pt;
538 union {
539 char data[sizeof(int) * 3];
540 int x[3];
541 } u;
542 wxASSERT(p);
544 u.x[0] = (int)(p->x * 100);
545 u.x[1] = (int)(p->y * 100);
546 u.x[2] = (int)(p->z * 100);
547 hash = (hash_data(u.data, sizeof(int) * 3) & (HTAB_SIZE - 1));
548 for (pt = htab[hash]; pt; pt = pt->next) {
549 if (pt->p.x == p->x && pt->p.y == p->y && pt->p.z == p->z)
550 return pt->label;
552 return "?";
555 class SVG : public ExportFilter {
556 const char * to_close;
557 bool close_g;
558 double factor;
559 /* for station labels */
560 double text_height;
561 char pending[1024];
563 public:
564 SVG(double scale, double text_height_)
565 : to_close(NULL),
566 close_g(false),
567 factor(1000.0 / scale),
568 text_height(text_height_) {
569 pending[0] = '\0';
571 const int * passes() const;
572 void header(const char *, const char *, time_t,
573 double min_x, double min_y, double min_z,
574 double max_x, double max_y, double max_z);
575 void start_pass(int layer);
576 void line(const img_point *, const img_point *, unsigned, bool);
577 void label(const img_point *, const char *, bool, int);
578 void cross(const img_point *, bool);
579 void xsect(const img_point *, double, double, double);
580 void wall(const img_point *, double, double);
581 void passage(const img_point *, double, double, double);
582 void tube_end();
583 void footer();
586 const int *
587 SVG::passes() const
589 static const int svg_passes[] = {
590 PASG, LEGS|SURF, XSECT, WALL1, WALL2, LABELS, STNS, 0
592 return svg_passes;
595 void
596 SVG::header(const char * title, const char *, time_t,
597 double min_x, double min_y, double /*min_z*/,
598 double max_x, double max_y, double /*max_z*/)
600 const char *unit = "mm";
601 const double SVG_MARGIN = 5.0; // In units of "unit".
602 htab = (point **)osmalloc(HTAB_SIZE * ossizeof(point *));
603 for (size_t i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
604 fprintf(fh, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
605 double width = (max_x - min_x) * factor + SVG_MARGIN * 2;
606 double height = (max_y - min_y) * factor + SVG_MARGIN * 2;
607 fprintf(fh, "<svg version=\"1.1\" baseProfile=\"full\"\n"
608 "xmlns=\"http://www.w3.org/2000/svg\"\n"
609 "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
610 "xmlns:ev=\"http://www.w3.org/2001/xml-events\"\n"
611 "width=\"%.3f%s\" height=\"%.3f%s\"\n"
612 "viewBox=\"0 0 %0.3f %0.3f\">\n",
613 width, unit, height, unit, width, height);
614 if (title && title[0]) {
615 fputs("<title>", fh);
616 html_escape(fh, title);
617 fputs("</title>\n", fh);
619 fprintf(fh, "<g transform=\"translate(%.3f %.3f)\">\n",
620 SVG_MARGIN - min_x * factor, SVG_MARGIN + max_y * factor);
621 to_close = NULL;
622 close_g = false;
625 void
626 SVG::start_pass(int layer)
628 if (to_close) {
629 fputs(to_close, fh);
630 to_close = NULL;
632 if (close_g) {
633 fprintf(fh, "</g>\n");
635 fprintf(fh, "<g id=\"%s\"", layer_name(layer));
636 if (layer & LEGS)
637 fprintf(fh, " stroke=\"black\" fill=\"none\" stroke-width=\"0.4px\"");
638 else if (layer & STNS)
639 fprintf(fh, " stroke=\"black\" fill=\"none\" stroke-width=\"0.05px\"");
640 else if (layer & LABELS)
641 fprintf(fh, " font-size=\"%.3fem\"", text_height);
642 else if (layer & XSECT)
643 fprintf(fh, " stroke=\"grey\" fill=\"none\" stroke-width=\"0.1px\"");
644 else if (layer & WALLS)
645 fprintf(fh, " stroke=\"black\" fill=\"none\" stroke-width=\"0.1px\"");
646 else if (layer & PASG)
647 fprintf(fh, " stroke=\"none\" fill=\"peru\"");
648 fprintf(fh, ">\n");
650 close_g = true;
653 void
654 SVG::line(const img_point *p1, const img_point *p, unsigned flags, bool fPendingMove)
656 bool splay = (flags & SPLAYS);
657 if (fPendingMove) {
658 if (to_close) {
659 fputs(to_close, fh);
661 fprintf(fh, "<path ");
662 if (splay) fprintf(fh, "stroke=\"grey\" stroke-width=\"0.1px\" ");
663 fprintf(fh, "d=\"M%.3f %.3f", p1->x * factor, p1->y * -factor);
665 fprintf(fh, "L%.3f %.3f", p->x * factor, p->y * -factor);
666 to_close = "\"/>\n";
669 void
670 SVG::label(const img_point *p, const char *s, bool fSurface, int)
672 (void)fSurface; /* unused */
673 fprintf(fh, "<text transform=\"translate(%.3f %.3f)\">",
674 p->x * factor, p->y * -factor);
675 html_escape(fh, s);
676 fputs("</text>\n", fh);
677 set_name(p, s);
680 void
681 SVG::cross(const img_point *p, bool fSurface)
683 (void)fSurface; /* unused */
684 fprintf(fh, "<circle id=\"%s\" cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\"/>\n",
685 find_name(p), p->x * factor, p->y * -factor, marker_size * SQRT_2);
686 fprintf(fh, "<path d=\"M%.3f %.3fL%.3f %.3fM%.3f %.3fL%.3f %.3f\"/>\n",
687 p->x * factor - marker_size, p->y * -factor - marker_size,
688 p->x * factor + marker_size, p->y * -factor + marker_size,
689 p->x * factor + marker_size, p->y * -factor - marker_size,
690 p->x * factor - marker_size, p->y * -factor + marker_size);
693 void
694 SVG::xsect(const img_point *p, double angle, double d1, double d2)
696 double s = sin(rad(angle));
697 double c = cos(rad(angle));
698 fprintf(fh, "<path d=\"M%.3f %.3fL%.3f %.3f\"/>\n",
699 (p->x + c * d1) * factor, (p->y + s * d1) * -factor,
700 (p->x - c * d2) * factor, (p->y - s * d2) * -factor);
703 void
704 SVG::wall(const img_point *p, double angle, double d)
706 if (!to_close) {
707 fprintf(fh, "<path d=\"M");
708 to_close = "\"/>\n";
709 } else {
710 fprintf(fh, "L");
712 double s = sin(rad(angle));
713 double c = cos(rad(angle));
714 fprintf(fh, "%.3f %.3f", (p->x + c * d) * factor, (p->y + s * d) * -factor);
717 void
718 SVG::passage(const img_point *p, double angle, double d1, double d2)
720 double s = sin(rad(angle));
721 double c = cos(rad(angle));
722 double x1 = (p->x + c * d1) * factor;
723 double y1 = (p->y + s * d1) * -factor;
724 double x2 = (p->x - c * d2) * factor;
725 double y2 = (p->y - s * d2) * -factor;
726 if (*pending) {
727 fputs(pending, fh);
728 fprintf(fh, "L%.3f %.3fL%.3f %.3fZ\"/>\n", x2, y2, x1, y1);
730 sprintf(pending, "<path d=\"M%.3f %.3fL%.3f %.3f", x1, y1, x2, y2);
733 void
734 SVG::tube_end()
736 *pending = '\0';
737 if (to_close) {
738 fputs(to_close, fh);
739 to_close = NULL;
743 void
744 SVG::footer()
746 if (to_close) {
747 fputs(to_close, fh);
748 to_close = NULL;
750 if (close_g) {
751 fprintf(fh, "</g>\n");
752 close_g = false;
754 fprintf(fh, "</g>\n</svg>\n");
757 class PLT : public ExportFilter {
758 string escaped;
760 const char * find_name_plt(const img_point *p);
762 double min_N, max_N, min_E, max_E, min_A, max_A;
764 public:
765 PLT() { }
766 const int * passes() const;
767 void header(const char *, const char *, time_t,
768 double min_x, double min_y, double min_z,
769 double max_x, double max_y, double max_z);
770 void line(const img_point *, const img_point *, unsigned, bool);
771 void label(const img_point *, const char *, bool, int);
772 void footer();
775 const int *
776 PLT::passes() const
778 static const int plt_passes[] = { LABELS, LEGS|SURF, 0 };
779 return plt_passes;
782 void
783 PLT::header(const char *title, const char *, time_t,
784 double min_x, double min_y, double min_z,
785 double max_x, double max_y, double max_z)
787 // FIXME: allow survey to be set from aven somehow!
788 const char *survey = NULL;
789 htab = (point **)osmalloc(HTAB_SIZE * ossizeof(point *));
790 for (size_t i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
791 /* Survex is E, N, Alt - PLT file is N, E, Alt */
792 min_N = min_y / METRES_PER_FOOT;
793 max_N = max_y / METRES_PER_FOOT;
794 min_E = min_x / METRES_PER_FOOT;
795 max_E = max_x / METRES_PER_FOOT;
796 min_A = min_z / METRES_PER_FOOT;
797 max_A = max_z / METRES_PER_FOOT;
798 fprintf(fh, "Z %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
799 min_N, max_N, min_E, max_E, min_A, max_A);
800 fprintf(fh, "N%s D 1 1 1 C%s\r\n", survey ? survey : "X",
801 (title && title[0]) ? title : "X");
804 void
805 PLT::line(const img_point *p1, const img_point *p, unsigned flags, bool fPendingMove)
807 (void)flags; /* unused */
808 if (fPendingMove) {
809 /* Survex is E, N, Alt - PLT file is N, E, Alt */
810 fprintf(fh, "M %.3f %.3f %.3f ",
811 p1->y / METRES_PER_FOOT, p1->x / METRES_PER_FOOT, p1->z / METRES_PER_FOOT);
812 /* dummy passage dimensions are required to avoid compass bug */
813 fprintf(fh, "S%s P -9 -9 -9 -9\r\n", find_name_plt(p1));
815 /* Survex is E, N, Alt - PLT file is N, E, Alt */
816 fprintf(fh, "D %.3f %.3f %.3f ",
817 p->y / METRES_PER_FOOT, p->x / METRES_PER_FOOT, p->z / METRES_PER_FOOT);
818 /* dummy passage dimensions are required to avoid compass bug */
819 fprintf(fh, "S%s P -9 -9 -9 -9\r\n", find_name_plt(p));
822 const char *
823 PLT::find_name_plt(const img_point *p)
825 const char * s = find_name(p);
826 escaped.resize(0);
828 // PLT format can't handle spaces or control characters, so escape them
829 // like in URLs (an arbitrary choice of escaping, but at least a familiar
830 // one and % isn't likely to occur in station names).
831 const char * q;
832 for (q = s; *q; ++q) {
833 unsigned char ch = *q;
834 if (ch <= ' ' || ch == '%') {
835 escaped.append(s, q - s);
836 escaped += '%';
837 escaped += "0123456789abcdef"[ch >> 4];
838 escaped += "0123456789abcdef"[ch & 0x0f];
839 s = q + 1;
842 if (!escaped.empty()) {
843 escaped.append(s, q - s);
844 return escaped.c_str();
846 return s;
849 void
850 PLT::label(const img_point *p, const char *s, bool fSurface, int)
852 (void)fSurface; /* unused */
853 set_name(p, s);
856 void
857 PLT::footer(void)
859 /* Survex is E, N, Alt - PLT file is N, E, Alt */
860 fprintf(fh, "X %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
861 min_N, max_N, min_E, max_E, min_A, max_A);
862 /* Yucky DOS "end of textfile" marker */
863 PUTC('\x1a', fh);
866 class EPS : public ExportFilter {
867 double factor;
868 bool first;
869 vector<pair<double, double> > psg;
870 public:
871 explicit EPS(double scale)
872 : factor(POINTS_PER_MM * 1000.0 / scale) { }
873 const int * passes() const;
874 void header(const char *, const char *, time_t,
875 double min_x, double min_y, double min_z,
876 double max_x, double max_y, double max_z);
877 void start_pass(int layer);
878 void line(const img_point *, const img_point *, unsigned, bool);
879 void label(const img_point *, const char *, bool, int);
880 void cross(const img_point *, bool);
881 void xsect(const img_point *, double, double, double);
882 void wall(const img_point *, double, double);
883 void passage(const img_point *, double, double, double);
884 void tube_end();
885 void footer();
888 const int *
889 EPS::passes() const
891 static const int eps_passes[] = {
892 PASG, XSECT, WALL1, WALL2, LEGS|SURF|STNS|LABELS, 0
894 return eps_passes;
897 void
898 EPS::header(const char *title, const char *, time_t,
899 double min_x, double min_y, double /*min_z*/,
900 double max_x, double max_y, double /*max_z*/)
902 const char * fontname_labels = "helvetica"; // FIXME
903 int fontsize_labels = 10; // FIXME
904 fputs("%!PS-Adobe-2.0 EPSF-1.2\n", fh);
905 fputs("%%Creator: Survex " VERSION " EPS Export Filter\n", fh);
907 if (title && title[0])
908 fprintf(fh, "%%%%Title: %s\n", title);
910 char buf[64];
911 time_t now = time(NULL);
912 if (strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %Z\n", localtime(&now))) {
913 fputs("%%CreationDate: ", fh);
914 fputs(buf, fh);
917 string name;
918 #if defined(HAVE_GETPWUID) && !defined(__DJGPP__)
919 struct passwd * ent = getpwuid(getuid());
920 if (ent && ent->pw_gecos[0]) name = ent->pw_gecos;
921 #endif
922 if (name.empty()) {
923 name = ::wxGetUserName().mb_str();
924 if (name.empty()) {
925 name = ::wxGetUserId().mb_str();
928 if (!name.empty()) {
929 fprintf(fh, "%%%%For: %s\n", name.c_str());
932 fprintf(fh, "%%%%BoundingBox: %d %d %d %d\n",
933 int(floor(min_x * factor)), int(floor(min_y * factor)),
934 int(ceil(max_x * factor)), int(ceil(max_y * factor)));
935 fprintf(fh, "%%%%HiResBoundingBox: %.4f %.4f %.4f %.4f\n",
936 min_x * factor, min_y * factor, max_x * factor, max_y * factor);
937 fputs("%%LanguageLevel: 1\n"
938 "%%PageOrder: Ascend\n"
939 "%%Pages: 1\n"
940 "%%Orientation: Portrait\n", fh);
942 fprintf(fh, "%%%%DocumentFonts: %s\n", fontname_labels);
944 fputs("%%EndComments\n"
945 "%%Page 1 1\n"
946 "save countdictstack mark\n", fh);
948 /* this code adapted from a2ps */
949 fputs("%%BeginResource: encoding ISO88591Encoding\n"
950 "/ISO88591Encoding [\n", fh);
951 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
952 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
953 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
954 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
955 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
956 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
957 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
958 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
959 fputs(
960 "/space /exclam /quotedbl /numbersign\n"
961 "/dollar /percent /ampersand /quoteright\n"
962 "/parenleft /parenright /asterisk /plus\n"
963 "/comma /minus /period /slash\n"
964 "/zero /one /two /three\n"
965 "/four /five /six /seven\n"
966 "/eight /nine /colon /semicolon\n"
967 "/less /equal /greater /question\n"
968 "/at /A /B /C /D /E /F /G\n"
969 "/H /I /J /K /L /M /N /O\n"
970 "/P /Q /R /S /T /U /V /W\n"
971 "/X /Y /Z /bracketleft\n"
972 "/backslash /bracketright /asciicircum /underscore\n"
973 "/quoteleft /a /b /c /d /e /f /g\n"
974 "/h /i /j /k /l /m /n /o\n"
975 "/p /q /r /s /t /u /v /w\n"
976 "/x /y /z /braceleft\n"
977 "/bar /braceright /asciitilde /.notdef\n", fh);
978 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
979 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
980 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
981 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
982 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
983 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
984 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
985 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
986 fputs(
987 "/space /exclamdown /cent /sterling\n"
988 "/currency /yen /brokenbar /section\n"
989 "/dieresis /copyright /ordfeminine /guillemotleft\n"
990 "/logicalnot /hyphen /registered /macron\n"
991 "/degree /plusminus /twosuperior /threesuperior\n"
992 "/acute /mu /paragraph /bullet\n"
993 "/cedilla /onesuperior /ordmasculine /guillemotright\n"
994 "/onequarter /onehalf /threequarters /questiondown\n"
995 "/Agrave /Aacute /Acircumflex /Atilde\n"
996 "/Adieresis /Aring /AE /Ccedilla\n"
997 "/Egrave /Eacute /Ecircumflex /Edieresis\n"
998 "/Igrave /Iacute /Icircumflex /Idieresis\n"
999 "/Eth /Ntilde /Ograve /Oacute\n"
1000 "/Ocircumflex /Otilde /Odieresis /multiply\n"
1001 "/Oslash /Ugrave /Uacute /Ucircumflex\n"
1002 "/Udieresis /Yacute /Thorn /germandbls\n"
1003 "/agrave /aacute /acircumflex /atilde\n"
1004 "/adieresis /aring /ae /ccedilla\n"
1005 "/egrave /eacute /ecircumflex /edieresis\n"
1006 "/igrave /iacute /icircumflex /idieresis\n"
1007 "/eth /ntilde /ograve /oacute\n"
1008 "/ocircumflex /otilde /odieresis /divide\n"
1009 "/oslash /ugrave /uacute /ucircumflex\n"
1010 "/udieresis /yacute /thorn /ydieresis\n"
1011 "] def\n"
1012 "%%EndResource\n", fh);
1014 /* this code adapted from a2ps */
1015 fputs(
1016 "/reencode {\n" /* def */
1017 "dup length 5 add dict begin\n"
1018 "{\n" /* forall */
1019 "1 index /FID ne\n"
1020 "{ def }{ pop pop } ifelse\n"
1021 "} forall\n"
1022 "/Encoding exch def\n"
1024 /* Use the font's bounding box to determine the ascent, descent,
1025 * and overall height; don't forget that these values have to be
1026 * transformed using the font's matrix.
1027 * We use `load' because sometimes BBox is executable, sometimes not.
1028 * Since we need 4 numbers and not an array avoid BBox from being executed
1030 "/FontBBox load aload pop\n"
1031 "FontMatrix transform /Ascent exch def pop\n"
1032 "FontMatrix transform /Descent exch def pop\n"
1033 "/FontHeight Ascent Descent sub def\n"
1035 /* Define these in case they're not in the FontInfo (also, here
1036 * they're easier to get to.
1038 "/UnderlinePosition 1 def\n"
1039 "/UnderlineThickness 1 def\n"
1041 /* Get the underline position and thickness if they're defined. */
1042 "currentdict /FontInfo known {\n"
1043 "FontInfo\n"
1045 "dup /UnderlinePosition known {\n"
1046 "dup /UnderlinePosition get\n"
1047 "0 exch FontMatrix transform exch pop\n"
1048 "/UnderlinePosition exch def\n"
1049 "} if\n"
1051 "dup /UnderlineThickness known {\n"
1052 "/UnderlineThickness get\n"
1053 "0 exch FontMatrix transform exch pop\n"
1054 "/UnderlineThickness exch def\n"
1055 "} if\n"
1057 "} if\n"
1058 "currentdict\n"
1059 "end\n"
1060 "} bind def\n", fh);
1062 fprintf(fh, "/lab ISO88591Encoding /%s findfont reencode definefont pop\n",
1063 fontname_labels);
1065 fprintf(fh, "/lab findfont %d scalefont setfont\n", int(fontsize_labels));
1067 #if 0
1068 /* C<digit> changes colour */
1069 /* FIXME: read from ini */
1071 size_t i;
1072 for (i = 0; i < sizeof(colour) / sizeof(colour[0]); ++i) {
1073 fprintf(fh, "/C%u {stroke %.3f %.3f %.3f setrgbcolor} def\n", i,
1074 (double)(colour[i] & 0xff0000) / 0xff0000,
1075 (double)(colour[i] & 0xff00) / 0xff00,
1076 (double)(colour[i] & 0xff) / 0xff);
1079 fputs("C0\n", fh);
1080 #endif
1082 /* Postscript definition for drawing a cross */
1083 fprintf(fh, "/X {stroke moveto %.2f %.2f rmoveto %.2f %.2f rlineto "
1084 "%.2f 0 rmoveto %.2f %.2f rlineto %.2f %.2f rmoveto} def\n",
1085 -marker_size, -marker_size, marker_size * 2, marker_size * 2,
1086 -marker_size * 2, marker_size * 2, -marker_size * 2,
1087 -marker_size, marker_size );
1089 /* define some functions to keep file short */
1090 fputs("/M {stroke moveto} def\n"
1091 "/P {stroke newpath moveto} def\n"
1092 "/F {closepath gsave 0.8 setgray fill grestore} def\n"
1093 "/L {lineto} def\n"
1094 "/R {rlineto} def\n"
1095 "/S {show} def\n", fh);
1097 fprintf(fh, "gsave %.8f dup scale\n", factor);
1098 #if 0
1099 if (grid > 0) {
1100 double x, y;
1101 x = floor(min_x / grid) * grid + grid;
1102 y = floor(min_y / grid) * grid + grid;
1103 #ifdef DEBUG_CAD3D
1104 printf("x_min: %f y_min: %f\n", x, y);
1105 #endif
1106 while (x < max_x) {
1107 /* horizontal line */
1108 fprintf(fh, "0\nLINE\n");
1109 fprintf(fh, "8\nGrid\n"); /* Layer */
1110 fprintf(fh, "10\n%6.2f\n", x);
1111 fprintf(fh, "20\n%6.2f\n", min_y);
1112 fprintf(fh, "30\n0\n");
1113 fprintf(fh, "11\n%6.2f\n", x);
1114 fprintf(fh, "21\n%6.2f\n", max_y);
1115 fprintf(fh, "31\n0\n");
1116 x += grid;
1118 while (y < max_y) {
1119 /* vertical line */
1120 fprintf(fh, "0\nLINE\n");
1121 fprintf(fh, "8\nGrid\n"); /* Layer */
1122 fprintf(fh, "10\n%6.2f\n", min_x);
1123 fprintf(fh, "20\n%6.2f\n", y);
1124 fprintf(fh, "30\n0\n");
1125 fprintf(fh, "11\n%6.2f\n", max_x);
1126 fprintf(fh, "21\n%6.2f\n", y);
1127 fprintf(fh, "31\n0\n");
1128 y += grid;
1131 #endif
1134 void
1135 EPS::start_pass(int layer)
1137 first = true;
1138 switch (layer) {
1139 case LEGS|SURF|STNS|LABELS:
1140 fprintf(fh, "0.1 setlinewidth\n");
1141 break;
1142 case PASG: case XSECT: case WALL1: case WALL2:
1143 fprintf(fh, "0.01 setlinewidth\n");
1144 break;
1148 void
1149 EPS::line(const img_point *p1, const img_point *p, unsigned flags, bool fPendingMove)
1151 (void)flags; /* unused */
1152 if (fPendingMove) {
1153 fprintf(fh, "%.2f %.2f M\n", p1->x, p1->y);
1155 fprintf(fh, "%.2f %.2f L\n", p->x, p->y);
1158 void
1159 EPS::label(const img_point *p, const char *s, bool /*fSurface*/, int)
1161 fprintf(fh, "%.2f %.2f M\n", p->x, p->y);
1162 PUTC('(', fh);
1163 while (*s) {
1164 unsigned char ch = *s++;
1165 switch (ch) {
1166 case '(': case ')': case '\\': /* need to escape these characters */
1167 PUTC('\\', fh);
1168 PUTC(ch, fh);
1169 break;
1170 default:
1171 PUTC(ch, fh);
1172 break;
1175 fputs(") S\n", fh);
1178 void
1179 EPS::cross(const img_point *p, bool fSurface)
1181 (void)fSurface; /* unused */
1182 fprintf(fh, "%.2f %.2f X\n", p->x, p->y);
1185 void
1186 EPS::xsect(const img_point *p, double angle, double d1, double d2)
1188 double s = sin(rad(angle));
1189 double c = cos(rad(angle));
1190 fprintf(fh, "%.2f %.2f M %.2f %.2f R\n",
1191 p->x - c * d2, p->y - s * d2,
1192 c * (d1 + d2), s * (d1 + d2));
1195 void
1196 EPS::wall(const img_point *p, double angle, double d)
1198 double s = sin(rad(angle));
1199 double c = cos(rad(angle));
1200 fprintf(fh, "%.2f %.2f %c\n", p->x + c * d, p->y + s * d, first ? 'M' : 'L');
1201 first = false;
1204 void
1205 EPS::passage(const img_point *p, double angle, double d1, double d2)
1207 double s = sin(rad(angle));
1208 double c = cos(rad(angle));
1209 double x1 = p->x + c * d1;
1210 double y1 = p->y + s * d1;
1211 double x2 = p->x - c * d2;
1212 double y2 = p->y - s * d2;
1213 fprintf(fh, "%.2f %.2f %c\n", x1, y1, first ? 'P' : 'L');
1214 first = false;
1215 psg.push_back(make_pair(x2, y2));
1218 void
1219 EPS::tube_end()
1221 if (!psg.empty()) {
1222 vector<pair<double, double> >::const_reverse_iterator i;
1223 for (i = psg.rbegin(); i != psg.rend(); ++i) {
1224 fprintf(fh, "%.2f %.2f L\n", i->first, i->second);
1226 fputs("F\n", fh);
1227 psg.clear();
1231 void
1232 EPS::footer(void)
1234 fputs("stroke showpage grestore\n"
1235 "%%Trailer\n"
1236 "cleartomark countdictstack exch sub { end } repeat restore\n"
1237 "%%EOF\n", fh);
1240 class UseNumericCLocale {
1241 char * current_locale;
1243 public:
1244 UseNumericCLocale() {
1245 current_locale = osstrdup(setlocale(LC_NUMERIC, NULL));
1246 setlocale(LC_NUMERIC, "C");
1249 ~UseNumericCLocale() {
1250 setlocale(LC_NUMERIC, current_locale);
1251 osfree(current_locale);
1255 static void
1256 transform_point(const Point& pos, const Vector3* pre_offset,
1257 double COS, double SIN, double COST, double SINT,
1258 img_point* p)
1260 double x = pos.GetX();
1261 double y = pos.GetY();
1262 double z = pos.GetZ();
1263 if (pre_offset) {
1264 x += pre_offset->GetX();
1265 y += pre_offset->GetY();
1266 z += pre_offset->GetZ();
1268 p->x = x * COS - y * SIN;
1269 double tmp = x * SIN + y * COS;
1270 p->y = z * COST - tmp * SINT;
1271 p->z = z * SINT + tmp * COST;
1274 bool
1275 Export(const wxString &fnm_out, const wxString &title,
1276 const wxString &datestamp, time_t datestamp_numeric,
1277 const MainFrm * mainfrm,
1278 double pan, double tilt, int show_mask, export_format format,
1279 const char * input_projection,
1280 double grid_, double text_height, double marker_size_,
1281 double scale)
1283 UseNumericCLocale dummy;
1284 int fPendingMove = 0;
1285 img_point p, p1;
1286 const int *pass;
1287 double SIN = sin(rad(pan));
1288 double COS = cos(rad(pan));
1289 double SINT = sin(rad(tilt));
1290 double COST = cos(rad(tilt));
1292 grid = grid_;
1293 marker_size = marker_size_;
1295 ExportFilter * filt;
1296 switch (format) {
1297 case FMT_DXF:
1298 filt = new DXF(text_height);
1299 break;
1300 case FMT_EPS:
1301 filt = new EPS(scale);
1302 break;
1303 case FMT_GPX:
1304 filt = new GPX(input_projection);
1305 show_mask |= FULL_COORDS;
1306 break;
1307 case FMT_HPGL:
1308 filt = new HPGL;
1309 // factor = POINTS_PER_MM * 1000.0 / scale;
1310 break;
1311 case FMT_JSON:
1312 filt = new JSON;
1313 break;
1314 case FMT_KML:
1315 filt = new KML(input_projection);
1316 show_mask |= FULL_COORDS;
1317 break;
1318 case FMT_PLT:
1319 filt = new PLT;
1320 show_mask |= FULL_COORDS;
1321 break;
1322 case FMT_POS:
1323 filt = new POS(mainfrm->GetSeparator());
1324 show_mask |= FULL_COORDS;
1325 break;
1326 case FMT_SK:
1327 filt = new Skencil(scale);
1328 break;
1329 case FMT_SVG:
1330 filt = new SVG(scale, text_height);
1331 break;
1332 default:
1333 return false;
1336 if (!filt->fopen(fnm_out)) {
1337 delete filt;
1338 return false;
1341 const Vector3* pre_offset = NULL;
1342 if (show_mask & FULL_COORDS) {
1343 pre_offset = &mainfrm->GetOffset();
1346 /* Get bounding box */
1347 double min_x, min_y, min_z, max_x, max_y, max_z;
1348 min_x = min_y = min_z = HUGE_VAL;
1349 max_x = max_y = max_z = -HUGE_VAL;
1350 list<traverse>::const_iterator trav = mainfrm->traverses_begin();
1351 list<traverse>::const_iterator tend = mainfrm->traverses_end();
1352 for ( ; trav != tend; ++trav) {
1353 if (trav->isSplay && (show_mask & SPLAYS) == 0) {
1354 continue;
1356 vector<PointInfo>::const_iterator pos = trav->begin();
1357 vector<PointInfo>::const_iterator end = trav->end();
1358 for ( ; pos != end; ++pos) {
1359 transform_point(*pos, pre_offset, COS, SIN, COST, SINT, &p);
1361 if (p.x < min_x) min_x = p.x;
1362 if (p.x > max_x) max_x = p.x;
1363 if (p.y < min_y) min_y = p.y;
1364 if (p.y > max_y) max_y = p.y;
1365 if (p.z < min_z) min_z = p.z;
1366 if (p.z > max_z) max_z = p.z;
1370 list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1371 list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1372 for ( ; pos != end; ++pos) {
1373 transform_point(**pos, pre_offset, COS, SIN, COST, SINT, &p);
1375 if (p.x < min_x) min_x = p.x;
1376 if (p.x > max_x) max_x = p.x;
1377 if (p.y < min_y) min_y = p.y;
1378 if (p.y > max_y) max_y = p.y;
1379 if (p.z < min_z) min_z = p.z;
1380 if (p.z > max_z) max_z = p.z;
1384 if (grid > 0) {
1385 min_x -= grid / 2;
1386 max_x += grid / 2;
1387 min_y -= grid / 2;
1388 max_y += grid / 2;
1391 /* handle empty file gracefully */
1392 if (min_x > max_x) {
1393 min_x = min_y = min_z = 0;
1394 max_x = max_y = max_z = 0;
1397 double x_offset, y_offset, z_offset;
1398 if (show_mask & FULL_COORDS) {
1399 // Full coordinates - offset is applied before rotations.
1400 x_offset = y_offset = z_offset = 0.0;
1401 } else if (show_mask & CENTRED) {
1402 // Centred.
1403 x_offset = (min_x + max_x) * -0.5;
1404 y_offset = (min_y + max_y) * -0.5;
1405 z_offset = (min_z + max_z) * -0.5;
1406 } else {
1407 // Origin at lowest SW corner.
1408 x_offset = -min_x;
1409 y_offset = -min_y;
1410 z_offset = -min_z;
1412 min_x += x_offset;
1413 max_x += x_offset;
1414 min_y += y_offset;
1415 max_y += y_offset;
1416 min_z += z_offset;
1417 max_z += z_offset;
1419 /* Header */
1420 filt->header(title.utf8_str(), datestamp.utf8_str(), datestamp_numeric,
1421 min_x, min_y, min_z, max_x, max_y, max_z);
1423 p1.x = p1.y = p1.z = 0; /* avoid compiler warning */
1425 for (pass = filt->passes(); *pass; ++pass) {
1426 int pass_mask = show_mask & *pass;
1427 if (!pass_mask)
1428 continue;
1429 filt->start_pass(*pass);
1430 if (pass_mask & LEGS) {
1431 trav = mainfrm->traverses_begin();
1432 tend = mainfrm->traverses_end();
1433 for ( ; trav != tend; ++trav) {
1434 unsigned flags = 0;
1435 if (trav->isSplay) {
1436 if ((show_mask & SPLAYS) == 0) {
1437 continue;
1439 flags = SPLAYS;
1441 assert(trav->size() > 1);
1442 vector<PointInfo>::const_iterator pos = trav->begin();
1443 vector<PointInfo>::const_iterator end = trav->end();
1444 for ( ; pos != end; ++pos) {
1445 transform_point(*pos, pre_offset, COS, SIN, COST, SINT, &p);
1446 p.x += x_offset;
1447 p.y += y_offset;
1448 p.z += z_offset;
1450 if (pos == trav->begin()) {
1451 // First point is move...
1452 #ifdef DEBUG_CAD3D
1453 printf("move to %9.2f %9.2f %9.2f\n",x,y,z);
1454 #endif
1455 fPendingMove = 1;
1456 } else {
1457 #ifdef DEBUG_CAD3D
1458 printf("line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
1459 #endif
1460 filt->line(&p1, &p, flags, fPendingMove);
1461 fPendingMove = 0;
1463 p1 = p;
1467 if (pass_mask & SURF) {
1468 trav = mainfrm->surface_traverses_begin();
1469 tend = mainfrm->surface_traverses_end();
1470 for ( ; trav != tend; ++trav) {
1471 unsigned flags = 0;
1472 if (trav->isSplay) {
1473 if ((show_mask & SPLAYS) == 0) {
1474 continue;
1476 flags = SURF|SPLAYS;
1478 assert(trav->size() > 1);
1479 vector<PointInfo>::const_iterator pos = trav->begin();
1480 vector<PointInfo>::const_iterator end = trav->end();
1481 for ( ; pos != end; ++pos) {
1482 transform_point(*pos, pre_offset, COS, SIN, COST, SINT, &p);
1483 p.x += x_offset;
1484 p.y += y_offset;
1485 p.z += z_offset;
1487 if (pos == trav->begin()) {
1488 // First point is move...
1489 #ifdef DEBUG_CAD3D
1490 printf("surface move to %9.2f %9.2f %9.2f\n",x,y,z);
1491 #endif
1492 fPendingMove = 1;
1493 } else {
1494 #ifdef DEBUG_CAD3D
1495 printf("surface line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
1496 #endif
1497 filt->line(&p1, &p, flags, fPendingMove);
1498 fPendingMove = 0;
1500 p1 = p;
1504 if (pass_mask & (STNS|LABELS|ENTS|FIXES|EXPORTS)) {
1505 list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1506 list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1507 for ( ; pos != end; ++pos) {
1508 transform_point(**pos, pre_offset, COS, SIN, COST, SINT, &p);
1509 p.x += x_offset;
1510 p.y += y_offset;
1511 p.z += z_offset;
1513 #ifdef DEBUG_CAD3D
1514 printf("label '%s' at %9.2f %9.2f %9.2f\n",(*pos)->GetText(),x,y,z);
1515 #endif
1516 int type = 0;
1517 if ((pass_mask & ENTS) && (*pos)->IsEntrance()) {
1518 type = ENTS;
1519 } else if ((pass_mask & FIXES) && (*pos)->IsFixedPt()) {
1520 type = FIXES;
1521 } else if ((pass_mask & EXPORTS) && (*pos)->IsExportedPt()) {
1522 type = EXPORTS;
1523 } else if (pass_mask & LABELS) {
1524 type = LABELS;
1526 /* Use !UNDERGROUND as the criterion - we want stations where a
1527 * surface and underground survey meet to be in the underground
1528 * layer */
1529 bool f_surface = !(*pos)->IsUnderground();
1530 if (type) {
1531 const wxString & text = (*pos)->GetText();
1532 filt->label(&p, text.utf8_str(), f_surface, type);
1534 if (pass_mask & STNS)
1535 filt->cross(&p, f_surface);
1538 if (pass_mask & (XSECT|WALLS|PASG)) {
1539 bool elevation = (tilt == 0.0);
1540 list<vector<XSect> >::const_iterator tube = mainfrm->tubes_begin();
1541 list<vector<XSect> >::const_iterator tube_end = mainfrm->tubes_end();
1542 for ( ; tube != tube_end; ++tube) {
1543 vector<XSect>::const_iterator pos = tube->begin();
1544 vector<XSect>::const_iterator end = tube->end();
1545 for ( ; pos != end; ++pos) {
1546 const XSect & xs = *pos;
1547 transform_point(xs, pre_offset, COS, SIN, COST, SINT, &p);
1548 p.x += x_offset;
1549 p.y += y_offset;
1550 p.z += z_offset;
1552 if (elevation) {
1553 if (pass_mask & XSECT)
1554 filt->xsect(&p, 90, xs.GetU(), xs.GetD());
1555 if (pass_mask & WALL1)
1556 filt->wall(&p, 90, xs.GetU());
1557 if (pass_mask & WALL2)
1558 filt->wall(&p, 270, xs.GetD());
1559 if (pass_mask & PASG)
1560 filt->passage(&p, 90, xs.GetU(), xs.GetD());
1561 } else {
1562 // Should only be enabled in plan or elevation mode.
1563 double angle = pan + xs.get_right_bearing();
1564 if (pass_mask & XSECT)
1565 filt->xsect(&p, angle + 180, xs.GetL(), xs.GetR());
1566 if (pass_mask & WALL1)
1567 filt->wall(&p, angle + 180, xs.GetL());
1568 if (pass_mask & WALL2)
1569 filt->wall(&p, angle, xs.GetR());
1570 if (pass_mask & PASG)
1571 filt->passage(&p, angle + 180, xs.GetL(), xs.GetR());
1574 filt->tube_end();
1578 filt->footer();
1579 delete filt;
1580 osfree(htab);
1581 htab = NULL;
1582 return true;