Allow for passing more flags when exporting a leg
[survex.git] / src / export.cc
blobfa965d4004ad053523b61e620055fc742439ba35
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=\"ISO-8859-1\"?>\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 (void)flags; /* unused */
657 if (fPendingMove) {
658 if (to_close) {
659 fputs(to_close, fh);
661 fprintf(fh, "<path d=\"M%.3f %.3f", p1->x * factor, p1->y * -factor);
663 fprintf(fh, "L%.3f %.3f", p->x * factor, p->y * -factor);
664 to_close = "\"/>\n";
667 void
668 SVG::label(const img_point *p, const char *s, bool fSurface, int)
670 (void)fSurface; /* unused */
671 fprintf(fh, "<text transform=\"translate(%.3f %.3f)\">",
672 p->x * factor, p->y * -factor);
673 html_escape(fh, s);
674 fputs("</text>\n", fh);
675 set_name(p, s);
678 void
679 SVG::cross(const img_point *p, bool fSurface)
681 (void)fSurface; /* unused */
682 fprintf(fh, "<circle id=\"%s\" cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\"/>\n",
683 find_name(p), p->x * factor, p->y * -factor, marker_size * SQRT_2);
684 fprintf(fh, "<path d=\"M%.3f %.3fL%.3f %.3fM%.3f %.3fL%.3f %.3f\"/>\n",
685 p->x * factor - marker_size, p->y * -factor - marker_size,
686 p->x * factor + marker_size, p->y * -factor + marker_size,
687 p->x * factor + marker_size, p->y * -factor - marker_size,
688 p->x * factor - marker_size, p->y * -factor + marker_size);
691 void
692 SVG::xsect(const img_point *p, double angle, double d1, double d2)
694 double s = sin(rad(angle));
695 double c = cos(rad(angle));
696 fprintf(fh, "<path d=\"M%.3f %.3fL%.3f %.3f\"/>\n",
697 (p->x + c * d1) * factor, (p->y + s * d1) * -factor,
698 (p->x - c * d2) * factor, (p->y - s * d2) * -factor);
701 void
702 SVG::wall(const img_point *p, double angle, double d)
704 if (!to_close) {
705 fprintf(fh, "<path d=\"M");
706 to_close = "\"/>\n";
707 } else {
708 fprintf(fh, "L");
710 double s = sin(rad(angle));
711 double c = cos(rad(angle));
712 fprintf(fh, "%.3f %.3f", (p->x + c * d) * factor, (p->y + s * d) * -factor);
715 void
716 SVG::passage(const img_point *p, double angle, double d1, double d2)
718 double s = sin(rad(angle));
719 double c = cos(rad(angle));
720 double x1 = (p->x + c * d1) * factor;
721 double y1 = (p->y + s * d1) * -factor;
722 double x2 = (p->x - c * d2) * factor;
723 double y2 = (p->y - s * d2) * -factor;
724 if (*pending) {
725 fputs(pending, fh);
726 fprintf(fh, "L%.3f %.3fL%.3f %.3fZ\"/>\n", x2, y2, x1, y1);
728 sprintf(pending, "<path d=\"M%.3f %.3fL%.3f %.3f", x1, y1, x2, y2);
731 void
732 SVG::tube_end()
734 *pending = '\0';
735 if (to_close) {
736 fputs(to_close, fh);
737 to_close = NULL;
741 void
742 SVG::footer()
744 if (to_close) {
745 fputs(to_close, fh);
746 to_close = NULL;
748 if (close_g) {
749 fprintf(fh, "</g>\n");
750 close_g = false;
752 fprintf(fh, "</g>\n</svg>\n");
755 class PLT : public ExportFilter {
756 string escaped;
758 const char * find_name_plt(const img_point *p);
760 double min_N, max_N, min_E, max_E, min_A, max_A;
762 public:
763 PLT() { }
764 const int * passes() const;
765 void header(const char *, const char *, time_t,
766 double min_x, double min_y, double min_z,
767 double max_x, double max_y, double max_z);
768 void line(const img_point *, const img_point *, unsigned, bool);
769 void label(const img_point *, const char *, bool, int);
770 void footer();
773 const int *
774 PLT::passes() const
776 static const int plt_passes[] = { LABELS, LEGS|SURF, 0 };
777 return plt_passes;
780 void
781 PLT::header(const char *title, const char *, time_t,
782 double min_x, double min_y, double min_z,
783 double max_x, double max_y, double max_z)
785 // FIXME: allow survey to be set from aven somehow!
786 const char *survey = NULL;
787 htab = (point **)osmalloc(HTAB_SIZE * ossizeof(point *));
788 for (size_t i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
789 /* Survex is E, N, Alt - PLT file is N, E, Alt */
790 min_N = min_y / METRES_PER_FOOT;
791 max_N = max_y / METRES_PER_FOOT;
792 min_E = min_x / METRES_PER_FOOT;
793 max_E = max_x / METRES_PER_FOOT;
794 min_A = min_z / METRES_PER_FOOT;
795 max_A = max_z / METRES_PER_FOOT;
796 fprintf(fh, "Z %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
797 min_N, max_N, min_E, max_E, min_A, max_A);
798 fprintf(fh, "N%s D 1 1 1 C%s\r\n", survey ? survey : "X",
799 (title && title[0]) ? title : "X");
802 void
803 PLT::line(const img_point *p1, const img_point *p, unsigned flags, bool fPendingMove)
805 (void)flags; /* unused */
806 if (fPendingMove) {
807 /* Survex is E, N, Alt - PLT file is N, E, Alt */
808 fprintf(fh, "M %.3f %.3f %.3f ",
809 p1->y / METRES_PER_FOOT, p1->x / METRES_PER_FOOT, p1->z / METRES_PER_FOOT);
810 /* dummy passage dimensions are required to avoid compass bug */
811 fprintf(fh, "S%s P -9 -9 -9 -9\r\n", find_name_plt(p1));
813 /* Survex is E, N, Alt - PLT file is N, E, Alt */
814 fprintf(fh, "D %.3f %.3f %.3f ",
815 p->y / METRES_PER_FOOT, p->x / METRES_PER_FOOT, p->z / METRES_PER_FOOT);
816 /* dummy passage dimensions are required to avoid compass bug */
817 fprintf(fh, "S%s P -9 -9 -9 -9\r\n", find_name_plt(p));
820 const char *
821 PLT::find_name_plt(const img_point *p)
823 const char * s = find_name(p);
824 escaped.resize(0);
826 // PLT format can't handle spaces or control characters, so escape them
827 // like in URLs (an arbitrary choice of escaping, but at least a familiar
828 // one and % isn't likely to occur in station names).
829 const char * q;
830 for (q = s; *q; ++q) {
831 unsigned char ch = *q;
832 if (ch <= ' ' || ch == '%') {
833 escaped.append(s, q - s);
834 escaped += '%';
835 escaped += "0123456789abcdef"[ch >> 4];
836 escaped += "0123456789abcdef"[ch & 0x0f];
837 s = q + 1;
840 if (!escaped.empty()) {
841 escaped.append(s, q - s);
842 return escaped.c_str();
844 return s;
847 void
848 PLT::label(const img_point *p, const char *s, bool fSurface, int)
850 (void)fSurface; /* unused */
851 set_name(p, s);
854 void
855 PLT::footer(void)
857 /* Survex is E, N, Alt - PLT file is N, E, Alt */
858 fprintf(fh, "X %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
859 min_N, max_N, min_E, max_E, min_A, max_A);
860 /* Yucky DOS "end of textfile" marker */
861 PUTC('\x1a', fh);
864 class EPS : public ExportFilter {
865 double factor;
866 bool first;
867 vector<pair<double, double> > psg;
868 public:
869 explicit EPS(double scale)
870 : factor(POINTS_PER_MM * 1000.0 / scale) { }
871 const int * passes() const;
872 void header(const char *, const char *, time_t,
873 double min_x, double min_y, double min_z,
874 double max_x, double max_y, double max_z);
875 void start_pass(int layer);
876 void line(const img_point *, const img_point *, unsigned, bool);
877 void label(const img_point *, const char *, bool, int);
878 void cross(const img_point *, bool);
879 void xsect(const img_point *, double, double, double);
880 void wall(const img_point *, double, double);
881 void passage(const img_point *, double, double, double);
882 void tube_end();
883 void footer();
886 const int *
887 EPS::passes() const
889 static const int eps_passes[] = {
890 PASG, XSECT, WALL1, WALL2, LEGS|SURF|STNS|LABELS, 0
892 return eps_passes;
895 void
896 EPS::header(const char *title, const char *, time_t,
897 double min_x, double min_y, double /*min_z*/,
898 double max_x, double max_y, double /*max_z*/)
900 const char * fontname_labels = "helvetica"; // FIXME
901 int fontsize_labels = 10; // FIXME
902 fputs("%!PS-Adobe-2.0 EPSF-1.2\n", fh);
903 fputs("%%Creator: Survex " VERSION " EPS Export Filter\n", fh);
905 if (title && title[0])
906 fprintf(fh, "%%%%Title: %s\n", title);
908 char buf[64];
909 time_t now = time(NULL);
910 if (strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %Z\n", localtime(&now))) {
911 fputs("%%CreationDate: ", fh);
912 fputs(buf, fh);
915 string name;
916 #if defined(HAVE_GETPWUID) && !defined(__DJGPP__)
917 struct passwd * ent = getpwuid(getuid());
918 if (ent && ent->pw_gecos[0]) name = ent->pw_gecos;
919 #endif
920 if (name.empty()) {
921 name = ::wxGetUserName().mb_str();
922 if (name.empty()) {
923 name = ::wxGetUserId().mb_str();
926 if (!name.empty()) {
927 fprintf(fh, "%%%%For: %s\n", name.c_str());
930 fprintf(fh, "%%%%BoundingBox: %d %d %d %d\n",
931 int(floor(min_x * factor)), int(floor(min_y * factor)),
932 int(ceil(max_x * factor)), int(ceil(max_y * factor)));
933 fprintf(fh, "%%%%HiResBoundingBox: %.4f %.4f %.4f %.4f\n",
934 min_x * factor, min_y * factor, max_x * factor, max_y * factor);
935 fputs("%%LanguageLevel: 1\n"
936 "%%PageOrder: Ascend\n"
937 "%%Pages: 1\n"
938 "%%Orientation: Portrait\n", fh);
940 fprintf(fh, "%%%%DocumentFonts: %s\n", fontname_labels);
942 fputs("%%EndComments\n"
943 "%%Page 1 1\n"
944 "save countdictstack mark\n", fh);
946 /* this code adapted from a2ps */
947 fputs("%%BeginResource: encoding ISO88591Encoding\n"
948 "/ISO88591Encoding [\n", fh);
949 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
950 fputs("/.notdef /.notdef /.notdef /.notdef\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(
958 "/space /exclam /quotedbl /numbersign\n"
959 "/dollar /percent /ampersand /quoteright\n"
960 "/parenleft /parenright /asterisk /plus\n"
961 "/comma /minus /period /slash\n"
962 "/zero /one /two /three\n"
963 "/four /five /six /seven\n"
964 "/eight /nine /colon /semicolon\n"
965 "/less /equal /greater /question\n"
966 "/at /A /B /C /D /E /F /G\n"
967 "/H /I /J /K /L /M /N /O\n"
968 "/P /Q /R /S /T /U /V /W\n"
969 "/X /Y /Z /bracketleft\n"
970 "/backslash /bracketright /asciicircum /underscore\n"
971 "/quoteleft /a /b /c /d /e /f /g\n"
972 "/h /i /j /k /l /m /n /o\n"
973 "/p /q /r /s /t /u /v /w\n"
974 "/x /y /z /braceleft\n"
975 "/bar /braceright /asciitilde /.notdef\n", fh);
976 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
977 fputs("/.notdef /.notdef /.notdef /.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(
985 "/space /exclamdown /cent /sterling\n"
986 "/currency /yen /brokenbar /section\n"
987 "/dieresis /copyright /ordfeminine /guillemotleft\n"
988 "/logicalnot /hyphen /registered /macron\n"
989 "/degree /plusminus /twosuperior /threesuperior\n"
990 "/acute /mu /paragraph /bullet\n"
991 "/cedilla /onesuperior /ordmasculine /guillemotright\n"
992 "/onequarter /onehalf /threequarters /questiondown\n"
993 "/Agrave /Aacute /Acircumflex /Atilde\n"
994 "/Adieresis /Aring /AE /Ccedilla\n"
995 "/Egrave /Eacute /Ecircumflex /Edieresis\n"
996 "/Igrave /Iacute /Icircumflex /Idieresis\n"
997 "/Eth /Ntilde /Ograve /Oacute\n"
998 "/Ocircumflex /Otilde /Odieresis /multiply\n"
999 "/Oslash /Ugrave /Uacute /Ucircumflex\n"
1000 "/Udieresis /Yacute /Thorn /germandbls\n"
1001 "/agrave /aacute /acircumflex /atilde\n"
1002 "/adieresis /aring /ae /ccedilla\n"
1003 "/egrave /eacute /ecircumflex /edieresis\n"
1004 "/igrave /iacute /icircumflex /idieresis\n"
1005 "/eth /ntilde /ograve /oacute\n"
1006 "/ocircumflex /otilde /odieresis /divide\n"
1007 "/oslash /ugrave /uacute /ucircumflex\n"
1008 "/udieresis /yacute /thorn /ydieresis\n"
1009 "] def\n"
1010 "%%EndResource\n", fh);
1012 /* this code adapted from a2ps */
1013 fputs(
1014 "/reencode {\n" /* def */
1015 "dup length 5 add dict begin\n"
1016 "{\n" /* forall */
1017 "1 index /FID ne\n"
1018 "{ def }{ pop pop } ifelse\n"
1019 "} forall\n"
1020 "/Encoding exch def\n"
1022 /* Use the font's bounding box to determine the ascent, descent,
1023 * and overall height; don't forget that these values have to be
1024 * transformed using the font's matrix.
1025 * We use `load' because sometimes BBox is executable, sometimes not.
1026 * Since we need 4 numbers and not an array avoid BBox from being executed
1028 "/FontBBox load aload pop\n"
1029 "FontMatrix transform /Ascent exch def pop\n"
1030 "FontMatrix transform /Descent exch def pop\n"
1031 "/FontHeight Ascent Descent sub def\n"
1033 /* Define these in case they're not in the FontInfo (also, here
1034 * they're easier to get to.
1036 "/UnderlinePosition 1 def\n"
1037 "/UnderlineThickness 1 def\n"
1039 /* Get the underline position and thickness if they're defined. */
1040 "currentdict /FontInfo known {\n"
1041 "FontInfo\n"
1043 "dup /UnderlinePosition known {\n"
1044 "dup /UnderlinePosition get\n"
1045 "0 exch FontMatrix transform exch pop\n"
1046 "/UnderlinePosition exch def\n"
1047 "} if\n"
1049 "dup /UnderlineThickness known {\n"
1050 "/UnderlineThickness get\n"
1051 "0 exch FontMatrix transform exch pop\n"
1052 "/UnderlineThickness exch def\n"
1053 "} if\n"
1055 "} if\n"
1056 "currentdict\n"
1057 "end\n"
1058 "} bind def\n", fh);
1060 fprintf(fh, "/lab ISO88591Encoding /%s findfont reencode definefont pop\n",
1061 fontname_labels);
1063 fprintf(fh, "/lab findfont %d scalefont setfont\n", int(fontsize_labels));
1065 #if 0
1066 /* C<digit> changes colour */
1067 /* FIXME: read from ini */
1069 size_t i;
1070 for (i = 0; i < sizeof(colour) / sizeof(colour[0]); ++i) {
1071 fprintf(fh, "/C%u {stroke %.3f %.3f %.3f setrgbcolor} def\n", i,
1072 (double)(colour[i] & 0xff0000) / 0xff0000,
1073 (double)(colour[i] & 0xff00) / 0xff00,
1074 (double)(colour[i] & 0xff) / 0xff);
1077 fputs("C0\n", fh);
1078 #endif
1080 /* Postscript definition for drawing a cross */
1081 fprintf(fh, "/X {stroke moveto %.2f %.2f rmoveto %.2f %.2f rlineto "
1082 "%.2f 0 rmoveto %.2f %.2f rlineto %.2f %.2f rmoveto} def\n",
1083 -marker_size, -marker_size, marker_size * 2, marker_size * 2,
1084 -marker_size * 2, marker_size * 2, -marker_size * 2,
1085 -marker_size, marker_size );
1087 /* define some functions to keep file short */
1088 fputs("/M {stroke moveto} def\n"
1089 "/P {stroke newpath moveto} def\n"
1090 "/F {closepath gsave 0.8 setgray fill grestore} def\n"
1091 "/L {lineto} def\n"
1092 "/R {rlineto} def\n"
1093 "/S {show} def\n", fh);
1095 fprintf(fh, "gsave %.8f dup scale\n", factor);
1096 #if 0
1097 if (grid > 0) {
1098 double x, y;
1099 x = floor(min_x / grid) * grid + grid;
1100 y = floor(min_y / grid) * grid + grid;
1101 #ifdef DEBUG_CAD3D
1102 printf("x_min: %f y_min: %f\n", x, y);
1103 #endif
1104 while (x < max_x) {
1105 /* horizontal line */
1106 fprintf(fh, "0\nLINE\n");
1107 fprintf(fh, "8\nGrid\n"); /* Layer */
1108 fprintf(fh, "10\n%6.2f\n", x);
1109 fprintf(fh, "20\n%6.2f\n", min_y);
1110 fprintf(fh, "30\n0\n");
1111 fprintf(fh, "11\n%6.2f\n", x);
1112 fprintf(fh, "21\n%6.2f\n", max_y);
1113 fprintf(fh, "31\n0\n");
1114 x += grid;
1116 while (y < max_y) {
1117 /* vertical line */
1118 fprintf(fh, "0\nLINE\n");
1119 fprintf(fh, "8\nGrid\n"); /* Layer */
1120 fprintf(fh, "10\n%6.2f\n", min_x);
1121 fprintf(fh, "20\n%6.2f\n", y);
1122 fprintf(fh, "30\n0\n");
1123 fprintf(fh, "11\n%6.2f\n", max_x);
1124 fprintf(fh, "21\n%6.2f\n", y);
1125 fprintf(fh, "31\n0\n");
1126 y += grid;
1129 #endif
1132 void
1133 EPS::start_pass(int layer)
1135 first = true;
1136 switch (layer) {
1137 case LEGS|SURF|STNS|LABELS:
1138 fprintf(fh, "0.1 setlinewidth\n");
1139 break;
1140 case PASG: case XSECT: case WALL1: case WALL2:
1141 fprintf(fh, "0.01 setlinewidth\n");
1142 break;
1146 void
1147 EPS::line(const img_point *p1, const img_point *p, unsigned flags, bool fPendingMove)
1149 (void)flags; /* unused */
1150 if (fPendingMove) {
1151 fprintf(fh, "%.2f %.2f M\n", p1->x, p1->y);
1153 fprintf(fh, "%.2f %.2f L\n", p->x, p->y);
1156 void
1157 EPS::label(const img_point *p, const char *s, bool /*fSurface*/, int)
1159 fprintf(fh, "%.2f %.2f M\n", p->x, p->y);
1160 PUTC('(', fh);
1161 while (*s) {
1162 unsigned char ch = *s++;
1163 switch (ch) {
1164 case '(': case ')': case '\\': /* need to escape these characters */
1165 PUTC('\\', fh);
1166 PUTC(ch, fh);
1167 break;
1168 default:
1169 PUTC(ch, fh);
1170 break;
1173 fputs(") S\n", fh);
1176 void
1177 EPS::cross(const img_point *p, bool fSurface)
1179 (void)fSurface; /* unused */
1180 fprintf(fh, "%.2f %.2f X\n", p->x, p->y);
1183 void
1184 EPS::xsect(const img_point *p, double angle, double d1, double d2)
1186 double s = sin(rad(angle));
1187 double c = cos(rad(angle));
1188 fprintf(fh, "%.2f %.2f M %.2f %.2f R\n",
1189 p->x - c * d2, p->y - s * d2,
1190 c * (d1 + d2), s * (d1 + d2));
1193 void
1194 EPS::wall(const img_point *p, double angle, double d)
1196 double s = sin(rad(angle));
1197 double c = cos(rad(angle));
1198 fprintf(fh, "%.2f %.2f %c\n", p->x + c * d, p->y + s * d, first ? 'M' : 'L');
1199 first = false;
1202 void
1203 EPS::passage(const img_point *p, double angle, double d1, double d2)
1205 double s = sin(rad(angle));
1206 double c = cos(rad(angle));
1207 double x1 = p->x + c * d1;
1208 double y1 = p->y + s * d1;
1209 double x2 = p->x - c * d2;
1210 double y2 = p->y - s * d2;
1211 fprintf(fh, "%.2f %.2f %c\n", x1, y1, first ? 'P' : 'L');
1212 first = false;
1213 psg.push_back(make_pair(x2, y2));
1216 void
1217 EPS::tube_end()
1219 if (!psg.empty()) {
1220 vector<pair<double, double> >::const_reverse_iterator i;
1221 for (i = psg.rbegin(); i != psg.rend(); ++i) {
1222 fprintf(fh, "%.2f %.2f L\n", i->first, i->second);
1224 fputs("F\n", fh);
1225 psg.clear();
1229 void
1230 EPS::footer(void)
1232 fputs("stroke showpage grestore\n"
1233 "%%Trailer\n"
1234 "cleartomark countdictstack exch sub { end } repeat restore\n"
1235 "%%EOF\n", fh);
1238 class UseNumericCLocale {
1239 char * current_locale;
1241 public:
1242 UseNumericCLocale() {
1243 current_locale = osstrdup(setlocale(LC_NUMERIC, NULL));
1244 setlocale(LC_NUMERIC, "C");
1247 ~UseNumericCLocale() {
1248 setlocale(LC_NUMERIC, current_locale);
1249 osfree(current_locale);
1253 bool
1254 Export(const wxString &fnm_out, const wxString &title,
1255 const wxString &datestamp, time_t datestamp_numeric,
1256 const MainFrm * mainfrm,
1257 double pan, double tilt, int show_mask, export_format format,
1258 const char * input_projection,
1259 double grid_, double text_height, double marker_size_,
1260 double scale)
1262 UseNumericCLocale dummy;
1263 int fPendingMove = 0;
1264 img_point p, p1;
1265 double s = 0, c = 0;
1266 const int *pass;
1267 bool elevation = (tilt == 0.0);
1269 grid = grid_;
1270 marker_size = marker_size_;
1272 ExportFilter * filt;
1273 switch (format) {
1274 case FMT_DXF:
1275 filt = new DXF(text_height);
1276 break;
1277 case FMT_EPS:
1278 filt = new EPS(scale);
1279 break;
1280 case FMT_GPX:
1281 filt = new GPX(input_projection);
1282 show_mask |= FULL_COORDS;
1283 break;
1284 case FMT_HPGL:
1285 filt = new HPGL;
1286 // factor = POINTS_PER_MM * 1000.0 / scale;
1287 break;
1288 case FMT_JSON:
1289 filt = new JSON;
1290 break;
1291 case FMT_KML:
1292 filt = new KML(input_projection);
1293 show_mask |= FULL_COORDS;
1294 break;
1295 case FMT_PLT:
1296 filt = new PLT;
1297 show_mask |= FULL_COORDS;
1298 break;
1299 case FMT_POS:
1300 filt = new POS(mainfrm->GetSeparator());
1301 show_mask |= FULL_COORDS;
1302 break;
1303 case FMT_SK:
1304 filt = new Skencil(scale);
1305 break;
1306 case FMT_SVG:
1307 filt = new SVG(scale, text_height);
1308 break;
1309 default:
1310 return false;
1313 if (!filt->fopen(fnm_out)) {
1314 delete filt;
1315 return false;
1318 if (elevation) {
1319 s = sin(rad(pan));
1320 c = cos(rad(pan));
1323 /* Get bounding box */
1324 double min_x, min_y, min_z, max_x, max_y, max_z;
1325 min_x = min_y = min_z = HUGE_VAL;
1326 max_x = max_y = max_z = -HUGE_VAL;
1327 list<traverse>::const_iterator trav = mainfrm->traverses_begin();
1328 list<traverse>::const_iterator tend = mainfrm->traverses_end();
1329 for ( ; trav != tend; ++trav) {
1330 if (trav->isSplay && (show_mask & SPLAYS) == 0) {
1331 continue;
1333 vector<PointInfo>::const_iterator pos = trav->begin();
1334 vector<PointInfo>::const_iterator end = trav->end();
1335 for ( ; pos != end; ++pos) {
1336 p.x = pos->GetX();
1337 p.y = pos->GetY();
1338 p.z = pos->GetZ();
1340 if (elevation) {
1341 double xnew = p.x * c - p.y * s;
1342 double znew = - p.x * s - p.y * c;
1343 p.y = p.z;
1344 p.z = znew;
1345 p.x = xnew;
1348 if (p.x < min_x) min_x = p.x;
1349 if (p.x > max_x) max_x = p.x;
1350 if (p.y < min_y) min_y = p.y;
1351 if (p.y > max_y) max_y = p.y;
1352 if (p.z < min_z) min_z = p.z;
1353 if (p.z > max_z) max_z = p.z;
1357 list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1358 list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1359 for ( ; pos != end; ++pos) {
1360 p.x = (*pos)->GetX();
1361 p.y = (*pos)->GetY();
1362 p.z = (*pos)->GetZ();
1364 if (elevation) {
1365 double xnew = p.x * c - p.y * s;
1366 double znew = - p.x * s - p.y * c;
1367 p.y = p.z;
1368 p.z = znew;
1369 p.x = xnew;
1372 if (p.x < min_x) min_x = p.x;
1373 if (p.x > max_x) max_x = p.x;
1374 if (p.y < min_y) min_y = p.y;
1375 if (p.y > max_y) max_y = p.y;
1376 if (p.z < min_z) min_z = p.z;
1377 if (p.z > max_z) max_z = p.z;
1381 if (grid > 0) {
1382 min_x -= grid / 2;
1383 max_x += grid / 2;
1384 min_y -= grid / 2;
1385 max_y += grid / 2;
1388 /* handle empty file gracefully */
1389 if (min_x > max_x) {
1390 min_x = min_y = min_z = 0;
1391 max_x = max_y = max_z = 0;
1394 double x_offset, y_offset, z_offset;
1395 if (show_mask & FULL_COORDS) {
1396 // Full coordinates.
1397 x_offset = mainfrm->GetOffset().GetX();
1398 y_offset = mainfrm->GetOffset().GetY();
1399 z_offset = mainfrm->GetOffset().GetZ();
1400 } else if (show_mask & CENTRED) {
1401 // Centred.
1402 x_offset = (min_x + max_x) * -0.5;
1403 y_offset = (min_y + max_y) * -0.5;
1404 z_offset = (min_z + max_z) * -0.5;
1405 } else {
1406 // Origin at lowest SW corner.
1407 x_offset = -min_x;
1408 y_offset = -min_y;
1409 z_offset = -min_z;
1411 min_x += x_offset;
1412 max_x += x_offset;
1413 min_y += y_offset;
1414 max_y += y_offset;
1415 min_z += z_offset;
1416 max_z += z_offset;
1418 /* Header */
1419 filt->header(title.utf8_str(), datestamp.utf8_str(), datestamp_numeric,
1420 min_x, min_y, min_z, max_x, max_y, max_z);
1422 p1.x = p1.y = p1.z = 0; /* avoid compiler warning */
1424 for (pass = filt->passes(); *pass; ++pass) {
1425 int pass_mask = show_mask & *pass;
1426 if (!pass_mask)
1427 continue;
1428 filt->start_pass(*pass);
1429 if (pass_mask & LEGS) {
1430 trav = mainfrm->traverses_begin();
1431 tend = mainfrm->traverses_end();
1432 for ( ; trav != tend; ++trav) {
1433 unsigned flags = 0;
1434 if (trav->isSplay) {
1435 if ((show_mask & SPLAYS) == 0) {
1436 continue;
1438 flags = SPLAYS;
1440 assert(trav->size() > 1);
1441 vector<PointInfo>::const_iterator pos = trav->begin();
1442 vector<PointInfo>::const_iterator end = trav->end();
1443 for ( ; pos != end; ++pos) {
1444 p.x = pos->GetX() + x_offset;
1445 p.y = pos->GetY() + y_offset;
1446 p.z = pos->GetZ() + z_offset;
1447 if (elevation) {
1448 double xnew = p.x * c - p.y * s;
1449 double znew = - p.x * s - p.y * c;
1450 p.y = p.z;
1451 p.z = znew;
1452 p.x = xnew;
1455 if (pos == trav->begin()) {
1456 // First point is move...
1457 #ifdef DEBUG_CAD3D
1458 printf("move to %9.2f %9.2f %9.2f\n",x,y,z);
1459 #endif
1460 fPendingMove = 1;
1461 } else {
1462 #ifdef DEBUG_CAD3D
1463 printf("line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
1464 #endif
1465 filt->line(&p1, &p, flags, fPendingMove);
1466 fPendingMove = 0;
1468 p1 = p;
1472 if (pass_mask & SURF) {
1473 trav = mainfrm->surface_traverses_begin();
1474 tend = mainfrm->surface_traverses_end();
1475 for ( ; trav != tend; ++trav) {
1476 unsigned flags = 0;
1477 if (trav->isSplay) {
1478 if ((show_mask & SPLAYS) == 0) {
1479 continue;
1481 flags = SURF|SPLAYS;
1483 assert(trav->size() > 1);
1484 vector<PointInfo>::const_iterator pos = trav->begin();
1485 vector<PointInfo>::const_iterator end = trav->end();
1486 for ( ; pos != end; ++pos) {
1487 p.x = pos->GetX() + x_offset;
1488 p.y = pos->GetY() + y_offset;
1489 p.z = pos->GetZ() + z_offset;
1491 if (elevation) {
1492 double xnew = p.x * c - p.y * s;
1493 double znew = - p.x * s - p.y * c;
1494 p.y = p.z;
1495 p.z = znew;
1496 p.x = xnew;
1499 if (pos == trav->begin()) {
1500 // First point is move...
1501 #ifdef DEBUG_CAD3D
1502 printf("surface move to %9.2f %9.2f %9.2f\n",x,y,z);
1503 #endif
1504 fPendingMove = 1;
1505 } else {
1506 #ifdef DEBUG_CAD3D
1507 printf("surface line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
1508 #endif
1509 filt->line(&p1, &p, flags, fPendingMove);
1510 fPendingMove = 0;
1512 p1 = p;
1516 if (pass_mask & (STNS|LABELS|ENTS|FIXES|EXPORTS)) {
1517 list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1518 list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1519 for ( ; pos != end; ++pos) {
1520 p.x = (*pos)->GetX() + x_offset;
1521 p.y = (*pos)->GetY() + y_offset;
1522 p.z = (*pos)->GetZ() + z_offset;
1524 if (elevation) {
1525 double xnew = p.x * c - p.y * s;
1526 double znew = - p.x * s - p.y * c;
1527 p.y = p.z;
1528 p.z = znew;
1529 p.x = xnew;
1531 #ifdef DEBUG_CAD3D
1532 printf("label '%s' at %9.2f %9.2f %9.2f\n",(*pos)->GetText(),x,y,z);
1533 #endif
1534 int type = 0;
1535 if ((pass_mask & ENTS) && (*pos)->IsEntrance()) {
1536 type = ENTS;
1537 } else if ((pass_mask & FIXES) && (*pos)->IsFixedPt()) {
1538 type = FIXES;
1539 } else if ((pass_mask & EXPORTS) && (*pos)->IsExportedPt()) {
1540 type = EXPORTS;
1541 } else if (pass_mask & LABELS) {
1542 type = LABELS;
1544 /* Use !UNDERGROUND as the criterion - we want stations where a
1545 * surface and underground survey meet to be in the underground
1546 * layer */
1547 bool f_surface = !(*pos)->IsUnderground();
1548 if (type) {
1549 const wxString & text = (*pos)->GetText();
1550 filt->label(&p, text.utf8_str(), f_surface, type);
1552 if (pass_mask & STNS)
1553 filt->cross(&p, f_surface);
1556 if (pass_mask & (XSECT|WALLS|PASG)) {
1557 list<vector<XSect> >::const_iterator tube = mainfrm->tubes_begin();
1558 list<vector<XSect> >::const_iterator tube_end = mainfrm->tubes_end();
1559 for ( ; tube != tube_end; ++tube) {
1560 vector<XSect>::const_iterator pos = tube->begin();
1561 vector<XSect>::const_iterator end = tube->end();
1562 for ( ; pos != end; ++pos) {
1563 const XSect & xs = *pos;
1564 p.x = xs.GetX() + x_offset;
1565 p.y = xs.GetY() + y_offset;
1566 p.z = xs.GetZ() + z_offset;
1568 if (elevation) {
1569 double xnew = p.x * c - p.y * s;
1570 double znew = - p.x * s - p.y * c;
1571 p.y = p.z;
1572 p.z = znew;
1573 p.x = xnew;
1574 if (pass_mask & XSECT)
1575 filt->xsect(&p, 90, xs.GetU(), xs.GetD());
1576 if (pass_mask & WALL1)
1577 filt->wall(&p, 90, xs.GetU());
1578 if (pass_mask & WALL2)
1579 filt->wall(&p, 270, xs.GetD());
1580 if (pass_mask & PASG)
1581 filt->passage(&p, 90, xs.GetU(), xs.GetD());
1582 } else {
1583 if (pass_mask & XSECT)
1584 filt->xsect(&p, xs.get_right_bearing() + 180, xs.GetL(), xs.GetR());
1585 if (pass_mask & WALL1)
1586 filt->wall(&p, xs.get_right_bearing() + 180, xs.GetL());
1587 if (pass_mask & WALL2)
1588 filt->wall(&p, xs.get_right_bearing(), xs.GetR());
1589 if (pass_mask & PASG)
1590 filt->passage(&p, xs.get_right_bearing() + 180, xs.GetL(), xs.GetR());
1593 filt->tube_end();
1597 filt->footer();
1598 delete filt;
1599 osfree(htab);
1600 htab = NULL;
1601 return true;