source: git/src/export.cc @ b4d8642

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

src/export.cc: If the survey has a title, add it as a top-level
<title> element to exported SVG files.

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