source: git/src/export.cc @ 70462c8

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

src/: Pass the datestamp from the 3d file to the export code.

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