source: git/src/export.cc @ 98a3786

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since 98a3786 was 98a3786, checked in by Olly Betts <olly@…>, 14 years ago

src/export.cc,src/gfxcore.cc: wxString::fn_str() on wxmsw returns
a wide string, which doesn't work with fopen(), etc, so call
char_str() instead for now.

git-svn-id: file:///home/survex-svn/survex/branches/survex-1_1@3394 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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