source: git/src/export.cc @ c0d8e5d

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since c0d8e5d was c0d8e5d, checked in by Olly Betts <olly@…>, 9 years ago

src/export.cc: Eliminate more static variables.

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