source: git/src/export.cc @ 1b0bf6a

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

src/export.cc,src/exportfilter.h: Pass bounding box as parameters to
header() method rather than storing them as static data.

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