source: git/src/export.cc @ 3c35956

RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereostereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 3c35956 was 4734106, checked in by Olly Betts <olly@…>, 12 years ago

lib/,src/: Rework findentrances into GPX export for aven.

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