source: git/src/export.cc @ 7797ada

RELEASE/1.0
Last change on this file since 7797ada was 7797ada, checked in by Olly Betts <olly@…>, 13 years ago

Backport change from 1.2.0:
src/export.cc: Explicit #include <time.h> for strftime().

git-svn-id: file:///home/survex-svn/survex/branches/1.0@3708 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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