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

line_contentsstereotravis-osx
Last change on this file since 1fe107a was 1fe107a, checked in by Olly Betts <olly@…>, 5 years ago

src/: Add JSON export.

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