source: git/src/export.cc @ 034141d

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

src/export.cc: Implement export of passage info for DXF (untested).

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