source: git/src/export.cc @ 4d7d209

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

NEWS,doc/cad3d.sgml,lib/,src/: The "Sketch" vector drawing program
got renamed to "Skencil" some time ago, so update all references in
code and documentation. cad3d has a new --skencil option to specify
this output format, but --sketch is still recognised for
compatibility.

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