source: git/trunk/src/export.cc @ 7bb8184

Last change on this file since 7bb8184 was 7bb8184, checked in by Olly Betts <olly@…>, 13 years ago

Retagging 1.2.0

git-svn-id: file:///home/survex-svn/survex/tags/1.2.0@3664 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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