source: git/src/export.cc @ 1fce809

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-datawalls-data-hanging-as-warning
Last change on this file since 1fce809 was 1534ed9, checked in by Olly Betts <olly@…>, 10 years ago

lib/survex.pot,src/: Add KML export (stations only currently).

  • Property mode set to 100644
File size: 40.4 KB
Line 
1/* export.cc
2 * Export to CAD-like formats (DXF, Skencil, SVG, EPS) and also Compass PLT.
3 */
4
5/* Copyright (C) 1994-2004,2005,2006,2008,2010,2011,2012,2013,2014 Olly Betts
6 * Copyright (C) 2004 John Pybus (SVG Output code)
7 *
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.
12 *
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.
17 *
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
21 */
22
23/* #define DEBUG_CAD3D */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include "export.h"
30
31#include "wx.h"
32#include <wx/utils.h>
33#include "exportfilter.h"
34#include "gpx.h"
35#include "hpgl.h"
36#include "kml.h"
37#include "mainfrm.h"
38
39#include <float.h>
40#include <math.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <time.h>
45
46#if defined(HAVE_GETPWUID) && !defined(__DJGPP__)
47# include <pwd.h>
48# include <sys/types.h>
49# include <unistd.h>
50#endif
51
52#include "cmdline.h"
53#include "debug.h"
54#include "filename.h"
55#include "hash.h"
56#include "img_hosted.h"
57#include "message.h"
58#include "useful.h"
59
60#define POINTS_PER_INCH 72.0
61#define POINTS_PER_MM (POINTS_PER_INCH / MM_PER_INCH)
62
63#define SQRT_2          1.41421356237309504880168872420969
64
65static void
66html_escape(FILE *fh, const char *s)
67{
68    while (*s) {
69        switch (*s) {
70            case '<':
71                fputs("&lt;", fh);
72                break;
73            case '>':
74                fputs("&gt;", fh);
75                break;
76            case '&':
77                fputs("&amp;", fh);
78                break;
79            default:
80                PUTC(*s, fh);
81        }
82        ++s;
83    }
84}
85
86static const char *layer_name(int mask) {
87    switch (mask) {
88        case LEGS: case LEGS|SURF:
89            return "Legs";
90        case SURF:
91            return "Surface";
92        case STNS:
93            return "Stations";
94        case LABELS:
95            return "Labels";
96        case XSECT:
97            return "Cross-sections";
98        case WALL1: case WALL2: case WALLS:
99            return "Walls";
100        case PASG:
101            return "Passages";
102    }
103    return "";
104}
105
106/* bounds */
107static double min_x, min_y, min_z, max_x, max_y, max_z;
108
109static double text_height; /* for station labels */
110static double marker_size; /* for station markers */
111static double grid; /* grid spacing (or 0 for no grid) */
112static double scale = 500.0;
113static double factor;
114static const char *unit = "mm";
115const double SVG_MARGIN = 5.0; // In units of "unit".
116
117static const char *survey = NULL;
118
119const int *
120ExportFilter::passes() const
121{
122    static const int default_passes[] = { LEGS|SURF|STNS|LABELS, 0 };
123    return default_passes;
124}
125
126class DXF : public ExportFilter {
127    const char * to_close;
128    char pending[1024];
129
130  public:
131    DXF() : to_close(0) { pending[0] = '\0'; }
132    const int * passes() const;
133    bool fopen(const char *fnm_out);
134    void header(const char *, const char *, time_t);
135    void line(const img_point *, const img_point *, bool, bool);
136    void label(const img_point *, const char *, bool, int);
137    void cross(const img_point *, bool);
138    void xsect(const img_point *, double, double, double);
139    void wall(const img_point *, double, double);
140    void passage(const img_point *, double, double, double);
141    void tube_end();
142    void footer();
143};
144
145const int *
146DXF::passes() const
147{
148    static const int dxf_passes[] = {
149        PASG, XSECT, WALL1, WALL2, LEGS|SURF|STNS|LABELS, 0
150    };
151    return dxf_passes;
152}
153
154bool
155DXF::fopen(const char *fnm_out)
156{
157    // DXF gets written as text rather than binary.
158    fh = ::fopen(fnm_out, "w");
159    return (fh != NULL);
160}
161
162void
163DXF::header(const char *, const char *, time_t)
164{
165   fprintf(fh, "0\nSECTION\n"
166               "2\nHEADER\n");
167   fprintf(fh, "9\n$EXTMIN\n"); /* lower left corner of drawing */
168   fprintf(fh, "10\n%#-.6f\n", min_x); /* x */
169   fprintf(fh, "20\n%#-.6f\n", min_y); /* y */
170   fprintf(fh, "30\n%#-.6f\n", min_z); /* min z */
171   fprintf(fh, "9\n$EXTMAX\n"); /* upper right corner of drawing */
172   fprintf(fh, "10\n%#-.6f\n", max_x); /* x */
173   fprintf(fh, "20\n%#-.6f\n", max_y); /* y */
174   fprintf(fh, "30\n%#-.6f\n", max_z); /* max z */
175   fprintf(fh, "9\n$PDMODE\n70\n3\n"); /* marker style as CROSS */
176   fprintf(fh, "9\n$PDSIZE\n40\n%6.2f\n", marker_size); /* marker size */
177   fprintf(fh, "0\nENDSEC\n");
178
179   fprintf(fh, "0\nSECTION\n"
180               "2\nTABLES\n");
181   fprintf(fh, "0\nTABLE\n" /* Define CONTINUOUS and DASHED line types. */
182               "2\nLTYPE\n"
183               "70\n10\n"
184               "0\nLTYPE\n"
185               "2\nCONTINUOUS\n"
186               "70\n64\n"
187               "3\nContinuous\n"
188               "72\n65\n"
189               "73\n0\n"
190               "40\n0.0\n"
191               "0\nLTYPE\n"
192               "2\nDASHED\n"
193               "70\n64\n"
194               "3\nDashed\n"
195               "72\n65\n"
196               "73\n2\n"
197               "40\n2.5\n"
198               "49\n1.25\n"
199               "49\n-1.25\n"
200               "0\nENDTAB\n");
201   fprintf(fh, "0\nTABLE\n"
202               "2\nLAYER\n");
203   fprintf(fh, "70\n10\n"); /* max # off layers in this DXF file : 10 */
204   /* First Layer: CentreLine */
205   fprintf(fh, "0\nLAYER\n2\nCentreLine\n");
206   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
207   fprintf(fh, "62\n5\n"); /* color: kept the same used by SpeleoGen */
208   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
209   /* Next Layer: Stations */
210   fprintf(fh, "0\nLAYER\n2\nStations\n");
211   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
212   fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
213   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
214   /* Next Layer: Labels */
215   fprintf(fh, "0\nLAYER\n2\nLabels\n");
216   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
217   fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
218   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
219   /* Next Layer: Surface */
220   fprintf(fh, "0\nLAYER\n2\nSurface\n");
221   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
222   fprintf(fh, "62\n5\n"); /* color */
223   fprintf(fh, "6\nDASHED\n"); /* linetype */
224   /* Next Layer: SurfaceStations */
225   fprintf(fh, "0\nLAYER\n2\nSurfaceStations\n");
226   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
227   fprintf(fh, "62\n7\n"); /* color */
228   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
229   /* Next Layer: SurfaceLabels */
230   fprintf(fh, "0\nLAYER\n2\nSurfaceLabels\n");
231   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
232   fprintf(fh, "62\n7\n"); /* color */
233   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
234   if (grid > 0) {
235      /* Next Layer: Grid */
236      fprintf(fh, "0\nLAYER\n2\nGrid\n");
237      fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
238      fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
239      fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
240   }
241   fprintf(fh, "0\nENDTAB\n"
242               "0\nENDSEC\n");
243
244   fprintf(fh, "0\nSECTION\n"
245               "2\nENTITIES\n");
246
247   if (grid > 0) {
248      double x, y;
249      x = floor(min_x / grid) * grid + grid;
250      y = floor(min_y / grid) * grid + grid;
251#ifdef DEBUG_CAD3D
252      printf("x_min: %f  y_min: %f\n", x, y);
253#endif
254      while (x < max_x) {
255         /* horizontal line */
256         fprintf(fh, "0\nLINE\n");
257         fprintf(fh, "8\nGrid\n"); /* Layer */
258         fprintf(fh, "10\n%6.2f\n", x);
259         fprintf(fh, "20\n%6.2f\n", min_y);
260         fprintf(fh, "30\n0\n");
261         fprintf(fh, "11\n%6.2f\n", x);
262         fprintf(fh, "21\n%6.2f\n", max_y);
263         fprintf(fh, "31\n0\n");
264         x += grid;
265      }
266      while (y < max_y) {
267         /* vertical line */
268         fprintf(fh, "0\nLINE\n");
269         fprintf(fh, "8\nGrid\n"); /* Layer */
270         fprintf(fh, "10\n%6.2f\n", min_x);
271         fprintf(fh, "20\n%6.2f\n", y);
272         fprintf(fh, "30\n0\n");
273         fprintf(fh, "11\n%6.2f\n", max_x);
274         fprintf(fh, "21\n%6.2f\n", y);
275         fprintf(fh, "31\n0\n");
276         y += grid;
277      }
278   }
279}
280
281void
282DXF::line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
283{
284   (void)fPendingMove; /* unused */
285   fprintf(fh, "0\nLINE\n");
286   fprintf(fh, fSurface ? "8\nSurface\n" : "8\nCentreLine\n"); /* Layer */
287   fprintf(fh, "10\n%6.2f\n", p1->x);
288   fprintf(fh, "20\n%6.2f\n", p1->y);
289   fprintf(fh, "30\n%6.2f\n", p1->z);
290   fprintf(fh, "11\n%6.2f\n", p->x);
291   fprintf(fh, "21\n%6.2f\n", p->y);
292   fprintf(fh, "31\n%6.2f\n", p->z);
293}
294
295void
296DXF::label(const img_point *p, const char *s, bool fSurface, int)
297{
298   /* write station label to dxf file */
299   fprintf(fh, "0\nTEXT\n");
300   fprintf(fh, fSurface ? "8\nSurfaceLabels\n" : "8\nLabels\n"); /* Layer */
301   fprintf(fh, "10\n%6.2f\n", p->x);
302   fprintf(fh, "20\n%6.2f\n", p->y);
303   fprintf(fh, "30\n%6.2f\n", p->z);
304   fprintf(fh, "40\n%6.2f\n", text_height);
305   fprintf(fh, "1\n%s\n", s);
306}
307
308void
309DXF::cross(const img_point *p, bool fSurface)
310{
311   /* write station marker to dxf file */
312   fprintf(fh, "0\nPOINT\n");
313   fprintf(fh, fSurface ? "8\nSurfaceStations\n" : "8\nStations\n"); /* Layer */
314   fprintf(fh, "10\n%6.2f\n", p->x);
315   fprintf(fh, "20\n%6.2f\n", p->y);
316   fprintf(fh, "30\n%6.2f\n", p->z);
317}
318
319void
320DXF::xsect(const img_point *p, double angle, double d1, double d2)
321{
322   double s = sin(rad(angle));
323   double c = cos(rad(angle));
324   fprintf(fh, "0\nLINE\n");
325   fprintf(fh, "8\nCross-sections\n"); /* Layer */
326   fprintf(fh, "10\n%6.2f\n", p->x + c * d1);
327   fprintf(fh, "20\n%6.2f\n", p->y + s * d1);
328   fprintf(fh, "30\n%6.2f\n", p->z);
329   fprintf(fh, "11\n%6.2f\n", p->x - c * d2);
330   fprintf(fh, "21\n%6.2f\n", p->y - s * d2);
331   fprintf(fh, "31\n%6.2f\n", p->z);
332}
333
334void
335DXF::wall(const img_point *p, double angle, double d)
336{
337   if (!to_close) {
338       fprintf(fh, "0\nPOLYLINE\n");
339       fprintf(fh, "8\nWalls\n"); /* Layer */
340       fprintf(fh, "70\n0\n"); /* bit 0 == 0 => Open polyline */
341       to_close = "0\nSEQEND\n";
342   }
343   double s = sin(rad(angle));
344   double c = cos(rad(angle));
345   fprintf(fh, "0\nVERTEX\n");
346   fprintf(fh, "8\nWalls\n"); /* Layer */
347   fprintf(fh, "10\n%6.2f\n", p->x + c * d);
348   fprintf(fh, "20\n%6.2f\n", p->y + s * d);
349   fprintf(fh, "30\n%6.2f\n", p->z);
350}
351
352void
353DXF::passage(const img_point *p, double angle, double d1, double d2)
354{
355   fprintf(fh, "0\nSOLID\n");
356   fprintf(fh, "8\nPassages\n"); /* Layer */
357   double s = sin(rad(angle));
358   double c = cos(rad(angle));
359   double x1 = p->x + c * d1;
360   double y1 = p->y + s * d1;
361   double x2 = p->x - c * d2;
362   double y2 = p->y - s * d2;
363   if (*pending) {
364       fputs(pending, fh);
365       fprintf(fh, "12\n%6.2f\n22\n%6.2f\n32\n%6.2f\n"
366                   "13\n%6.2f\n23\n%6.2f\n33\n%6.2f\n",
367                   x1, y1, p->z,
368                   x2, y2, p->z);
369   }
370   sprintf(pending, "10\n%6.2f\n20\n%6.2f\n30\n%6.2f\n"
371                    "11\n%6.2f\n21\n%6.2f\n31\n%6.2f\n",
372                    x1, y1, p->z,
373                    x2, y2, p->z);
374}
375
376void
377DXF::tube_end()
378{
379   *pending = '\0';
380   if (to_close) {
381      fputs(to_close, fh);
382      to_close = NULL;
383   }
384}
385
386void
387DXF::footer()
388{
389   fprintf(fh, "000\nENDSEC\n");
390   fprintf(fh, "000\nEOF\n");
391}
392
393class Skencil : public ExportFilter {
394  public:
395    Skencil() { }
396    const int * passes() const;
397    void header(const char *, const char *, time_t);
398    void start_pass(int layer);
399    void line(const img_point *, const img_point *, bool, bool);
400    void label(const img_point *, const char *, bool, int);
401    void cross(const img_point *, bool);
402    void footer();
403};
404
405const int *
406Skencil::passes() const
407{
408    static const int skencil_passes[] = { LEGS|SURF, STNS, LABELS, 0 };
409    return skencil_passes;
410}
411
412void
413Skencil::header(const char *, const char *, time_t)
414{
415   fprintf(fh, "##Sketch 1 2\n"); /* File format version */
416   fprintf(fh, "document()\n");
417   fprintf(fh, "layout((%.3f,%.3f),0)\n",
418           (max_x - min_x) * factor, (max_y - min_y) * factor);
419}
420
421void
422Skencil::start_pass(int layer)
423{
424   fprintf(fh, "layer('%s',1,1,0,0,(0,0,0))\n", layer_name(layer));
425}
426
427void
428Skencil::line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
429{
430   (void)fSurface; /* unused */
431   if (fPendingMove) {
432       fprintf(fh, "b()\n");
433       fprintf(fh, "bs(%.3f,%.3f,%.3f)\n", p1->x * factor, p1->y * factor, 0.0);
434   }
435   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n", p->x * factor, p->y * factor, 0.0);
436}
437
438void
439Skencil::label(const img_point *p, const char *s, bool fSurface, int)
440{
441   (void)fSurface; /* unused */
442   fprintf(fh, "fp((0,0,0))\n");
443   fprintf(fh, "le()\n");
444   fprintf(fh, "Fn('Times-Roman')\n");
445   fprintf(fh, "Fs(5)\n");
446   fprintf(fh, "txt('");
447   while (*s) {
448      int ch = *s++;
449      if (ch == '\'' || ch == '\\') PUTC('\\', fh);
450      PUTC(ch, fh);
451   }
452   fprintf(fh, "',(%.3f,%.3f))\n", p->x * factor, p->y * factor);
453}
454
455void
456Skencil::cross(const img_point *p, bool fSurface)
457{
458   (void)fSurface; /* unused */
459   fprintf(fh, "b()\n");
460   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
461           p->x * factor - marker_size, p->y * factor - marker_size, 0.0);
462   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
463           p->x * factor + marker_size, p->y * factor + marker_size, 0.0);
464   fprintf(fh, "bn()\n");
465   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
466           p->x * factor + marker_size, p->y * factor - marker_size, 0.0);
467   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
468           p->x * factor - marker_size, p->y * factor + marker_size, 0.0);
469}
470
471void
472Skencil::footer(void)
473{
474   fprintf(fh, "guidelayer('Guide Lines',1,0,0,1,(0,0,1))\n");
475   if (grid) {
476      fprintf(fh, "grid((0,0,%.3f,%.3f),1,(0,0,1),'Grid')\n",
477              grid * factor, grid * factor);
478   }
479}
480
481typedef struct point {
482   img_point p;
483   const char *label;
484   struct point *next;
485} point;
486
487#define HTAB_SIZE 0x2000
488
489static point **htab;
490
491static void
492set_name(const img_point *p, const char *s)
493{
494   int hash;
495   point *pt;
496   union {
497      char data[sizeof(int) * 3];
498      int x[3];
499   } u;
500
501   u.x[0] = (int)(p->x * 100);
502   u.x[1] = (int)(p->y * 100);
503   u.x[2] = (int)(p->z * 100);
504   hash = (hash_data(u.data, sizeof(int) * 3) & (HTAB_SIZE - 1));
505   for (pt = htab[hash]; pt; pt = pt->next) {
506      if (pt->p.x == p->x && pt->p.y == p->y && pt->p.z == p->z) {
507         /* already got name for these coordinates */
508         /* FIXME: what about multiple names for the same station? */
509         return;
510      }
511   }
512
513   pt = osnew(point);
514   pt->label = osstrdup(s);
515   pt->p = *p;
516   pt->next = htab[hash];
517   htab[hash] = pt;
518
519   return;
520}
521
522static const char *
523find_name(const img_point *p)
524{
525   int hash;
526   point *pt;
527   union {
528      char data[sizeof(int) * 3];
529      int x[3];
530   } u;
531   SVX_ASSERT(p);
532
533   u.x[0] = (int)(p->x * 100);
534   u.x[1] = (int)(p->y * 100);
535   u.x[2] = (int)(p->z * 100);
536   hash = (hash_data(u.data, sizeof(int) * 3) & (HTAB_SIZE - 1));
537   for (pt = htab[hash]; pt; pt = pt->next) {
538      if (pt->p.x == p->x && pt->p.y == p->y && pt->p.z == p->z)
539         return pt->label;
540   }
541   return "?";
542}
543
544class SVG : public ExportFilter {
545    const char * to_close;
546    bool close_g;
547    char pending[1024];
548
549  public:
550    SVG() : to_close(NULL), close_g(false) { pending[0] = '\0'; }
551    const int * passes() const;
552    void header(const char *, const char *, time_t);
553    void start_pass(int layer);
554    void line(const img_point *, const img_point *, bool, bool);
555    void label(const img_point *, const char *, bool, int);
556    void cross(const img_point *, bool);
557    void xsect(const img_point *, double, double, double);
558    void wall(const img_point *, double, double);
559    void passage(const img_point *, double, double, double);
560    void tube_end();
561    void footer();
562};
563
564const int *
565SVG::passes() const
566{
567    static const int svg_passes[] = {
568        PASG, LEGS|SURF, XSECT, WALL1, WALL2, LABELS, STNS, 0
569    };
570    return svg_passes;
571}
572
573void
574SVG::header(const char * title, const char *, time_t)
575{
576   size_t i;
577   htab = (point **)osmalloc(HTAB_SIZE * ossizeof(point *));
578   for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
579   fprintf(fh, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
580   double width = (max_x - min_x) * factor + SVG_MARGIN * 2;
581   double height = (max_y - min_y) * factor + SVG_MARGIN * 2;
582   fprintf(fh, "<svg version=\"1.1\" baseProfile=\"full\"\n"
583               "xmlns=\"http://www.w3.org/2000/svg\"\n"
584               "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
585               "xmlns:ev=\"http://www.w3.org/2001/xml-events\"\n"
586               "width=\"%.3f%s\" height=\"%.3f%s\"\n"
587               "viewBox=\"0 0 %0.3f %0.3f\">\n",
588           width, unit, height, unit, width, height);
589   if (title && title[0]) {
590       fputs("<title>", fh);
591       html_escape(fh, title);
592       fputs("</title>\n", fh);
593   }
594   fprintf(fh, "<g transform=\"translate(%.3f %.3f)\">\n",
595           SVG_MARGIN - min_x * factor, SVG_MARGIN + max_y * factor);
596   to_close = NULL;
597   close_g = false;
598}
599
600void
601SVG::start_pass(int layer)
602{
603   if (to_close) {
604      fputs(to_close, fh);
605      to_close = NULL;
606   }
607   if (close_g) {
608      fprintf(fh, "</g>\n");
609   }
610   fprintf(fh, "<g id=\"%s\"", layer_name(layer));
611   if (layer & LEGS)
612      fprintf(fh, " stroke=\"black\" fill=\"none\" stroke-width=\"0.4px\"");
613   else if (layer & STNS)
614      fprintf(fh, " stroke=\"black\" fill=\"none\" stroke-width=\"0.05px\"");
615   else if (layer & LABELS)
616      fprintf(fh, " font-size=\"%.3fem\"", text_height);
617   else if (layer & XSECT)
618      fprintf(fh, " stroke=\"grey\" fill=\"none\" stroke-width=\"0.1px\"");
619   else if (layer & WALLS)
620      fprintf(fh, " stroke=\"black\" fill=\"none\" stroke-width=\"0.1px\"");
621   else if (layer & PASG)
622      fprintf(fh, " stroke=\"none\" fill=\"peru\"");
623   fprintf(fh, ">\n");
624
625   close_g = true;
626}
627
628void
629SVG::line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
630{
631   (void)fSurface; /* unused */
632   if (fPendingMove) {
633       if (to_close) {
634           fputs(to_close, fh);
635       }
636       fprintf(fh, "<path d=\"M%.3f %.3f", p1->x * factor, p1->y * -factor);
637   }
638   fprintf(fh, "L%.3f %.3f", p->x * factor, p->y * -factor);
639   to_close = "\"/>\n";
640}
641
642void
643SVG::label(const img_point *p, const char *s, bool fSurface, int)
644{
645   (void)fSurface; /* unused */
646   fprintf(fh, "<text transform=\"translate(%.3f %.3f)\">",
647           p->x * factor, p->y * -factor);
648   html_escape(fh, s);
649   fputs("</text>\n", fh);
650   set_name(p, s);
651}
652
653void
654SVG::cross(const img_point *p, bool fSurface)
655{
656   (void)fSurface; /* unused */
657   fprintf(fh, "<circle id=\"%s\" cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\"/>\n",
658           find_name(p), p->x * factor, p->y * -factor, marker_size * SQRT_2);
659   fprintf(fh, "<path d=\"M%.3f %.3fL%.3f %.3fM%.3f %.3fL%.3f %.3f\"/>\n",
660           p->x * factor - marker_size, p->y * -factor - marker_size,
661           p->x * factor + marker_size, p->y * -factor + marker_size,
662           p->x * factor + marker_size, p->y * -factor - marker_size,
663           p->x * factor - marker_size, p->y * -factor + marker_size);
664}
665
666void
667SVG::xsect(const img_point *p, double angle, double d1, double d2)
668{
669   double s = sin(rad(angle));
670   double c = cos(rad(angle));
671   fprintf(fh, "<path d=\"M%.3f %.3fL%.3f %.3f\"/>\n",
672           (p->x + c * d1) * factor, (p->y + s * d1) * -factor,
673           (p->x - c * d2) * factor, (p->y - s * d2) * -factor);
674}
675
676void
677SVG::wall(const img_point *p, double angle, double d)
678{
679   if (!to_close) {
680       fprintf(fh, "<path d=\"M");
681       to_close = "\"/>\n";
682   } else {
683       fprintf(fh, "L");
684   }
685   double s = sin(rad(angle));
686   double c = cos(rad(angle));
687   fprintf(fh, "%.3f %.3f", (p->x + c * d) * factor, (p->y + s * d) * -factor);
688}
689
690void
691SVG::passage(const img_point *p, double angle, double d1, double d2)
692{
693   double s = sin(rad(angle));
694   double c = cos(rad(angle));
695   double x1 = (p->x + c * d1) * factor;
696   double y1 = (p->y + s * d1) * -factor;
697   double x2 = (p->x - c * d2) * factor;
698   double y2 = (p->y - s * d2) * -factor;
699   if (*pending) {
700       fputs(pending, fh);
701       fprintf(fh, "L%.3f %.3fL%.3f %.3fZ\"/>\n", x2, y2, x1, y1);
702   }
703   sprintf(pending, "<path d=\"M%.3f %.3fL%.3f %.3f", x1, y1, x2, y2);
704}
705
706void
707SVG::tube_end()
708{
709   *pending = '\0';
710   if (to_close) {
711      fputs(to_close, fh);
712      to_close = NULL;
713   }
714}
715
716void
717SVG::footer()
718{
719   if (to_close) {
720      fputs(to_close, fh);
721      to_close = NULL;
722   }
723   if (close_g) {
724      fprintf(fh, "</g>\n");
725      close_g = false;
726   }
727   fprintf(fh, "</g>\n</svg>\n");
728}
729
730class PLT : public ExportFilter {
731    string escaped;
732
733    const char * find_name_plt(const img_point *p);
734
735  public:
736    PLT() { }
737    const int * passes() const;
738    void header(const char *, const char *, time_t);
739    void line(const img_point *, const img_point *, bool, bool);
740    void label(const img_point *, const char *, bool, int);
741    void footer();
742};
743
744const int *
745PLT::passes() const
746{
747    static const int plt_passes[] = { LABELS, LEGS|SURF, 0 };
748    return plt_passes;
749}
750
751void
752PLT::header(const char *title, const char *, time_t)
753{
754   size_t i;
755   htab = (point **)osmalloc(HTAB_SIZE * ossizeof(point *));
756   for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
757   /* Survex is E, N, Alt - PLT file is N, E, Alt */
758   fprintf(fh, "Z %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
759           min_y / METRES_PER_FOOT, max_y / METRES_PER_FOOT,
760           min_x / METRES_PER_FOOT, max_x / METRES_PER_FOOT,
761           min_z / METRES_PER_FOOT, max_z / METRES_PER_FOOT);
762   fprintf(fh, "N%s D 1 1 1 C%s\r\n", survey ? survey : "X",
763           (title && title[0]) ? title : "X");
764}
765
766void
767PLT::line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
768{
769   (void)fSurface; /* unused */
770   if (fPendingMove) {
771       /* Survex is E, N, Alt - PLT file is N, E, Alt */
772       fprintf(fh, "M %.3f %.3f %.3f ",
773               p1->y / METRES_PER_FOOT, p1->x / METRES_PER_FOOT, p1->z / METRES_PER_FOOT);
774       /* dummy passage dimensions are required to avoid compass bug */
775       fprintf(fh, "S%s P -9 -9 -9 -9\r\n", find_name_plt(p1));
776   }
777   /* Survex is E, N, Alt - PLT file is N, E, Alt */
778   fprintf(fh, "D %.3f %.3f %.3f ",
779           p->y / METRES_PER_FOOT, p->x / METRES_PER_FOOT, p->z / METRES_PER_FOOT);
780   /* dummy passage dimensions are required to avoid compass bug */
781   fprintf(fh, "S%s P -9 -9 -9 -9\r\n", find_name_plt(p));
782}
783
784const char *
785PLT::find_name_plt(const img_point *p)
786{
787    const char * s = find_name(p);
788    escaped.resize(0);
789
790    // PLT format can't handle spaces or control characters, so escape them
791    // like in URLs (an arbitrary choice of escaping, but at least a familiar
792    // one and % isn't likely to occur in station names).
793    const char * q;
794    for (q = s; *q; ++q) {
795        unsigned char ch = *q;
796        if (ch <= ' ' || ch == '%') {
797            escaped.append(s, q - s);
798            escaped += '%';
799            escaped += "0123456789abcdef"[ch >> 4];
800            escaped += "0123456789abcdef"[ch & 0x0f];
801            s = q + 1;
802        }
803    }
804    if (!escaped.empty()) {
805        escaped.append(s, q - s);
806        return escaped.c_str();
807    }
808    return s;
809}
810
811void
812PLT::label(const img_point *p, const char *s, bool fSurface, int)
813{
814   (void)fSurface; /* unused */
815   set_name(p, s);
816}
817
818void
819PLT::footer(void)
820{
821   /* Survex is E, N, Alt - PLT file is N, E, Alt */
822   fprintf(fh, "X %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
823           min_y / METRES_PER_FOOT, max_y / METRES_PER_FOOT,
824           min_x / METRES_PER_FOOT, max_x / METRES_PER_FOOT,
825           min_z / METRES_PER_FOOT, max_z / METRES_PER_FOOT);
826   /* Yucky DOS "end of textfile" marker */
827   PUTC('\x1a', fh);
828}
829
830class EPS : public ExportFilter {
831  public:
832    EPS() { }
833    void header(const char *, const char *, time_t);
834    void line(const img_point *, const img_point *, bool, bool);
835    void label(const img_point *, const char *, bool, int);
836    void cross(const img_point *, bool);
837    void footer();
838};
839
840void
841EPS::header(const char *title, const char *, time_t)
842{
843   const char * fontname_labels = "helvetica"; // FIXME
844   int fontsize_labels = 10; // FIXME
845   fputs("%!PS-Adobe-2.0 EPSF-1.2\n", fh);
846   fputs("%%Creator: Survex "VERSION" EPS Output Filter\n", fh);
847
848   if (title && title[0])
849       fprintf(fh, "%%%%Title: %s\n", title);
850
851   char buf[64];
852   time_t now = time(NULL);
853   if (strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %Z\n", localtime(&now))) {
854      fputs("%%CreationDate: ", fh);
855      fputs(buf, fh);
856   }
857
858   string name;
859#if defined(HAVE_GETPWUID) && !defined(__DJGPP__)
860   struct passwd * ent = getpwuid(getuid());
861   if (ent && ent->pw_gecos[0]) name = ent->pw_gecos;
862#endif
863   if (name.empty()) {
864       name = ::wxGetUserName().mb_str();
865       if (name.empty()) {
866           name = ::wxGetUserId().mb_str();
867       }
868   }
869   if (!name.empty()) {
870       fprintf(fh, "%%%%For: %s\n", name.c_str());
871   }
872
873   fprintf(fh, "%%%%BoundingBox: %d %d %d %d\n",
874           int(floor(min_x * factor)), int(floor(min_y * factor)),
875           int(ceil(max_x * factor)), int(ceil(max_y * factor)));
876   fprintf(fh, "%%%%HiResBoundingBox: %.4f %.4f %.4f %.4f\n",
877           min_x * factor, min_y * factor, max_x * factor, max_y * factor);
878   fputs("%%LanguageLevel: 1\n"
879         "%%PageOrder: Ascend\n"
880         "%%Pages: 1\n"
881         "%%Orientation: Portrait\n", fh);
882
883   fprintf(fh, "%%%%DocumentFonts: %s\n", fontname_labels);
884
885   fputs("%%EndComments\n"
886         "%%Page 1 1\n"
887         "save countdictstack mark\n", fh);
888
889   /* this code adapted from a2ps */
890   fputs("%%BeginResource: encoding ISO88591Encoding\n"
891         "/ISO88591Encoding [\n", fh);
892   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
893   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
894   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
895   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
896   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
897   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
898   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
899   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
900   fputs(
901"/space /exclam /quotedbl /numbersign\n"
902"/dollar /percent /ampersand /quoteright\n"
903"/parenleft /parenright /asterisk /plus\n"
904"/comma /minus /period /slash\n"
905"/zero /one /two /three\n"
906"/four /five /six /seven\n"
907"/eight /nine /colon /semicolon\n"
908"/less /equal /greater /question\n"
909"/at /A /B /C /D /E /F /G\n"
910"/H /I /J /K /L /M /N /O\n"
911"/P /Q /R /S /T /U /V /W\n"
912"/X /Y /Z /bracketleft\n"
913"/backslash /bracketright /asciicircum /underscore\n"
914"/quoteleft /a /b /c /d /e /f /g\n"
915"/h /i /j /k /l /m /n /o\n"
916"/p /q /r /s /t /u /v /w\n"
917"/x /y /z /braceleft\n"
918"/bar /braceright /asciitilde /.notdef\n", fh);
919   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
920   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
921   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
922   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
923   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
924   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
925   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
926   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
927   fputs(
928"/space /exclamdown /cent /sterling\n"
929"/currency /yen /brokenbar /section\n"
930"/dieresis /copyright /ordfeminine /guillemotleft\n"
931"/logicalnot /hyphen /registered /macron\n"
932"/degree /plusminus /twosuperior /threesuperior\n"
933"/acute /mu /paragraph /bullet\n"
934"/cedilla /onesuperior /ordmasculine /guillemotright\n"
935"/onequarter /onehalf /threequarters /questiondown\n"
936"/Agrave /Aacute /Acircumflex /Atilde\n"
937"/Adieresis /Aring /AE /Ccedilla\n"
938"/Egrave /Eacute /Ecircumflex /Edieresis\n"
939"/Igrave /Iacute /Icircumflex /Idieresis\n"
940"/Eth /Ntilde /Ograve /Oacute\n"
941"/Ocircumflex /Otilde /Odieresis /multiply\n"
942"/Oslash /Ugrave /Uacute /Ucircumflex\n"
943"/Udieresis /Yacute /Thorn /germandbls\n"
944"/agrave /aacute /acircumflex /atilde\n"
945"/adieresis /aring /ae /ccedilla\n"
946"/egrave /eacute /ecircumflex /edieresis\n"
947"/igrave /iacute /icircumflex /idieresis\n"
948"/eth /ntilde /ograve /oacute\n"
949"/ocircumflex /otilde /odieresis /divide\n"
950"/oslash /ugrave /uacute /ucircumflex\n"
951"/udieresis /yacute /thorn /ydieresis\n"
952"] def\n"
953"%%EndResource\n", fh);
954
955   /* this code adapted from a2ps */
956   fputs(
957"/reencode {\n" /* def */
958"dup length 5 add dict begin\n"
959"{\n" /* forall */
960"1 index /FID ne\n"
961"{ def }{ pop pop } ifelse\n"
962"} forall\n"
963"/Encoding exch def\n"
964
965/* Use the font's bounding box to determine the ascent, descent,
966 * and overall height; don't forget that these values have to be
967 * transformed using the font's matrix.
968 * We use `load' because sometimes BBox is executable, sometimes not.
969 * Since we need 4 numbers and not an array avoid BBox from being executed
970 */
971"/FontBBox load aload pop\n"
972"FontMatrix transform /Ascent exch def pop\n"
973"FontMatrix transform /Descent exch def pop\n"
974"/FontHeight Ascent Descent sub def\n"
975
976/* Define these in case they're not in the FontInfo (also, here
977 * they're easier to get to.
978 */
979"/UnderlinePosition 1 def\n"
980"/UnderlineThickness 1 def\n"
981
982/* Get the underline position and thickness if they're defined. */
983"currentdict /FontInfo known {\n"
984"FontInfo\n"
985
986"dup /UnderlinePosition known {\n"
987"dup /UnderlinePosition get\n"
988"0 exch FontMatrix transform exch pop\n"
989"/UnderlinePosition exch def\n"
990"} if\n"
991
992"dup /UnderlineThickness known {\n"
993"/UnderlineThickness get\n"
994"0 exch FontMatrix transform exch pop\n"
995"/UnderlineThickness exch def\n"
996"} if\n"
997
998"} if\n"
999"currentdict\n"
1000"end\n"
1001"} bind def\n", fh);
1002
1003   fprintf(fh, "/lab ISO88591Encoding /%s findfont reencode definefont pop\n",
1004           fontname_labels);
1005
1006   fprintf(fh, "/lab findfont %d scalefont setfont\n", int(fontsize_labels));
1007
1008   fprintf(fh, "0.1 setlinewidth\n");
1009
1010#if 0
1011   /* C<digit> changes colour */
1012   /* FIXME: read from ini */
1013   {
1014      size_t i;
1015      for (i = 0; i < sizeof(colour) / sizeof(colour[0]); ++i) {
1016         fprintf(fh, "/C%u {stroke %.3f %.3f %.3f setrgbcolor} def\n", i,
1017                 (double)(colour[i] & 0xff0000) / 0xff0000,
1018                 (double)(colour[i] & 0xff00) / 0xff00,
1019                 (double)(colour[i] & 0xff) / 0xff);
1020      }
1021   }
1022   fputs("C0\n", fh);
1023#endif
1024
1025   /* Postscript definition for drawing a cross */
1026   fprintf(fh, "/X {stroke moveto %.2f %.2f rmoveto %.2f %.2f rlineto "
1027           "%.2f 0 rmoveto %.2f %.2f rlineto %.2f %.2f rmoveto} def\n",
1028           -marker_size, -marker_size,  marker_size * 2, marker_size * 2,
1029           -marker_size * 2,  marker_size * 2, -marker_size * 2,
1030           -marker_size, marker_size );
1031
1032   /* define some functions to keep file short */
1033   fputs("/M {stroke moveto} def\n"
1034         "/L {lineto} def\n"
1035/*       "/R {rlineto} def\n" */
1036         "/S {show} def\n", fh);
1037
1038   fprintf(fh, "gsave %.8f dup scale\n", factor);
1039#if 0
1040   if (grid > 0) {
1041      double x, y;
1042      x = floor(min_x / grid) * grid + grid;
1043      y = floor(min_y / grid) * grid + grid;
1044#ifdef DEBUG_CAD3D
1045      printf("x_min: %f  y_min: %f\n", x, y);
1046#endif
1047      while (x < max_x) {
1048         /* horizontal line */
1049         fprintf(fh, "0\nLINE\n");
1050         fprintf(fh, "8\nGrid\n"); /* Layer */
1051         fprintf(fh, "10\n%6.2f\n", x);
1052         fprintf(fh, "20\n%6.2f\n", min_y);
1053         fprintf(fh, "30\n0\n");
1054         fprintf(fh, "11\n%6.2f\n", x);
1055         fprintf(fh, "21\n%6.2f\n", max_y);
1056         fprintf(fh, "31\n0\n");
1057         x += grid;
1058      }
1059      while (y < max_y) {
1060         /* vertical line */
1061         fprintf(fh, "0\nLINE\n");
1062         fprintf(fh, "8\nGrid\n"); /* Layer */
1063         fprintf(fh, "10\n%6.2f\n", min_x);
1064         fprintf(fh, "20\n%6.2f\n", y);
1065         fprintf(fh, "30\n0\n");
1066         fprintf(fh, "11\n%6.2f\n", max_x);
1067         fprintf(fh, "21\n%6.2f\n", y);
1068         fprintf(fh, "31\n0\n");
1069         y += grid;
1070      }
1071   }
1072#endif
1073}
1074
1075void
1076EPS::line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
1077{
1078   (void)fSurface; /* unused */
1079   if (fPendingMove) {
1080       fprintf(fh, "%.2f %.2f M\n", p1->x, p1->y);
1081   }
1082   fprintf(fh, "%.2f %.2f L\n", p->x, p->y);
1083}
1084
1085void
1086EPS::label(const img_point *p, const char *s, bool /*fSurface*/, int)
1087{
1088   fprintf(fh, "%.2f %.2f M\n", p->x, p->y);
1089   PUTC('(', fh);
1090   while (*s) {
1091       unsigned char ch = *s++;
1092       switch (ch) {
1093           case '(': case ')': case '\\': /* need to escape these characters */
1094               PUTC('\\', fh);
1095               PUTC(ch, fh);
1096               break;
1097           default:
1098               PUTC(ch, fh);
1099               break;
1100       }
1101   }
1102   fputs(") S\n", fh);
1103}
1104
1105void
1106EPS::cross(const img_point *p, bool fSurface)
1107{
1108   (void)fSurface; /* unused */
1109   fprintf(fh, "%.2f %.2f X\n", p->x, p->y);
1110}
1111
1112void
1113EPS::footer(void)
1114{
1115   fputs("stroke showpage grestore\n"
1116         "%%Trailer\n"
1117         "cleartomark countdictstack exch sub { end } repeat restore\n"
1118         "%%EOF\n", fh);
1119}
1120
1121bool
1122Export(const wxString &fnm_out, const wxString &title,
1123       const wxString &datestamp, time_t datestamp_numeric,
1124       const MainFrm * mainfrm,
1125       double pan, double tilt, int show_mask, export_format format,
1126       const char * input_projection,
1127       double grid_, double text_height_, double marker_size_)
1128{
1129   int fPendingMove = 0;
1130   img_point p, p1;
1131   double s = 0, c = 0;
1132   const int *pass;
1133   bool elevation = (tilt == 0.0);
1134
1135   grid = grid_;
1136   text_height = text_height_;
1137   marker_size = marker_size_;
1138
1139#if 0 // FIXME: allow these to be set from aven somehow!
1140       case 'r': /* Reduction factor */
1141         scale = cmdline_double_arg();
1142         break;
1143       case 's':
1144         survey = optarg;
1145         break;
1146#endif
1147
1148   ExportFilter * filt;
1149   switch (format) {
1150       case FMT_DXF:
1151           filt = new DXF;
1152           break;
1153       case FMT_EPS:
1154           filt = new EPS;
1155           factor = POINTS_PER_MM * 1000.0 / scale;
1156           break;
1157       case FMT_GPX:
1158           filt = new GPX(input_projection);
1159           show_mask |= FULL_COORDS;
1160           break;
1161       case FMT_HPGL:
1162           filt = new HPGL;
1163           // factor = POINTS_PER_MM * 1000.0 / scale;
1164           break;
1165       case FMT_KML:
1166           filt = new KML(input_projection);
1167           show_mask |= FULL_COORDS;
1168           break;
1169       case FMT_PLT:
1170           filt = new PLT;
1171           show_mask |= FULL_COORDS;
1172           break;
1173       case FMT_SK:
1174           filt = new Skencil;
1175           factor = POINTS_PER_MM * 1000.0 / scale;
1176           break;
1177       case FMT_SVG:
1178           filt = new SVG;
1179           factor = 1000.0 / scale;
1180           break;
1181       default:
1182           return false;
1183   }
1184
1185   // FIXME: This should really use fn_str() - currently we probably can't
1186   // save to a Unicode path on wxmsw.
1187   if (!filt->fopen(fnm_out.mb_str())) {
1188       delete filt;
1189       return false;
1190   }
1191
1192   if (elevation) {
1193      s = sin(rad(pan));
1194      c = cos(rad(pan));
1195   }
1196
1197   /* Get drawing corners */
1198   min_x = min_y = min_z = HUGE_VAL;
1199   max_x = max_y = max_z = -HUGE_VAL;
1200   list<traverse>::const_iterator trav = mainfrm->traverses_begin();
1201   list<traverse>::const_iterator tend = mainfrm->traverses_end();
1202   for ( ; trav != tend; ++trav) {
1203        vector<PointInfo>::const_iterator pos = trav->begin();
1204        vector<PointInfo>::const_iterator end = trav->end();
1205        for ( ; pos != end; ++pos) {
1206            p.x = pos->GetX();
1207            p.y = pos->GetY();
1208            p.z = pos->GetZ();
1209
1210            if (elevation) {
1211                double xnew = p.x * c - p.y * s;
1212                double znew = - p.x * s - p.y * c;
1213                p.y = p.z;
1214                p.z = znew;
1215                p.x = xnew;
1216            }
1217
1218            if (p.x < min_x) min_x = p.x;
1219            if (p.x > max_x) max_x = p.x;
1220            if (p.y < min_y) min_y = p.y;
1221            if (p.y > max_y) max_y = p.y;
1222            if (p.z < min_z) min_z = p.z;
1223            if (p.z > max_z) max_z = p.z;
1224        }
1225   }
1226   {
1227        list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1228        list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1229        for ( ; pos != end; ++pos) {
1230            p.x = (*pos)->GetX();
1231            p.y = (*pos)->GetY();
1232            p.z = (*pos)->GetZ();
1233
1234            if (elevation) {
1235                double xnew = p.x * c - p.y * s;
1236                double znew = - p.x * s - p.y * c;
1237                p.y = p.z;
1238                p.z = znew;
1239                p.x = xnew;
1240            }
1241
1242            if (p.x < min_x) min_x = p.x;
1243            if (p.x > max_x) max_x = p.x;
1244            if (p.y < min_y) min_y = p.y;
1245            if (p.y > max_y) max_y = p.y;
1246            if (p.z < min_z) min_z = p.z;
1247            if (p.z > max_z) max_z = p.z;
1248        }
1249   }
1250
1251   if (grid > 0) {
1252      min_x -= grid / 2;
1253      max_x += grid / 2;
1254      min_y -= grid / 2;
1255      max_y += grid / 2;
1256   }
1257
1258   /* handle empty file gracefully */
1259   if (min_x > max_x) {
1260      min_x = min_y = min_z = 0;
1261      max_x = max_y = max_z = 0;
1262   }
1263
1264   double x_offset, y_offset, z_offset;
1265   if (show_mask & FULL_COORDS) {
1266       // Full coordinates.
1267       x_offset = mainfrm->GetOffset().GetX();
1268       y_offset = mainfrm->GetOffset().GetY();
1269       z_offset = mainfrm->GetOffset().GetZ();
1270   } else if (show_mask & CENTRED) {
1271       // Centred.
1272       x_offset = (min_x + max_x) * -0.5;
1273       y_offset = (min_y + max_y) * -0.5;
1274       z_offset = (min_z + max_z) * -0.5;
1275   } else {
1276       // Origin at lowest SW corner.
1277       x_offset = -min_x;
1278       y_offset = -min_y;
1279       z_offset = -min_z;
1280   }
1281   min_x += x_offset;
1282   max_x += x_offset;
1283   min_y += y_offset;
1284   max_y += y_offset;
1285   min_z += z_offset;
1286   max_z += z_offset;
1287
1288   /* Header */
1289   filt->header(title.mb_str(), datestamp.mb_str(), datestamp_numeric);
1290
1291   p1.x = p1.y = p1.z = 0; /* avoid compiler warning */
1292
1293   for (pass = filt->passes(); *pass; ++pass) {
1294      int pass_mask = show_mask & *pass;
1295      if (!pass_mask)
1296          continue;
1297      filt->start_pass(*pass);
1298      if (pass_mask & LEGS) {
1299          trav = mainfrm->traverses_begin();
1300          tend = mainfrm->traverses_end();
1301          for ( ; trav != tend; ++trav) {
1302             assert(trav->size() > 1);
1303             vector<PointInfo>::const_iterator pos = trav->begin();
1304             vector<PointInfo>::const_iterator end = trav->end();
1305             for ( ; pos != end; ++pos) {
1306                 p.x = pos->GetX() + x_offset;
1307                 p.y = pos->GetY() + y_offset;
1308                 p.z = pos->GetZ() + z_offset;
1309                 if (elevation) {
1310                     double xnew = p.x * c - p.y * s;
1311                     double znew = - p.x * s - p.y * c;
1312                     p.y = p.z;
1313                     p.z = znew;
1314                     p.x = xnew;
1315                 }
1316
1317                 if (pos == trav->begin()) {
1318                     // First point is move...
1319#ifdef DEBUG_CAD3D
1320                     printf("move to %9.2f %9.2f %9.2f\n",x,y,z);
1321#endif
1322                     fPendingMove = 1;
1323                 } else {
1324#ifdef DEBUG_CAD3D
1325                     printf("line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
1326#endif
1327                     filt->line(&p1, &p, false, fPendingMove);
1328                     fPendingMove = 0;
1329                 }
1330                 p1 = p;
1331             }
1332         }
1333      }
1334      if (pass_mask & SURF) {
1335          trav = mainfrm->surface_traverses_begin();
1336          tend = mainfrm->surface_traverses_end();
1337          for ( ; trav != tend; ++trav) {
1338             assert(trav->size() > 1);
1339             vector<PointInfo>::const_iterator pos = trav->begin();
1340             vector<PointInfo>::const_iterator end = trav->end();
1341             for ( ; pos != end; ++pos) {
1342                 p.x = pos->GetX() + x_offset;
1343                 p.y = pos->GetY() + y_offset;
1344                 p.z = pos->GetZ() + z_offset;
1345
1346                 if (elevation) {
1347                     double xnew = p.x * c - p.y * s;
1348                     double znew = - p.x * s - p.y * c;
1349                     p.y = p.z;
1350                     p.z = znew;
1351                     p.x = xnew;
1352                 }
1353
1354                 if (pos == trav->begin()) {
1355                     // First point is move...
1356#ifdef DEBUG_CAD3D
1357                     printf("surface move to %9.2f %9.2f %9.2f\n",x,y,z);
1358#endif
1359                     fPendingMove = 1;
1360                 } else {
1361#ifdef DEBUG_CAD3D
1362                     printf("surface line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
1363#endif
1364                     filt->line(&p1, &p, true, fPendingMove);
1365                     fPendingMove = 0;
1366                 }
1367                 p1 = p;
1368             }
1369         }
1370      }
1371      if (pass_mask & (STNS|LABELS|ENTS|FIXES|EXPORTS)) {
1372          list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1373          list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1374          for ( ; pos != end; ++pos) {
1375              p.x = (*pos)->GetX() + x_offset;
1376              p.y = (*pos)->GetY() + y_offset;
1377              p.z = (*pos)->GetZ() + z_offset;
1378
1379              if (elevation) {
1380                  double xnew = p.x * c - p.y * s;
1381                  double znew = - p.x * s - p.y * c;
1382                  p.y = p.z;
1383                  p.z = znew;
1384                  p.x = xnew;
1385              }
1386#ifdef DEBUG_CAD3D
1387              printf("label '%s' at %9.2f %9.2f %9.2f\n",(*pos)->GetText(),x,y,z);
1388#endif
1389              int type = 0;
1390              if ((pass_mask & ENTS) && (*pos)->IsEntrance()) {
1391                  type = ENTS;
1392              } else if ((pass_mask & FIXES) && (*pos)->IsFixedPt()) {
1393                  type = FIXES;
1394              } else if ((pass_mask & EXPORTS) && (*pos)->IsExportedPt())  {
1395                  type = EXPORTS;
1396              } else if (pass_mask & LABELS) {
1397                  type = LABELS;
1398              }
1399              /* Use !UNDERGROUND as the criterion - we want stations where a
1400               * surface and underground survey meet to be in the underground
1401               * layer */
1402              bool f_surface = !(*pos)->IsUnderground();
1403              if (type) {
1404                  const wxString & text = (*pos)->GetText();
1405                  filt->label(&p, text.mb_str(), f_surface, type);
1406              }
1407              if (pass_mask & STNS)
1408                  filt->cross(&p, f_surface);
1409          }
1410      }
1411      if (pass_mask & (XSECT|WALLS|PASG)) {
1412          list<vector<XSect> >::const_iterator tube = mainfrm->tubes_begin();
1413          list<vector<XSect> >::const_iterator tube_end = mainfrm->tubes_end();
1414          for ( ; tube != tube_end; ++tube) {
1415              vector<XSect>::const_iterator pos = tube->begin();
1416              vector<XSect>::const_iterator end = tube->end();
1417              for ( ; pos != end; ++pos) {
1418                  const XSect & xs = *pos;
1419                  p.x = xs.GetX() + x_offset;
1420                  p.y = xs.GetY() + y_offset;
1421                  p.z = xs.GetZ() + z_offset;
1422
1423                  if (elevation) {
1424                      double xnew = p.x * c - p.y * s;
1425                      double znew = - p.x * s - p.y * c;
1426                      p.y = p.z;
1427                      p.z = znew;
1428                      p.x = xnew;
1429                      if (pass_mask & XSECT)
1430                          filt->xsect(&p, 90, xs.GetU(), xs.GetD());
1431                      if (pass_mask & WALL1)
1432                          filt->wall(&p, 90, xs.GetU());
1433                      if (pass_mask & WALL2)
1434                          filt->wall(&p, 270, xs.GetD());
1435                      if (pass_mask & PASG)
1436                          filt->passage(&p, 90, xs.GetU(), xs.GetD());
1437                  } else {
1438                      if (pass_mask & XSECT)
1439                          filt->xsect(&p, xs.get_right_bearing() + 180, xs.GetL(), xs.GetR());
1440                      if (pass_mask & WALL1)
1441                          filt->wall(&p, xs.get_right_bearing() + 180, xs.GetL());
1442                      if (pass_mask & WALL2)
1443                          filt->wall(&p, xs.get_right_bearing(), xs.GetR());
1444                      if (pass_mask & PASG)
1445                          filt->passage(&p, xs.get_right_bearing() + 180, xs.GetL(), xs.GetR());
1446                  }
1447              }
1448              filt->tube_end();
1449          }
1450      }
1451   }
1452   filt->footer();
1453   delete filt;
1454   osfree(htab);
1455   htab = NULL;
1456   return true;
1457}
Note: See TracBrowser for help on using the repository browser.