source: git/src/export.cc @ 9185847

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

src/: Use cast to void rather than assignment to self to suppress
unused parameter warnings, as clang warns about the latter.

  • Property mode set to 100644
File size: 40.2 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 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 "mainfrm.h"
37
38#include <float.h>
39#include <math.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <time.h>
44
45#if defined(HAVE_GETPWUID) && !defined(__DJGPP__)
46# include <pwd.h>
47# include <sys/types.h>
48# include <unistd.h>
49#endif
50
51#include "cmdline.h"
52#include "debug.h"
53#include "filename.h"
54#include "hash.h"
55#include "img_hosted.h"
56#include "message.h"
57#include "useful.h"
58
59#define POINTS_PER_INCH 72.0
60#define POINTS_PER_MM (POINTS_PER_INCH / MM_PER_INCH)
61
62#define SQRT_2          1.41421356237309504880168872420969
63
64static void
65html_escape(FILE *fh, const char *s)
66{
67    while (*s) {
68        switch (*s) {
69            case '<':
70                fputs("&lt;", fh);
71                break;
72            case '>':
73                fputs("&gt;", fh);
74                break;
75            case '&':
76                fputs("&amp;", fh);
77                break;
78            default:
79                PUTC(*s, fh);
80        }
81        ++s;
82    }
83}
84
85static const char *layer_name(int mask) {
86    switch (mask) {
87        case LEGS: case LEGS|SURF:
88            return "Legs";
89        case SURF:
90            return "Surface";
91        case STNS:
92            return "Stations";
93        case LABELS:
94            return "Labels";
95        case XSECT:
96            return "Cross-sections";
97        case WALL1: case WALL2: case WALLS:
98            return "Walls";
99        case PASG:
100            return "Passages";
101    }
102    return "";
103}
104
105/* bounds */
106static double min_x, min_y, min_z, max_x, max_y, max_z;
107
108static double text_height; /* for station labels */
109static double marker_size; /* for station markers */
110static double grid; /* grid spacing (or 0 for no grid) */
111static double scale = 500.0;
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{
1128   int fPendingMove = 0;
1129   img_point p, p1;
1130   double s = 0, c = 0;
1131   const int *pass;
1132   bool elevation = (tilt == 0.0);
1133
1134   grid = grid_;
1135   text_height = text_height_;
1136   marker_size = marker_size_;
1137
1138#if 0 // FIXME: allow these to be set from aven somehow!
1139       case 'r': /* Reduction factor */
1140         scale = cmdline_double_arg();
1141         break;
1142       case 's':
1143         survey = optarg;
1144         break;
1145#endif
1146
1147   ExportFilter * filt;
1148   switch (format) {
1149       case FMT_DXF:
1150           filt = new DXF;
1151           break;
1152       case FMT_EPS:
1153           filt = new EPS;
1154           factor = POINTS_PER_MM * 1000.0 / scale;
1155           break;
1156       case FMT_GPX:
1157           filt = new GPX(input_projection);
1158           show_mask |= FULL_COORDS;
1159           break;
1160       case FMT_HPGL:
1161           filt = new HPGL;
1162           // factor = POINTS_PER_MM * 1000.0 / scale;
1163           break;
1164       case FMT_PLT:
1165           filt = new PLT;
1166           show_mask |= FULL_COORDS;
1167           break;
1168       case FMT_SK:
1169           filt = new Skencil;
1170           factor = POINTS_PER_MM * 1000.0 / scale;
1171           break;
1172       case FMT_SVG:
1173           filt = new SVG;
1174           factor = 1000.0 / scale;
1175           break;
1176       default:
1177           return false;
1178   }
1179
1180   // FIXME: This should really use fn_str() - currently we probably can't
1181   // save to a Unicode path on wxmsw.
1182   if (!filt->fopen(fnm_out.mb_str())) {
1183       delete filt;
1184       return false;
1185   }
1186
1187   if (elevation) {
1188      s = sin(rad(pan));
1189      c = cos(rad(pan));
1190   }
1191
1192   /* Get drawing corners */
1193   min_x = min_y = min_z = HUGE_VAL;
1194   max_x = max_y = max_z = -HUGE_VAL;
1195   list<traverse>::const_iterator trav = mainfrm->traverses_begin();
1196   list<traverse>::const_iterator tend = mainfrm->traverses_end();
1197   for ( ; trav != tend; ++trav) {
1198        vector<PointInfo>::const_iterator pos = trav->begin();
1199        vector<PointInfo>::const_iterator end = trav->end();
1200        for ( ; pos != end; ++pos) {
1201            p.x = pos->GetX();
1202            p.y = pos->GetY();
1203            p.z = pos->GetZ();
1204
1205            if (elevation) {
1206                double xnew = p.x * c - p.y * s;
1207                double znew = - p.x * s - p.y * c;
1208                p.y = p.z;
1209                p.z = znew;
1210                p.x = xnew;
1211            }
1212
1213            if (p.x < min_x) min_x = p.x;
1214            if (p.x > max_x) max_x = p.x;
1215            if (p.y < min_y) min_y = p.y;
1216            if (p.y > max_y) max_y = p.y;
1217            if (p.z < min_z) min_z = p.z;
1218            if (p.z > max_z) max_z = p.z;
1219        }
1220   }
1221   {
1222        list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1223        list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1224        for ( ; pos != end; ++pos) {
1225            p.x = (*pos)->GetX();
1226            p.y = (*pos)->GetY();
1227            p.z = (*pos)->GetZ();
1228
1229            if (elevation) {
1230                double xnew = p.x * c - p.y * s;
1231                double znew = - p.x * s - p.y * c;
1232                p.y = p.z;
1233                p.z = znew;
1234                p.x = xnew;
1235            }
1236
1237            if (p.x < min_x) min_x = p.x;
1238            if (p.x > max_x) max_x = p.x;
1239            if (p.y < min_y) min_y = p.y;
1240            if (p.y > max_y) max_y = p.y;
1241            if (p.z < min_z) min_z = p.z;
1242            if (p.z > max_z) max_z = p.z;
1243        }
1244   }
1245
1246   if (grid > 0) {
1247      min_x -= grid / 2;
1248      max_x += grid / 2;
1249      min_y -= grid / 2;
1250      max_y += grid / 2;
1251   }
1252
1253   /* handle empty file gracefully */
1254   if (min_x > max_x) {
1255      min_x = min_y = min_z = 0;
1256      max_x = max_y = max_z = 0;
1257   }
1258
1259   double x_offset, y_offset, z_offset;
1260   if (show_mask & FULL_COORDS) {
1261       // Full coordinates.
1262       x_offset = mainfrm->GetOffset().GetX();
1263       y_offset = mainfrm->GetOffset().GetY();
1264       z_offset = mainfrm->GetOffset().GetZ();
1265   } else if (show_mask & CENTRED) {
1266       // Centred.
1267       x_offset = (min_x + max_x) * -0.5;
1268       y_offset = (min_y + max_y) * -0.5;
1269       z_offset = (min_z + max_z) * -0.5;
1270   } else {
1271       // Origin at lowest SW corner.
1272       x_offset = -min_x;
1273       y_offset = -min_y;
1274       z_offset = -min_z;
1275   }
1276   min_x += x_offset;
1277   max_x += x_offset;
1278   min_y += y_offset;
1279   max_y += y_offset;
1280   min_z += z_offset;
1281   max_z += z_offset;
1282
1283   /* Header */
1284   filt->header(title.mb_str(), datestamp.mb_str(), datestamp_numeric);
1285
1286   p1.x = p1.y = p1.z = 0; /* avoid compiler warning */
1287
1288   for (pass = filt->passes(); *pass; ++pass) {
1289      int pass_mask = show_mask & *pass;
1290      if (!pass_mask)
1291          continue;
1292      filt->start_pass(*pass);
1293      if (pass_mask & LEGS) {
1294          trav = mainfrm->traverses_begin();
1295          tend = mainfrm->traverses_end();
1296          for ( ; trav != tend; ++trav) {
1297             assert(trav->size() > 1);
1298             vector<PointInfo>::const_iterator pos = trav->begin();
1299             vector<PointInfo>::const_iterator end = trav->end();
1300             for ( ; pos != end; ++pos) {
1301                 p.x = pos->GetX() + x_offset;
1302                 p.y = pos->GetY() + y_offset;
1303                 p.z = pos->GetZ() + z_offset;
1304                 if (elevation) {
1305                     double xnew = p.x * c - p.y * s;
1306                     double znew = - p.x * s - p.y * c;
1307                     p.y = p.z;
1308                     p.z = znew;
1309                     p.x = xnew;
1310                 }
1311
1312                 if (pos == trav->begin()) {
1313                     // First point is move...
1314#ifdef DEBUG_CAD3D
1315                     printf("move to %9.2f %9.2f %9.2f\n",x,y,z);
1316#endif
1317                     fPendingMove = 1;
1318                 } else {
1319#ifdef DEBUG_CAD3D
1320                     printf("line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
1321#endif
1322                     filt->line(&p1, &p, false, fPendingMove);
1323                     fPendingMove = 0;
1324                 }
1325                 p1 = p;
1326             }
1327         }
1328      }
1329      if (pass_mask & SURF) {
1330          trav = mainfrm->surface_traverses_begin();
1331          tend = mainfrm->surface_traverses_end();
1332          for ( ; trav != tend; ++trav) {
1333             assert(trav->size() > 1);
1334             vector<PointInfo>::const_iterator pos = trav->begin();
1335             vector<PointInfo>::const_iterator end = trav->end();
1336             for ( ; pos != end; ++pos) {
1337                 p.x = pos->GetX() + x_offset;
1338                 p.y = pos->GetY() + y_offset;
1339                 p.z = pos->GetZ() + z_offset;
1340
1341                 if (elevation) {
1342                     double xnew = p.x * c - p.y * s;
1343                     double znew = - p.x * s - p.y * c;
1344                     p.y = p.z;
1345                     p.z = znew;
1346                     p.x = xnew;
1347                 }
1348
1349                 if (pos == trav->begin()) {
1350                     // First point is move...
1351#ifdef DEBUG_CAD3D
1352                     printf("surface move to %9.2f %9.2f %9.2f\n",x,y,z);
1353#endif
1354                     fPendingMove = 1;
1355                 } else {
1356#ifdef DEBUG_CAD3D
1357                     printf("surface line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
1358#endif
1359                     filt->line(&p1, &p, true, fPendingMove);
1360                     fPendingMove = 0;
1361                 }
1362                 p1 = p;
1363             }
1364         }
1365      }
1366      if (pass_mask & (STNS|LABELS|ENTS|FIXES|EXPORTS)) {
1367          list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1368          list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1369          for ( ; pos != end; ++pos) {
1370              p.x = (*pos)->GetX() + x_offset;
1371              p.y = (*pos)->GetY() + y_offset;
1372              p.z = (*pos)->GetZ() + z_offset;
1373
1374              if (elevation) {
1375                  double xnew = p.x * c - p.y * s;
1376                  double znew = - p.x * s - p.y * c;
1377                  p.y = p.z;
1378                  p.z = znew;
1379                  p.x = xnew;
1380              }
1381#ifdef DEBUG_CAD3D
1382              printf("label '%s' at %9.2f %9.2f %9.2f\n",(*pos)->GetText(),x,y,z);
1383#endif
1384              int type = 0;
1385              if ((pass_mask & ENTS) && (*pos)->IsEntrance()) {
1386                  type = ENTS;
1387              } else if ((pass_mask & FIXES) && (*pos)->IsFixedPt()) {
1388                  type = FIXES;
1389              } else if ((pass_mask & EXPORTS) && (*pos)->IsExportedPt())  {
1390                  type = EXPORTS;
1391              } else if (pass_mask & LABELS) {
1392                  type = LABELS;
1393              }
1394              if (type) {
1395                  const char * text = (*pos)->GetText().mb_str();
1396                  /* Use !UNDERGROUND as the criterion - we want stations where
1397                   * a surface and underground survey meet to be in the
1398                   * underground layer */
1399                  filt->label(&p, text, !(*pos)->IsUnderground(), type);
1400              }
1401              if (pass_mask & STNS)
1402                  filt->cross(&p, !(*pos)->IsUnderground());
1403          }
1404      }
1405      if (pass_mask & (XSECT|WALLS|PASG)) {
1406          list<vector<XSect> >::const_iterator tube = mainfrm->tubes_begin();
1407          list<vector<XSect> >::const_iterator tube_end = mainfrm->tubes_end();
1408          for ( ; tube != tube_end; ++tube) {
1409              vector<XSect>::const_iterator pos = tube->begin();
1410              vector<XSect>::const_iterator end = tube->end();
1411              for ( ; pos != end; ++pos) {
1412                  const XSect & xs = *pos;
1413                  p.x = xs.GetX() + x_offset;
1414                  p.y = xs.GetY() + y_offset;
1415                  p.z = xs.GetZ() + z_offset;
1416
1417                  if (elevation) {
1418                      double xnew = p.x * c - p.y * s;
1419                      double znew = - p.x * s - p.y * c;
1420                      p.y = p.z;
1421                      p.z = znew;
1422                      p.x = xnew;
1423                      if (pass_mask & XSECT)
1424                          filt->xsect(&p, 0, xs.GetU(), xs.GetD());
1425                      if (pass_mask & WALL1)
1426                          filt->wall(&p, 0, xs.GetU());
1427                      if (pass_mask & WALL2)
1428                          filt->wall(&p, 180, xs.GetD());
1429                      if (pass_mask & PASG)
1430                          filt->passage(&p, 0, xs.GetU(), xs.GetD());
1431                  } else {
1432                      if (pass_mask & XSECT)
1433                          filt->xsect(&p, xs.get_right_bearing() + 180, xs.GetL(), xs.GetR());
1434                      if (pass_mask & WALL1)
1435                          filt->wall(&p, xs.get_right_bearing() + 180, xs.GetL());
1436                      if (pass_mask & WALL2)
1437                          filt->wall(&p, xs.get_right_bearing(), xs.GetR());
1438                      if (pass_mask & PASG)
1439                          filt->passage(&p, xs.get_right_bearing() + 180, xs.GetL(), xs.GetR());
1440                  }
1441              }
1442              filt->tube_end();
1443          }
1444      }
1445   }
1446   filt->footer();
1447   delete filt;
1448   osfree(htab);
1449   htab = NULL;
1450   return true;
1451}
Note: See TracBrowser for help on using the repository browser.