source: git/src/export.cc @ cd4684a

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

src/export.cc: Eliminate use of static for text_height.

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