source: git/src/export.cc @ 586e78c

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

src/export.cc: Eliminate static double factor.

  • 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 "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 const char *unit = "mm";
109const double SVG_MARGIN = 5.0; // In units of "unit".
110
111static const char *survey = NULL;
112
113const int *
114ExportFilter::passes() const
115{
116    static const int default_passes[] = { LEGS|SURF|STNS|LABELS, 0 };
117    return default_passes;
118}
119
120class DXF : public ExportFilter {
121    const char * to_close;
122    /* for station labels */
123    double text_height;
124    char pending[1024];
125
126  public:
127    DXF(double text_height_)
128        : to_close(0), text_height(text_height_) { pending[0] = '\0'; }
129    const int * passes() const;
130    bool fopen(const char *fnm_out);
131    void header(const char *, const char *, time_t,
132                double min_x, double min_y, double min_z,
133                double max_x, double max_y, double max_z);
134    void line(const img_point *, const img_point *, bool, bool);
135    void label(const img_point *, const char *, bool, int);
136    void cross(const img_point *, bool);
137    void xsect(const img_point *, double, double, double);
138    void wall(const img_point *, double, double);
139    void passage(const img_point *, double, double, double);
140    void tube_end();
141    void footer();
142};
143
144const int *
145DXF::passes() const
146{
147    static const int dxf_passes[] = {
148        PASG, XSECT, WALL1, WALL2, LEGS|SURF|STNS|LABELS, 0
149    };
150    return dxf_passes;
151}
152
153bool
154DXF::fopen(const char *fnm_out)
155{
156    // DXF gets written as text rather than binary.
157    fh = ::fopen(fnm_out, "w");
158    return (fh != NULL);
159}
160
161void
162DXF::header(const char *, const char *, time_t,
163            double min_x, double min_y, double min_z,
164            double max_x, double max_y, double max_z)
165{
166   fprintf(fh, "0\nSECTION\n"
167               "2\nHEADER\n");
168   fprintf(fh, "9\n$EXTMIN\n"); /* lower left corner of drawing */
169   fprintf(fh, "10\n%#-.6f\n", min_x); /* x */
170   fprintf(fh, "20\n%#-.6f\n", min_y); /* y */
171   fprintf(fh, "30\n%#-.6f\n", min_z); /* min z */
172   fprintf(fh, "9\n$EXTMAX\n"); /* upper right corner of drawing */
173   fprintf(fh, "10\n%#-.6f\n", max_x); /* x */
174   fprintf(fh, "20\n%#-.6f\n", max_y); /* y */
175   fprintf(fh, "30\n%#-.6f\n", max_z); /* max z */
176   fprintf(fh, "9\n$PDMODE\n70\n3\n"); /* marker style as CROSS */
177   fprintf(fh, "9\n$PDSIZE\n40\n%6.2f\n", marker_size); /* marker size */
178   fprintf(fh, "0\nENDSEC\n");
179
180   fprintf(fh, "0\nSECTION\n"
181               "2\nTABLES\n");
182   fprintf(fh, "0\nTABLE\n" /* Define CONTINUOUS and DASHED line types. */
183               "2\nLTYPE\n"
184               "70\n10\n"
185               "0\nLTYPE\n"
186               "2\nCONTINUOUS\n"
187               "70\n64\n"
188               "3\nContinuous\n"
189               "72\n65\n"
190               "73\n0\n"
191               "40\n0.0\n"
192               "0\nLTYPE\n"
193               "2\nDASHED\n"
194               "70\n64\n"
195               "3\nDashed\n"
196               "72\n65\n"
197               "73\n2\n"
198               "40\n2.5\n"
199               "49\n1.25\n"
200               "49\n-1.25\n"
201               "0\nENDTAB\n");
202   fprintf(fh, "0\nTABLE\n"
203               "2\nLAYER\n");
204   fprintf(fh, "70\n10\n"); /* max # off layers in this DXF file : 10 */
205   /* First Layer: CentreLine */
206   fprintf(fh, "0\nLAYER\n2\nCentreLine\n");
207   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
208   fprintf(fh, "62\n5\n"); /* color: kept the same used by SpeleoGen */
209   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
210   /* Next Layer: Stations */
211   fprintf(fh, "0\nLAYER\n2\nStations\n");
212   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
213   fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
214   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
215   /* Next Layer: Labels */
216   fprintf(fh, "0\nLAYER\n2\nLabels\n");
217   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
218   fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
219   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
220   /* Next Layer: Surface */
221   fprintf(fh, "0\nLAYER\n2\nSurface\n");
222   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
223   fprintf(fh, "62\n5\n"); /* color */
224   fprintf(fh, "6\nDASHED\n"); /* linetype */
225   /* Next Layer: SurfaceStations */
226   fprintf(fh, "0\nLAYER\n2\nSurfaceStations\n");
227   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
228   fprintf(fh, "62\n7\n"); /* color */
229   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
230   /* Next Layer: SurfaceLabels */
231   fprintf(fh, "0\nLAYER\n2\nSurfaceLabels\n");
232   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
233   fprintf(fh, "62\n7\n"); /* color */
234   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
235   if (grid > 0) {
236      /* Next Layer: Grid */
237      fprintf(fh, "0\nLAYER\n2\nGrid\n");
238      fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
239      fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
240      fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
241   }
242   fprintf(fh, "0\nENDTAB\n"
243               "0\nENDSEC\n");
244
245   fprintf(fh, "0\nSECTION\n"
246               "2\nENTITIES\n");
247
248   if (grid > 0) {
249      double x, y;
250      x = floor(min_x / grid) * grid + grid;
251      y = floor(min_y / grid) * grid + grid;
252#ifdef DEBUG_CAD3D
253      printf("x_min: %f  y_min: %f\n", x, y);
254#endif
255      while (x < max_x) {
256         /* horizontal line */
257         fprintf(fh, "0\nLINE\n");
258         fprintf(fh, "8\nGrid\n"); /* Layer */
259         fprintf(fh, "10\n%6.2f\n", x);
260         fprintf(fh, "20\n%6.2f\n", min_y);
261         fprintf(fh, "30\n0\n");
262         fprintf(fh, "11\n%6.2f\n", x);
263         fprintf(fh, "21\n%6.2f\n", max_y);
264         fprintf(fh, "31\n0\n");
265         x += grid;
266      }
267      while (y < max_y) {
268         /* vertical line */
269         fprintf(fh, "0\nLINE\n");
270         fprintf(fh, "8\nGrid\n"); /* Layer */
271         fprintf(fh, "10\n%6.2f\n", min_x);
272         fprintf(fh, "20\n%6.2f\n", y);
273         fprintf(fh, "30\n0\n");
274         fprintf(fh, "11\n%6.2f\n", max_x);
275         fprintf(fh, "21\n%6.2f\n", y);
276         fprintf(fh, "31\n0\n");
277         y += grid;
278      }
279   }
280}
281
282void
283DXF::line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
284{
285   (void)fPendingMove; /* unused */
286   fprintf(fh, "0\nLINE\n");
287   fprintf(fh, fSurface ? "8\nSurface\n" : "8\nCentreLine\n"); /* Layer */
288   fprintf(fh, "10\n%6.2f\n", p1->x);
289   fprintf(fh, "20\n%6.2f\n", p1->y);
290   fprintf(fh, "30\n%6.2f\n", p1->z);
291   fprintf(fh, "11\n%6.2f\n", p->x);
292   fprintf(fh, "21\n%6.2f\n", p->y);
293   fprintf(fh, "31\n%6.2f\n", p->z);
294}
295
296void
297DXF::label(const img_point *p, const char *s, bool fSurface, int)
298{
299   /* write station label to dxf file */
300   fprintf(fh, "0\nTEXT\n");
301   fprintf(fh, fSurface ? "8\nSurfaceLabels\n" : "8\nLabels\n"); /* Layer */
302   fprintf(fh, "10\n%6.2f\n", p->x);
303   fprintf(fh, "20\n%6.2f\n", p->y);
304   fprintf(fh, "30\n%6.2f\n", p->z);
305   fprintf(fh, "40\n%6.2f\n", text_height);
306   fprintf(fh, "1\n%s\n", s);
307}
308
309void
310DXF::cross(const img_point *p, bool fSurface)
311{
312   /* write station marker to dxf file */
313   fprintf(fh, "0\nPOINT\n");
314   fprintf(fh, fSurface ? "8\nSurfaceStations\n" : "8\nStations\n"); /* Layer */
315   fprintf(fh, "10\n%6.2f\n", p->x);
316   fprintf(fh, "20\n%6.2f\n", p->y);
317   fprintf(fh, "30\n%6.2f\n", p->z);
318}
319
320void
321DXF::xsect(const img_point *p, double angle, double d1, double d2)
322{
323   double s = sin(rad(angle));
324   double c = cos(rad(angle));
325   fprintf(fh, "0\nLINE\n");
326   fprintf(fh, "8\nCross-sections\n"); /* Layer */
327   fprintf(fh, "10\n%6.2f\n", p->x + c * d1);
328   fprintf(fh, "20\n%6.2f\n", p->y + s * d1);
329   fprintf(fh, "30\n%6.2f\n", p->z);
330   fprintf(fh, "11\n%6.2f\n", p->x - c * d2);
331   fprintf(fh, "21\n%6.2f\n", p->y - s * d2);
332   fprintf(fh, "31\n%6.2f\n", p->z);
333}
334
335void
336DXF::wall(const img_point *p, double angle, double d)
337{
338   if (!to_close) {
339       fprintf(fh, "0\nPOLYLINE\n");
340       fprintf(fh, "8\nWalls\n"); /* Layer */
341       fprintf(fh, "70\n0\n"); /* bit 0 == 0 => Open polyline */
342       to_close = "0\nSEQEND\n";
343   }
344   double s = sin(rad(angle));
345   double c = cos(rad(angle));
346   fprintf(fh, "0\nVERTEX\n");
347   fprintf(fh, "8\nWalls\n"); /* Layer */
348   fprintf(fh, "10\n%6.2f\n", p->x + c * d);
349   fprintf(fh, "20\n%6.2f\n", p->y + s * d);
350   fprintf(fh, "30\n%6.2f\n", p->z);
351}
352
353void
354DXF::passage(const img_point *p, double angle, double d1, double d2)
355{
356   fprintf(fh, "0\nSOLID\n");
357   fprintf(fh, "8\nPassages\n"); /* Layer */
358   double s = sin(rad(angle));
359   double c = cos(rad(angle));
360   double x1 = p->x + c * d1;
361   double y1 = p->y + s * d1;
362   double x2 = p->x - c * d2;
363   double y2 = p->y - s * d2;
364   if (*pending) {
365       fputs(pending, fh);
366       fprintf(fh, "12\n%6.2f\n22\n%6.2f\n32\n%6.2f\n"
367                   "13\n%6.2f\n23\n%6.2f\n33\n%6.2f\n",
368                   x1, y1, p->z,
369                   x2, y2, p->z);
370   }
371   sprintf(pending, "10\n%6.2f\n20\n%6.2f\n30\n%6.2f\n"
372                    "11\n%6.2f\n21\n%6.2f\n31\n%6.2f\n",
373                    x1, y1, p->z,
374                    x2, y2, p->z);
375}
376
377void
378DXF::tube_end()
379{
380   *pending = '\0';
381   if (to_close) {
382      fputs(to_close, fh);
383      to_close = NULL;
384   }
385}
386
387void
388DXF::footer()
389{
390   fprintf(fh, "000\nENDSEC\n");
391   fprintf(fh, "000\nEOF\n");
392}
393
394class Skencil : public ExportFilter {
395    double factor;
396  public:
397    Skencil(double scale)
398        : factor(POINTS_PER_MM * 1000.0 / scale) { }
399    const int * passes() const;
400    void header(const char *, const char *, time_t,
401                double min_x, double min_y, double min_z,
402                double max_x, double max_y, double max_z);
403    void start_pass(int layer);
404    void line(const img_point *, const img_point *, bool, bool);
405    void label(const img_point *, const char *, bool, int);
406    void cross(const img_point *, bool);
407    void footer();
408};
409
410const int *
411Skencil::passes() const
412{
413    static const int skencil_passes[] = { LEGS|SURF, STNS, LABELS, 0 };
414    return skencil_passes;
415}
416
417void
418Skencil::header(const char *, const char *, time_t,
419                double min_x, double min_y, double /*min_z*/,
420                double max_x, double max_y, double /*max_z*/)
421{
422   fprintf(fh, "##Sketch 1 2\n"); /* File format version */
423   fprintf(fh, "document()\n");
424   fprintf(fh, "layout((%.3f,%.3f),0)\n",
425           (max_x - min_x) * factor, (max_y - min_y) * factor);
426}
427
428void
429Skencil::start_pass(int layer)
430{
431   fprintf(fh, "layer('%s',1,1,0,0,(0,0,0))\n", layer_name(layer));
432}
433
434void
435Skencil::line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
436{
437   (void)fSurface; /* unused */
438   if (fPendingMove) {
439       fprintf(fh, "b()\n");
440       fprintf(fh, "bs(%.3f,%.3f,%.3f)\n", p1->x * factor, p1->y * factor, 0.0);
441   }
442   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n", p->x * factor, p->y * factor, 0.0);
443}
444
445void
446Skencil::label(const img_point *p, const char *s, bool fSurface, int)
447{
448   (void)fSurface; /* unused */
449   fprintf(fh, "fp((0,0,0))\n");
450   fprintf(fh, "le()\n");
451   fprintf(fh, "Fn('Times-Roman')\n");
452   fprintf(fh, "Fs(5)\n");
453   fprintf(fh, "txt('");
454   while (*s) {
455      int ch = *s++;
456      if (ch == '\'' || ch == '\\') PUTC('\\', fh);
457      PUTC(ch, fh);
458   }
459   fprintf(fh, "',(%.3f,%.3f))\n", p->x * factor, p->y * factor);
460}
461
462void
463Skencil::cross(const img_point *p, bool fSurface)
464{
465   (void)fSurface; /* unused */
466   fprintf(fh, "b()\n");
467   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
468           p->x * factor - marker_size, p->y * factor - marker_size, 0.0);
469   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
470           p->x * factor + marker_size, p->y * factor + marker_size, 0.0);
471   fprintf(fh, "bn()\n");
472   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
473           p->x * factor + marker_size, p->y * factor - marker_size, 0.0);
474   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
475           p->x * factor - marker_size, p->y * factor + marker_size, 0.0);
476}
477
478void
479Skencil::footer(void)
480{
481   fprintf(fh, "guidelayer('Guide Lines',1,0,0,1,(0,0,1))\n");
482   if (grid) {
483      fprintf(fh, "grid((0,0,%.3f,%.3f),1,(0,0,1),'Grid')\n",
484              grid * factor, grid * factor);
485   }
486}
487
488typedef struct point {
489   img_point p;
490   const char *label;
491   struct point *next;
492} point;
493
494#define HTAB_SIZE 0x2000
495
496static point **htab;
497
498static void
499set_name(const img_point *p, const char *s)
500{
501   int hash;
502   point *pt;
503   union {
504      char data[sizeof(int) * 3];
505      int x[3];
506   } u;
507
508   u.x[0] = (int)(p->x * 100);
509   u.x[1] = (int)(p->y * 100);
510   u.x[2] = (int)(p->z * 100);
511   hash = (hash_data(u.data, sizeof(int) * 3) & (HTAB_SIZE - 1));
512   for (pt = htab[hash]; pt; pt = pt->next) {
513      if (pt->p.x == p->x && pt->p.y == p->y && pt->p.z == p->z) {
514         /* already got name for these coordinates */
515         /* FIXME: what about multiple names for the same station? */
516         return;
517      }
518   }
519
520   pt = osnew(point);
521   pt->label = osstrdup(s);
522   pt->p = *p;
523   pt->next = htab[hash];
524   htab[hash] = pt;
525
526   return;
527}
528
529static const char *
530find_name(const img_point *p)
531{
532   int hash;
533   point *pt;
534   union {
535      char data[sizeof(int) * 3];
536      int x[3];
537   } u;
538   SVX_ASSERT(p);
539
540   u.x[0] = (int)(p->x * 100);
541   u.x[1] = (int)(p->y * 100);
542   u.x[2] = (int)(p->z * 100);
543   hash = (hash_data(u.data, sizeof(int) * 3) & (HTAB_SIZE - 1));
544   for (pt = htab[hash]; pt; pt = pt->next) {
545      if (pt->p.x == p->x && pt->p.y == p->y && pt->p.z == p->z)
546         return pt->label;
547   }
548   return "?";
549}
550
551class SVG : public ExportFilter {
552    const char * to_close;
553    bool close_g;
554    double factor;
555    /* for station labels */
556    double text_height;
557    char pending[1024];
558
559  public:
560    SVG(double scale, double text_height_)
561        : to_close(NULL),
562          close_g(false),
563          factor(1000.0 / scale),
564          text_height(text_height_) {
565        pending[0] = '\0';
566    }
567    const int * passes() const;
568    void header(const char *, const char *, time_t,
569                double min_x, double min_y, double min_z,
570                double max_x, double max_y, double max_z);
571    void start_pass(int layer);
572    void line(const img_point *, const img_point *, bool, bool);
573    void label(const img_point *, const char *, bool, int);
574    void cross(const img_point *, bool);
575    void xsect(const img_point *, double, double, double);
576    void wall(const img_point *, double, double);
577    void passage(const img_point *, double, double, double);
578    void tube_end();
579    void footer();
580};
581
582const int *
583SVG::passes() const
584{
585    static const int svg_passes[] = {
586        PASG, LEGS|SURF, XSECT, WALL1, WALL2, LABELS, STNS, 0
587    };
588    return svg_passes;
589}
590
591void
592SVG::header(const char * title, const char *, time_t,
593            double min_x, double min_y, double /*min_z*/,
594            double max_x, double max_y, double /*max_z*/)
595{
596   size_t i;
597   htab = (point **)osmalloc(HTAB_SIZE * ossizeof(point *));
598   for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
599   fprintf(fh, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
600   double width = (max_x - min_x) * factor + SVG_MARGIN * 2;
601   double height = (max_y - min_y) * factor + SVG_MARGIN * 2;
602   fprintf(fh, "<svg version=\"1.1\" baseProfile=\"full\"\n"
603               "xmlns=\"http://www.w3.org/2000/svg\"\n"
604               "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
605               "xmlns:ev=\"http://www.w3.org/2001/xml-events\"\n"
606               "width=\"%.3f%s\" height=\"%.3f%s\"\n"
607               "viewBox=\"0 0 %0.3f %0.3f\">\n",
608           width, unit, height, unit, width, height);
609   if (title && title[0]) {
610       fputs("<title>", fh);
611       html_escape(fh, title);
612       fputs("</title>\n", fh);
613   }
614   fprintf(fh, "<g transform=\"translate(%.3f %.3f)\">\n",
615           SVG_MARGIN - min_x * factor, SVG_MARGIN + max_y * factor);
616   to_close = NULL;
617   close_g = false;
618}
619
620void
621SVG::start_pass(int layer)
622{
623   if (to_close) {
624      fputs(to_close, fh);
625      to_close = NULL;
626   }
627   if (close_g) {
628      fprintf(fh, "</g>\n");
629   }
630   fprintf(fh, "<g id=\"%s\"", layer_name(layer));
631   if (layer & LEGS)
632      fprintf(fh, " stroke=\"black\" fill=\"none\" stroke-width=\"0.4px\"");
633   else if (layer & STNS)
634      fprintf(fh, " stroke=\"black\" fill=\"none\" stroke-width=\"0.05px\"");
635   else if (layer & LABELS)
636      fprintf(fh, " font-size=\"%.3fem\"", text_height);
637   else if (layer & XSECT)
638      fprintf(fh, " stroke=\"grey\" fill=\"none\" stroke-width=\"0.1px\"");
639   else if (layer & WALLS)
640      fprintf(fh, " stroke=\"black\" fill=\"none\" stroke-width=\"0.1px\"");
641   else if (layer & PASG)
642      fprintf(fh, " stroke=\"none\" fill=\"peru\"");
643   fprintf(fh, ">\n");
644
645   close_g = true;
646}
647
648void
649SVG::line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
650{
651   (void)fSurface; /* unused */
652   if (fPendingMove) {
653       if (to_close) {
654           fputs(to_close, fh);
655       }
656       fprintf(fh, "<path d=\"M%.3f %.3f", p1->x * factor, p1->y * -factor);
657   }
658   fprintf(fh, "L%.3f %.3f", p->x * factor, p->y * -factor);
659   to_close = "\"/>\n";
660}
661
662void
663SVG::label(const img_point *p, const char *s, bool fSurface, int)
664{
665   (void)fSurface; /* unused */
666   fprintf(fh, "<text transform=\"translate(%.3f %.3f)\">",
667           p->x * factor, p->y * -factor);
668   html_escape(fh, s);
669   fputs("</text>\n", fh);
670   set_name(p, s);
671}
672
673void
674SVG::cross(const img_point *p, bool fSurface)
675{
676   (void)fSurface; /* unused */
677   fprintf(fh, "<circle id=\"%s\" cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\"/>\n",
678           find_name(p), p->x * factor, p->y * -factor, marker_size * SQRT_2);
679   fprintf(fh, "<path d=\"M%.3f %.3fL%.3f %.3fM%.3f %.3fL%.3f %.3f\"/>\n",
680           p->x * factor - marker_size, p->y * -factor - marker_size,
681           p->x * factor + marker_size, p->y * -factor + marker_size,
682           p->x * factor + marker_size, p->y * -factor - marker_size,
683           p->x * factor - marker_size, p->y * -factor + marker_size);
684}
685
686void
687SVG::xsect(const img_point *p, double angle, double d1, double d2)
688{
689   double s = sin(rad(angle));
690   double c = cos(rad(angle));
691   fprintf(fh, "<path d=\"M%.3f %.3fL%.3f %.3f\"/>\n",
692           (p->x + c * d1) * factor, (p->y + s * d1) * -factor,
693           (p->x - c * d2) * factor, (p->y - s * d2) * -factor);
694}
695
696void
697SVG::wall(const img_point *p, double angle, double d)
698{
699   if (!to_close) {
700       fprintf(fh, "<path d=\"M");
701       to_close = "\"/>\n";
702   } else {
703       fprintf(fh, "L");
704   }
705   double s = sin(rad(angle));
706   double c = cos(rad(angle));
707   fprintf(fh, "%.3f %.3f", (p->x + c * d) * factor, (p->y + s * d) * -factor);
708}
709
710void
711SVG::passage(const img_point *p, double angle, double d1, double d2)
712{
713   double s = sin(rad(angle));
714   double c = cos(rad(angle));
715   double x1 = (p->x + c * d1) * factor;
716   double y1 = (p->y + s * d1) * -factor;
717   double x2 = (p->x - c * d2) * factor;
718   double y2 = (p->y - s * d2) * -factor;
719   if (*pending) {
720       fputs(pending, fh);
721       fprintf(fh, "L%.3f %.3fL%.3f %.3fZ\"/>\n", x2, y2, x1, y1);
722   }
723   sprintf(pending, "<path d=\"M%.3f %.3fL%.3f %.3f", x1, y1, x2, y2);
724}
725
726void
727SVG::tube_end()
728{
729   *pending = '\0';
730   if (to_close) {
731      fputs(to_close, fh);
732      to_close = NULL;
733   }
734}
735
736void
737SVG::footer()
738{
739   if (to_close) {
740      fputs(to_close, fh);
741      to_close = NULL;
742   }
743   if (close_g) {
744      fprintf(fh, "</g>\n");
745      close_g = false;
746   }
747   fprintf(fh, "</g>\n</svg>\n");
748}
749
750class PLT : public ExportFilter {
751    string escaped;
752
753    const char * find_name_plt(const img_point *p);
754
755    double min_N, max_N, min_E, max_E, min_A, max_A;
756
757  public:
758    PLT() { }
759    const int * passes() const;
760    void header(const char *, const char *, time_t,
761                double min_x, double min_y, double min_z,
762                double max_x, double max_y, double max_z);
763    void line(const img_point *, const img_point *, bool, bool);
764    void label(const img_point *, const char *, bool, int);
765    void footer();
766};
767
768const int *
769PLT::passes() const
770{
771    static const int plt_passes[] = { LABELS, LEGS|SURF, 0 };
772    return plt_passes;
773}
774
775void
776PLT::header(const char *title, const char *, time_t,
777            double min_x, double min_y, double min_z,
778            double max_x, double max_y, double max_z)
779{
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#if 0 // FIXME: allow this to be set from aven somehow!
1173       case 's':
1174         survey = optarg;
1175         break;
1176#endif
1177
1178   ExportFilter * filt;
1179   switch (format) {
1180       case FMT_DXF:
1181           filt = new DXF(text_height);
1182           break;
1183       case FMT_EPS:
1184           filt = new EPS(scale);
1185           break;
1186       case FMT_GPX:
1187           filt = new GPX(input_projection);
1188           show_mask |= FULL_COORDS;
1189           break;
1190       case FMT_HPGL:
1191           filt = new HPGL;
1192           // factor = POINTS_PER_MM * 1000.0 / scale;
1193           break;
1194       case FMT_KML:
1195           filt = new KML(input_projection);
1196           show_mask |= FULL_COORDS;
1197           break;
1198       case FMT_PLT:
1199           filt = new PLT;
1200           show_mask |= FULL_COORDS;
1201           break;
1202       case FMT_SK:
1203           filt = new Skencil(scale);
1204           break;
1205       case FMT_SVG:
1206           filt = new SVG(scale, text_height);
1207           break;
1208       default:
1209           return false;
1210   }
1211
1212   // FIXME: This should really use fn_str() - currently we probably can't
1213   // save to a Unicode path on wxmsw.
1214   if (!filt->fopen(fnm_out.mb_str())) {
1215       delete filt;
1216       return false;
1217   }
1218
1219   if (elevation) {
1220      s = sin(rad(pan));
1221      c = cos(rad(pan));
1222   }
1223
1224   /* Get bounding box */
1225   double min_x, min_y, min_z, max_x, max_y, max_z;
1226   min_x = min_y = min_z = HUGE_VAL;
1227   max_x = max_y = max_z = -HUGE_VAL;
1228   list<traverse>::const_iterator trav = mainfrm->traverses_begin();
1229   list<traverse>::const_iterator tend = mainfrm->traverses_end();
1230   for ( ; trav != tend; ++trav) {
1231        vector<PointInfo>::const_iterator pos = trav->begin();
1232        vector<PointInfo>::const_iterator end = trav->end();
1233        for ( ; pos != end; ++pos) {
1234            p.x = pos->GetX();
1235            p.y = pos->GetY();
1236            p.z = pos->GetZ();
1237
1238            if (elevation) {
1239                double xnew = p.x * c - p.y * s;
1240                double znew = - p.x * s - p.y * c;
1241                p.y = p.z;
1242                p.z = znew;
1243                p.x = xnew;
1244            }
1245
1246            if (p.x < min_x) min_x = p.x;
1247            if (p.x > max_x) max_x = p.x;
1248            if (p.y < min_y) min_y = p.y;
1249            if (p.y > max_y) max_y = p.y;
1250            if (p.z < min_z) min_z = p.z;
1251            if (p.z > max_z) max_z = p.z;
1252        }
1253   }
1254   {
1255        list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1256        list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1257        for ( ; pos != end; ++pos) {
1258            p.x = (*pos)->GetX();
1259            p.y = (*pos)->GetY();
1260            p.z = (*pos)->GetZ();
1261
1262            if (elevation) {
1263                double xnew = p.x * c - p.y * s;
1264                double znew = - p.x * s - p.y * c;
1265                p.y = p.z;
1266                p.z = znew;
1267                p.x = xnew;
1268            }
1269
1270            if (p.x < min_x) min_x = p.x;
1271            if (p.x > max_x) max_x = p.x;
1272            if (p.y < min_y) min_y = p.y;
1273            if (p.y > max_y) max_y = p.y;
1274            if (p.z < min_z) min_z = p.z;
1275            if (p.z > max_z) max_z = p.z;
1276        }
1277   }
1278
1279   if (grid > 0) {
1280      min_x -= grid / 2;
1281      max_x += grid / 2;
1282      min_y -= grid / 2;
1283      max_y += grid / 2;
1284   }
1285
1286   /* handle empty file gracefully */
1287   if (min_x > max_x) {
1288      min_x = min_y = min_z = 0;
1289      max_x = max_y = max_z = 0;
1290   }
1291
1292   double x_offset, y_offset, z_offset;
1293   if (show_mask & FULL_COORDS) {
1294       // Full coordinates.
1295       x_offset = mainfrm->GetOffset().GetX();
1296       y_offset = mainfrm->GetOffset().GetY();
1297       z_offset = mainfrm->GetOffset().GetZ();
1298   } else if (show_mask & CENTRED) {
1299       // Centred.
1300       x_offset = (min_x + max_x) * -0.5;
1301       y_offset = (min_y + max_y) * -0.5;
1302       z_offset = (min_z + max_z) * -0.5;
1303   } else {
1304       // Origin at lowest SW corner.
1305       x_offset = -min_x;
1306       y_offset = -min_y;
1307       z_offset = -min_z;
1308   }
1309   min_x += x_offset;
1310   max_x += x_offset;
1311   min_y += y_offset;
1312   max_y += y_offset;
1313   min_z += z_offset;
1314   max_z += z_offset;
1315
1316   /* Header */
1317   filt->header(title.mb_str(), datestamp.mb_str(), datestamp_numeric,
1318                min_x, min_y, min_z, max_x, max_y, max_z);
1319
1320   p1.x = p1.y = p1.z = 0; /* avoid compiler warning */
1321
1322   for (pass = filt->passes(); *pass; ++pass) {
1323      int pass_mask = show_mask & *pass;
1324      if (!pass_mask)
1325          continue;
1326      filt->start_pass(*pass);
1327      if (pass_mask & LEGS) {
1328          trav = mainfrm->traverses_begin();
1329          tend = mainfrm->traverses_end();
1330          for ( ; trav != tend; ++trav) {
1331             assert(trav->size() > 1);
1332             vector<PointInfo>::const_iterator pos = trav->begin();
1333             vector<PointInfo>::const_iterator end = trav->end();
1334             for ( ; pos != end; ++pos) {
1335                 p.x = pos->GetX() + x_offset;
1336                 p.y = pos->GetY() + y_offset;
1337                 p.z = pos->GetZ() + z_offset;
1338                 if (elevation) {
1339                     double xnew = p.x * c - p.y * s;
1340                     double znew = - p.x * s - p.y * c;
1341                     p.y = p.z;
1342                     p.z = znew;
1343                     p.x = xnew;
1344                 }
1345
1346                 if (pos == trav->begin()) {
1347                     // First point is move...
1348#ifdef DEBUG_CAD3D
1349                     printf("move to %9.2f %9.2f %9.2f\n",x,y,z);
1350#endif
1351                     fPendingMove = 1;
1352                 } else {
1353#ifdef DEBUG_CAD3D
1354                     printf("line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
1355#endif
1356                     filt->line(&p1, &p, false, fPendingMove);
1357                     fPendingMove = 0;
1358                 }
1359                 p1 = p;
1360             }
1361         }
1362      }
1363      if (pass_mask & SURF) {
1364          trav = mainfrm->surface_traverses_begin();
1365          tend = mainfrm->surface_traverses_end();
1366          for ( ; trav != tend; ++trav) {
1367             assert(trav->size() > 1);
1368             vector<PointInfo>::const_iterator pos = trav->begin();
1369             vector<PointInfo>::const_iterator end = trav->end();
1370             for ( ; pos != end; ++pos) {
1371                 p.x = pos->GetX() + x_offset;
1372                 p.y = pos->GetY() + y_offset;
1373                 p.z = pos->GetZ() + z_offset;
1374
1375                 if (elevation) {
1376                     double xnew = p.x * c - p.y * s;
1377                     double znew = - p.x * s - p.y * c;
1378                     p.y = p.z;
1379                     p.z = znew;
1380                     p.x = xnew;
1381                 }
1382
1383                 if (pos == trav->begin()) {
1384                     // First point is move...
1385#ifdef DEBUG_CAD3D
1386                     printf("surface move to %9.2f %9.2f %9.2f\n",x,y,z);
1387#endif
1388                     fPendingMove = 1;
1389                 } else {
1390#ifdef DEBUG_CAD3D
1391                     printf("surface line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
1392#endif
1393                     filt->line(&p1, &p, true, fPendingMove);
1394                     fPendingMove = 0;
1395                 }
1396                 p1 = p;
1397             }
1398         }
1399      }
1400      if (pass_mask & (STNS|LABELS|ENTS|FIXES|EXPORTS)) {
1401          list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1402          list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1403          for ( ; pos != end; ++pos) {
1404              p.x = (*pos)->GetX() + x_offset;
1405              p.y = (*pos)->GetY() + y_offset;
1406              p.z = (*pos)->GetZ() + z_offset;
1407
1408              if (elevation) {
1409                  double xnew = p.x * c - p.y * s;
1410                  double znew = - p.x * s - p.y * c;
1411                  p.y = p.z;
1412                  p.z = znew;
1413                  p.x = xnew;
1414              }
1415#ifdef DEBUG_CAD3D
1416              printf("label '%s' at %9.2f %9.2f %9.2f\n",(*pos)->GetText(),x,y,z);
1417#endif
1418              int type = 0;
1419              if ((pass_mask & ENTS) && (*pos)->IsEntrance()) {
1420                  type = ENTS;
1421              } else if ((pass_mask & FIXES) && (*pos)->IsFixedPt()) {
1422                  type = FIXES;
1423              } else if ((pass_mask & EXPORTS) && (*pos)->IsExportedPt())  {
1424                  type = EXPORTS;
1425              } else if (pass_mask & LABELS) {
1426                  type = LABELS;
1427              }
1428              /* Use !UNDERGROUND as the criterion - we want stations where a
1429               * surface and underground survey meet to be in the underground
1430               * layer */
1431              bool f_surface = !(*pos)->IsUnderground();
1432              if (type) {
1433                  const wxString & text = (*pos)->GetText();
1434                  filt->label(&p, text.mb_str(), f_surface, type);
1435              }
1436              if (pass_mask & STNS)
1437                  filt->cross(&p, f_surface);
1438          }
1439      }
1440      if (pass_mask & (XSECT|WALLS|PASG)) {
1441          list<vector<XSect> >::const_iterator tube = mainfrm->tubes_begin();
1442          list<vector<XSect> >::const_iterator tube_end = mainfrm->tubes_end();
1443          for ( ; tube != tube_end; ++tube) {
1444              vector<XSect>::const_iterator pos = tube->begin();
1445              vector<XSect>::const_iterator end = tube->end();
1446              for ( ; pos != end; ++pos) {
1447                  const XSect & xs = *pos;
1448                  p.x = xs.GetX() + x_offset;
1449                  p.y = xs.GetY() + y_offset;
1450                  p.z = xs.GetZ() + z_offset;
1451
1452                  if (elevation) {
1453                      double xnew = p.x * c - p.y * s;
1454                      double znew = - p.x * s - p.y * c;
1455                      p.y = p.z;
1456                      p.z = znew;
1457                      p.x = xnew;
1458                      if (pass_mask & XSECT)
1459                          filt->xsect(&p, 90, xs.GetU(), xs.GetD());
1460                      if (pass_mask & WALL1)
1461                          filt->wall(&p, 90, xs.GetU());
1462                      if (pass_mask & WALL2)
1463                          filt->wall(&p, 270, xs.GetD());
1464                      if (pass_mask & PASG)
1465                          filt->passage(&p, 90, xs.GetU(), xs.GetD());
1466                  } else {
1467                      if (pass_mask & XSECT)
1468                          filt->xsect(&p, xs.get_right_bearing() + 180, xs.GetL(), xs.GetR());
1469                      if (pass_mask & WALL1)
1470                          filt->wall(&p, xs.get_right_bearing() + 180, xs.GetL());
1471                      if (pass_mask & WALL2)
1472                          filt->wall(&p, xs.get_right_bearing(), xs.GetR());
1473                      if (pass_mask & PASG)
1474                          filt->passage(&p, xs.get_right_bearing() + 180, xs.GetL(), xs.GetR());
1475                  }
1476              }
1477              filt->tube_end();
1478          }
1479      }
1480   }
1481   filt->footer();
1482   delete filt;
1483   osfree(htab);
1484   htab = NULL;
1485   return true;
1486}
Note: See TracBrowser for help on using the repository browser.