source: git/src/export.cc @ 0ca8fe0

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

src/: Hook up GPX export options to export entrances, fixed points,
and/or exported points, and specify coloured pin symbols for these
types of stations. These options now default to what's being shown
in the survey view, like other export options. GPX export
"creator" how includes the Survex version and website URL, and the
survey title is includes as a <name> tag inside the <gpx> tag.

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