source: git/src/export.cc @ 18ff765

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

src/export.cc,src/export.h,src/printwx.cc: For export formats where
scaling is supporting, aven now actually uses the scale specified in
the export dialog (previously it ignored this and used 1:500).

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