2 * Export to CAD-like formats (DXF, Skencil, SVG, EPS) and also Compass PLT.
5 /* Copyright (C) 1994-2004,2005,2006,2008,2010,2011,2012,2013,2014,2015,2016,2018 Olly Betts
6 * Copyright (C) 2004 John Pybus (SVG Output code)
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.
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.
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
31 #include "exportfilter.h"
47 #if defined(HAVE_GETPWUID) && !defined(__DJGPP__)
49 # include <sys/types.h>
60 #include "img_hosted.h"
64 #define POINTS_PER_INCH 72.0
65 #define POINTS_PER_MM (POINTS_PER_INCH / MM_PER_INCH)
67 #define SQRT_2 1.41421356237309504880168872420969
69 // Order here needs to match order of export_format enum in export.h.
71 const format_info export_format_info
[] = {
72 { ".csv", /*CSV files*/101,
73 LABELS
|ENTS
|FIXES
|EXPORTS
|EXPORT_3D
,
75 { ".dxf", /*DXF files*/411,
76 LABELS
|LEGS
|SURF
|SPLAYS
|STNS
|PASG
|XSECT
|WALLS
|MARKER_SIZE
|TEXT_HEIGHT
|GRID
|FULL_COORDS
,
78 { ".eps", /*EPS files*/412,
79 LABELS
|LEGS
|SURF
|SPLAYS
|STNS
|PASG
|XSECT
|WALLS
,
81 { ".gpx", /*GPX files*/413,
82 LABELS
|LEGS
|SURF
|SPLAYS
|ENTS
|FIXES
|EXPORTS
|PROJ
|EXPORT_3D
,
84 /* TRANSLATORS: Here "plotter" refers to a machine which draws a printout
85 * on a (usually large) sheet of paper using a pen mounted in a motorised
87 { ".hpgl", /*HPGL for plotters*/414,
88 LABELS
|LEGS
|SURF
|SPLAYS
|STNS
|CENTRED
,
90 { ".json", /*JSON files*/445,
91 LEGS
|SPLAYS
|CENTRED
|EXPORT_3D
,
93 { ".kml", /*KML files*/444,
94 LABELS
|LEGS
|SPLAYS
|PASG
|XSECT
|WALLS
|ENTS
|FIXES
|EXPORTS
|PROJ
|EXPORT_3D
|CLAMP_TO_GROUND
,
96 /* TRANSLATORS: "Compass" and "Carto" are the names of software packages,
97 * so should not be translated:
98 * http://www.fountainware.com/compass/
99 * http://www.psc-cavers.org/carto/ */
100 { ".plt", /*Compass PLT for use with Carto*/415,
101 LABELS
|LEGS
|SURF
|SPLAYS
,
103 /* TRANSLATORS: "Skencil" is the name of a software package, so should not be
104 * translated: http://www.skencil.org/ */
105 { ".sk", /*Skencil files*/416,
106 LABELS
|LEGS
|SURF
|SPLAYS
|STNS
|MARKER_SIZE
|GRID
|SCALE
,
108 /* TRANSLATORS: Survex is the name of the software, and "pos" refers to a
109 * file extension, so neither should be translated. */
110 { ".pos", /*Survex pos files*/166,
111 LABELS
|ENTS
|FIXES
|EXPORTS
|EXPORT_3D
,
113 { ".svg", /*SVG files*/417,
114 LABELS
|LEGS
|SURF
|SPLAYS
|STNS
|PASG
|XSECT
|WALLS
|MARKER_SIZE
|TEXT_HEIGHT
|SCALE
,
118 static_assert(sizeof(export_format_info
) == FMT_MAX_PLUS_ONE_
* sizeof(export_format_info
[0]),
119 "export_format_info[] matches enum export_format");
122 html_escape(FILE *fh
, const char *s
)
142 // Used by Skencil and SVG.
143 static const char *layer_name(int mask
) {
145 case LEGS
: case LEGS
|SURF
:
154 return "Cross-sections";
155 case WALL1
: case WALL2
: case WALLS
:
163 static double marker_size
; /* for station markers */
164 static double grid
; /* grid spacing (or 0 for no grid) */
167 ExportFilter::passes() const
169 static const int default_passes
[] = { LEGS
|SURF
|STNS
|LABELS
, 0 };
170 return default_passes
;
173 class DXF
: public ExportFilter
{
174 const char * to_close
;
175 /* for station labels */
180 explicit DXF(double text_height_
)
181 : to_close(0), text_height(text_height_
) { pending
[0] = '\0'; }
182 const int * passes() const;
183 bool fopen(const wxString
& fnm_out
);
184 void header(const char *, const char *, time_t,
185 double min_x
, double min_y
, double min_z
,
186 double max_x
, double max_y
, double max_z
);
187 void line(const img_point
*, const img_point
*, unsigned, bool);
188 void label(const img_point
*, const char *, bool, int);
189 void cross(const img_point
*, bool);
190 void xsect(const img_point
*, double, double, double);
191 void wall(const img_point
*, double, double);
192 void passage(const img_point
*, double, double, double);
200 static const int dxf_passes
[] = {
201 PASG
, XSECT
, WALL1
, WALL2
, LEGS
|SURF
|STNS
|LABELS
, 0
207 DXF::fopen(const wxString
& fnm_out
)
209 // DXF gets written as text rather than binary.
210 fh
= wxFopen(fnm_out
.fn_str(), wxT("w"));
215 DXF::header(const char *, const char *, time_t,
216 double min_x
, double min_y
, double min_z
,
217 double max_x
, double max_y
, double max_z
)
219 fprintf(fh
, "0\nSECTION\n"
221 fprintf(fh
, "9\n$EXTMIN\n"); /* lower left corner of drawing */
222 fprintf(fh
, "10\n%#-.2f\n", min_x
); /* x */
223 fprintf(fh
, "20\n%#-.2f\n", min_y
); /* y */
224 fprintf(fh
, "30\n%#-.2f\n", min_z
); /* min z */
225 fprintf(fh
, "9\n$EXTMAX\n"); /* upper right corner of drawing */
226 fprintf(fh
, "10\n%#-.2f\n", max_x
); /* x */
227 fprintf(fh
, "20\n%#-.2f\n", max_y
); /* y */
228 fprintf(fh
, "30\n%#-.2f\n", max_z
); /* max z */
229 fprintf(fh
, "9\n$PDMODE\n70\n3\n"); /* marker style as CROSS */
230 fprintf(fh
, "9\n$PDSIZE\n40\n%6.2f\n", marker_size
); /* marker size */
231 fprintf(fh
, "0\nENDSEC\n");
233 fprintf(fh
, "0\nSECTION\n"
235 fprintf(fh
, "0\nTABLE\n" /* Define CONTINUOUS and DASHED line types. */
255 fprintf(fh
, "0\nTABLE\n"
257 fprintf(fh
, "70\n10\n"); /* max # off layers in this DXF file : 10 */
258 /* First Layer: CentreLine */
259 fprintf(fh
, "0\nLAYER\n2\nCentreLine\n");
260 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
261 fprintf(fh
, "62\n5\n"); /* color: kept the same used by SpeleoGen */
262 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
263 /* Next Layer: Stations */
264 fprintf(fh
, "0\nLAYER\n2\nStations\n");
265 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
266 fprintf(fh
, "62\n7\n"); /* color: kept the same used by SpeleoGen */
267 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
268 /* Next Layer: Labels */
269 fprintf(fh
, "0\nLAYER\n2\nLabels\n");
270 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
271 fprintf(fh
, "62\n7\n"); /* color: kept the same used by SpeleoGen */
272 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
273 /* Next Layer: Surface */
274 fprintf(fh
, "0\nLAYER\n2\nSurface\n");
275 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
276 fprintf(fh
, "62\n5\n"); /* color */
277 fprintf(fh
, "6\nDASHED\n"); /* linetype */
278 /* Next Layer: SurfaceStations */
279 fprintf(fh
, "0\nLAYER\n2\nSurfaceStations\n");
280 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
281 fprintf(fh
, "62\n7\n"); /* color */
282 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
283 /* Next Layer: SurfaceLabels */
284 fprintf(fh
, "0\nLAYER\n2\nSurfaceLabels\n");
285 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
286 fprintf(fh
, "62\n7\n"); /* color */
287 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
289 /* Next Layer: Grid */
290 fprintf(fh
, "0\nLAYER\n2\nGrid\n");
291 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
292 fprintf(fh
, "62\n7\n"); /* color: kept the same used by SpeleoGen */
293 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
295 fprintf(fh
, "0\nENDTAB\n"
298 fprintf(fh
, "0\nSECTION\n"
303 x
= floor(min_x
/ grid
) * grid
+ grid
;
304 y
= floor(min_y
/ grid
) * grid
+ grid
;
306 /* horizontal line */
307 fprintf(fh
, "0\nLINE\n");
308 fprintf(fh
, "8\nGrid\n"); /* Layer */
309 fprintf(fh
, "10\n%6.2f\n", x
);
310 fprintf(fh
, "20\n%6.2f\n", min_y
);
311 fprintf(fh
, "30\n0\n");
312 fprintf(fh
, "11\n%6.2f\n", x
);
313 fprintf(fh
, "21\n%6.2f\n", max_y
);
314 fprintf(fh
, "31\n0\n");
319 fprintf(fh
, "0\nLINE\n");
320 fprintf(fh
, "8\nGrid\n"); /* Layer */
321 fprintf(fh
, "10\n%6.2f\n", min_x
);
322 fprintf(fh
, "20\n%6.2f\n", y
);
323 fprintf(fh
, "30\n0\n");
324 fprintf(fh
, "11\n%6.2f\n", max_x
);
325 fprintf(fh
, "21\n%6.2f\n", y
);
326 fprintf(fh
, "31\n0\n");
333 DXF::line(const img_point
*p1
, const img_point
*p
, unsigned flags
, bool fPendingMove
)
335 bool fSurface
= (flags
& SURF
);
336 (void)fPendingMove
; /* unused */
337 fprintf(fh
, "0\nLINE\n");
338 fprintf(fh
, fSurface
? "8\nSurface\n" : "8\nCentreLine\n"); /* Layer */
339 fprintf(fh
, "10\n%6.2f\n", p1
->x
);
340 fprintf(fh
, "20\n%6.2f\n", p1
->y
);
341 fprintf(fh
, "30\n%6.2f\n", p1
->z
);
342 fprintf(fh
, "11\n%6.2f\n", p
->x
);
343 fprintf(fh
, "21\n%6.2f\n", p
->y
);
344 fprintf(fh
, "31\n%6.2f\n", p
->z
);
348 DXF::label(const img_point
*p
, const char *s
, bool fSurface
, int)
350 /* write station label to dxf file */
351 fprintf(fh
, "0\nTEXT\n");
352 fprintf(fh
, fSurface
? "8\nSurfaceLabels\n" : "8\nLabels\n"); /* Layer */
353 fprintf(fh
, "10\n%6.2f\n", p
->x
);
354 fprintf(fh
, "20\n%6.2f\n", p
->y
);
355 fprintf(fh
, "30\n%6.2f\n", p
->z
);
356 fprintf(fh
, "40\n%6.2f\n", text_height
);
357 fprintf(fh
, "1\n%s\n", s
);
361 DXF::cross(const img_point
*p
, bool fSurface
)
363 /* write station marker to dxf file */
364 fprintf(fh
, "0\nPOINT\n");
365 fprintf(fh
, fSurface
? "8\nSurfaceStations\n" : "8\nStations\n"); /* Layer */
366 fprintf(fh
, "10\n%6.2f\n", p
->x
);
367 fprintf(fh
, "20\n%6.2f\n", p
->y
);
368 fprintf(fh
, "30\n%6.2f\n", p
->z
);
372 DXF::xsect(const img_point
*p
, double angle
, double d1
, double d2
)
374 double s
= sin(rad(angle
));
375 double c
= cos(rad(angle
));
376 fprintf(fh
, "0\nLINE\n");
377 fprintf(fh
, "8\nCross-sections\n"); /* Layer */
378 fprintf(fh
, "10\n%6.2f\n", p
->x
+ c
* d1
);
379 fprintf(fh
, "20\n%6.2f\n", p
->y
+ s
* d1
);
380 fprintf(fh
, "30\n%6.2f\n", p
->z
);
381 fprintf(fh
, "11\n%6.2f\n", p
->x
- c
* d2
);
382 fprintf(fh
, "21\n%6.2f\n", p
->y
- s
* d2
);
383 fprintf(fh
, "31\n%6.2f\n", p
->z
);
387 DXF::wall(const img_point
*p
, double angle
, double d
)
390 fprintf(fh
, "0\nPOLYLINE\n");
391 fprintf(fh
, "8\nWalls\n"); /* Layer */
392 fprintf(fh
, "70\n0\n"); /* bit 0 == 0 => Open polyline */
393 to_close
= "0\nSEQEND\n";
395 double s
= sin(rad(angle
));
396 double c
= cos(rad(angle
));
397 fprintf(fh
, "0\nVERTEX\n");
398 fprintf(fh
, "8\nWalls\n"); /* Layer */
399 fprintf(fh
, "10\n%6.2f\n", p
->x
+ c
* d
);
400 fprintf(fh
, "20\n%6.2f\n", p
->y
+ s
* d
);
401 fprintf(fh
, "30\n%6.2f\n", p
->z
);
405 DXF::passage(const img_point
*p
, double angle
, double d1
, double d2
)
407 fprintf(fh
, "0\nSOLID\n");
408 fprintf(fh
, "8\nPassages\n"); /* Layer */
409 double s
= sin(rad(angle
));
410 double c
= cos(rad(angle
));
411 double x1
= p
->x
+ c
* d1
;
412 double y1
= p
->y
+ s
* d1
;
413 double x2
= p
->x
- c
* d2
;
414 double y2
= p
->y
- s
* d2
;
417 fprintf(fh
, "12\n%6.2f\n22\n%6.2f\n32\n%6.2f\n"
418 "13\n%6.2f\n23\n%6.2f\n33\n%6.2f\n",
422 sprintf(pending
, "10\n%6.2f\n20\n%6.2f\n30\n%6.2f\n"
423 "11\n%6.2f\n21\n%6.2f\n31\n%6.2f\n",
441 fprintf(fh
, "000\nENDSEC\n");
442 fprintf(fh
, "000\nEOF\n");
445 class Skencil
: public ExportFilter
{
448 explicit Skencil(double scale
)
449 : factor(POINTS_PER_MM
* 1000.0 / scale
) { }
450 const int * passes() const;
451 void header(const char *, const char *, time_t,
452 double min_x
, double min_y
, double min_z
,
453 double max_x
, double max_y
, double max_z
);
454 void start_pass(int layer
);
455 void line(const img_point
*, const img_point
*, unsigned, bool);
456 void label(const img_point
*, const char *, bool, int);
457 void cross(const img_point
*, bool);
462 Skencil::passes() const
464 static const int skencil_passes
[] = { LEGS
|SURF
, STNS
, LABELS
, 0 };
465 return skencil_passes
;
469 Skencil::header(const char *, const char *, time_t,
470 double min_x
, double min_y
, double /*min_z*/,
471 double max_x
, double max_y
, double /*max_z*/)
473 fprintf(fh
, "##Sketch 1 2\n"); /* File format version */
474 fprintf(fh
, "document()\n");
475 fprintf(fh
, "layout((%.3f,%.3f),0)\n",
476 (max_x
- min_x
) * factor
, (max_y
- min_y
) * factor
);
480 Skencil::start_pass(int layer
)
482 fprintf(fh
, "layer('%s',1,1,0,0,(0,0,0))\n", layer_name(layer
));
486 Skencil::line(const img_point
*p1
, const img_point
*p
, unsigned flags
, bool fPendingMove
)
488 (void)flags
; /* unused */
490 fprintf(fh
, "b()\n");
491 fprintf(fh
, "bs(%.3f,%.3f,%.3f)\n", p1
->x
* factor
, p1
->y
* factor
, 0.0);
493 fprintf(fh
, "bs(%.3f,%.3f,%.3f)\n", p
->x
* factor
, p
->y
* factor
, 0.0);
497 Skencil::label(const img_point
*p
, const char *s
, bool fSurface
, int)
499 (void)fSurface
; /* unused */
500 fprintf(fh
, "fp((0,0,0))\n");
501 fprintf(fh
, "le()\n");
502 fprintf(fh
, "Fn('Times-Roman')\n");
503 fprintf(fh
, "Fs(5)\n");
504 fprintf(fh
, "txt('");
507 if (ch
== '\'' || ch
== '\\') PUTC('\\', fh
);
510 fprintf(fh
, "',(%.3f,%.3f))\n", p
->x
* factor
, p
->y
* factor
);
514 Skencil::cross(const img_point
*p
, bool fSurface
)
516 (void)fSurface
; /* unused */
517 fprintf(fh
, "b()\n");
518 fprintf(fh
, "bs(%.3f,%.3f,%.3f)\n",
519 p
->x
* factor
- marker_size
, p
->y
* factor
- marker_size
, 0.0);
520 fprintf(fh
, "bs(%.3f,%.3f,%.3f)\n",
521 p
->x
* factor
+ marker_size
, p
->y
* factor
+ marker_size
, 0.0);
522 fprintf(fh
, "bn()\n");
523 fprintf(fh
, "bs(%.3f,%.3f,%.3f)\n",
524 p
->x
* factor
+ marker_size
, p
->y
* factor
- marker_size
, 0.0);
525 fprintf(fh
, "bs(%.3f,%.3f,%.3f)\n",
526 p
->x
* factor
- marker_size
, p
->y
* factor
+ marker_size
, 0.0);
530 Skencil::footer(void)
532 fprintf(fh
, "guidelayer('Guide Lines',1,0,0,1,(0,0,1))\n");
534 fprintf(fh
, "grid((0,0,%.3f,%.3f),1,(0,0,1),'Grid')\n",
535 grid
* factor
, grid
* factor
);
539 typedef struct point
{
545 #define HTAB_SIZE 0x2000
550 set_name(const img_point
*p
, const char *s
)
555 char data
[sizeof(int) * 3];
559 u
.x
[0] = (int)(p
->x
* 100);
560 u
.x
[1] = (int)(p
->y
* 100);
561 u
.x
[2] = (int)(p
->z
* 100);
562 hash
= (hash_data(u
.data
, sizeof(int) * 3) & (HTAB_SIZE
- 1));
563 for (pt
= htab
[hash
]; pt
; pt
= pt
->next
) {
564 if (pt
->p
.x
== p
->x
&& pt
->p
.y
== p
->y
&& pt
->p
.z
== p
->z
) {
565 /* already got name for these coordinates */
566 /* FIXME: what about multiple names for the same station? */
572 pt
->label
= osstrdup(s
);
574 pt
->next
= htab
[hash
];
581 find_name(const img_point
*p
)
586 char data
[sizeof(int) * 3];
591 u
.x
[0] = (int)(p
->x
* 100);
592 u
.x
[1] = (int)(p
->y
* 100);
593 u
.x
[2] = (int)(p
->z
* 100);
594 hash
= (hash_data(u
.data
, sizeof(int) * 3) & (HTAB_SIZE
- 1));
595 for (pt
= htab
[hash
]; pt
; pt
= pt
->next
) {
596 if (pt
->p
.x
== p
->x
&& pt
->p
.y
== p
->y
&& pt
->p
.z
== p
->z
)
602 class SVG
: public ExportFilter
{
603 const char * to_close
;
606 /* for station labels */
611 SVG(double scale
, double text_height_
)
614 factor(1000.0 / scale
),
615 text_height(text_height_
) {
618 const int * passes() const;
619 void header(const char *, const char *, time_t,
620 double min_x
, double min_y
, double min_z
,
621 double max_x
, double max_y
, double max_z
);
622 void start_pass(int layer
);
623 void line(const img_point
*, const img_point
*, unsigned, bool);
624 void label(const img_point
*, const char *, bool, int);
625 void cross(const img_point
*, bool);
626 void xsect(const img_point
*, double, double, double);
627 void wall(const img_point
*, double, double);
628 void passage(const img_point
*, double, double, double);
636 static const int svg_passes
[] = {
637 PASG
, LEGS
|SURF
, XSECT
, WALL1
, WALL2
, LABELS
, STNS
, 0
643 SVG::header(const char * title
, const char *, time_t,
644 double min_x
, double min_y
, double /*min_z*/,
645 double max_x
, double max_y
, double /*max_z*/)
647 const char *unit
= "mm";
648 const double SVG_MARGIN
= 5.0; // In units of "unit".
649 htab
= (point
**)osmalloc(HTAB_SIZE
* ossizeof(point
*));
650 for (size_t i
= 0; i
< HTAB_SIZE
; ++i
) htab
[i
] = NULL
;
651 fprintf(fh
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
652 double width
= (max_x
- min_x
) * factor
+ SVG_MARGIN
* 2;
653 double height
= (max_y
- min_y
) * factor
+ SVG_MARGIN
* 2;
654 fprintf(fh
, "<svg version=\"1.1\" baseProfile=\"full\"\n"
655 "xmlns=\"http://www.w3.org/2000/svg\"\n"
656 "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
657 "xmlns:ev=\"http://www.w3.org/2001/xml-events\"\n"
658 "width=\"%.3f%s\" height=\"%.3f%s\"\n"
659 "viewBox=\"0 0 %0.3f %0.3f\">\n",
660 width
, unit
, height
, unit
, width
, height
);
661 if (title
&& title
[0]) {
662 fputs("<title>", fh
);
663 html_escape(fh
, title
);
664 fputs("</title>\n", fh
);
666 fprintf(fh
, "<g transform=\"translate(%.3f %.3f)\">\n",
667 SVG_MARGIN
- min_x
* factor
, SVG_MARGIN
+ max_y
* factor
);
673 SVG::start_pass(int layer
)
680 fprintf(fh
, "</g>\n");
682 fprintf(fh
, "<g id=\"%s\"", layer_name(layer
));
684 fprintf(fh
, " stroke=\"black\" fill=\"none\" stroke-width=\"0.4px\"");
685 else if (layer
& STNS
)
686 fprintf(fh
, " stroke=\"black\" fill=\"none\" stroke-width=\"0.05px\"");
687 else if (layer
& LABELS
)
688 fprintf(fh
, " font-size=\"%.3fem\"", text_height
);
689 else if (layer
& XSECT
)
690 fprintf(fh
, " stroke=\"grey\" fill=\"none\" stroke-width=\"0.1px\"");
691 else if (layer
& WALLS
)
692 fprintf(fh
, " stroke=\"black\" fill=\"none\" stroke-width=\"0.1px\"");
693 else if (layer
& PASG
)
694 fprintf(fh
, " stroke=\"none\" fill=\"peru\"");
701 SVG::line(const img_point
*p1
, const img_point
*p
, unsigned flags
, bool fPendingMove
)
703 bool splay
= (flags
& SPLAYS
);
708 fprintf(fh
, "<path ");
709 if (splay
) fprintf(fh
, "stroke=\"grey\" stroke-width=\"0.1px\" ");
710 fprintf(fh
, "d=\"M%.3f %.3f", p1
->x
* factor
, p1
->y
* -factor
);
712 fprintf(fh
, "L%.3f %.3f", p
->x
* factor
, p
->y
* -factor
);
717 SVG::label(const img_point
*p
, const char *s
, bool fSurface
, int)
719 (void)fSurface
; /* unused */
720 fprintf(fh
, "<text transform=\"translate(%.3f %.3f)\">",
721 p
->x
* factor
, p
->y
* -factor
);
723 fputs("</text>\n", fh
);
728 SVG::cross(const img_point
*p
, bool fSurface
)
730 (void)fSurface
; /* unused */
731 fprintf(fh
, "<circle id=\"%s\" cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\"/>\n",
732 find_name(p
), p
->x
* factor
, p
->y
* -factor
, marker_size
* SQRT_2
);
733 fprintf(fh
, "<path d=\"M%.3f %.3fL%.3f %.3fM%.3f %.3fL%.3f %.3f\"/>\n",
734 p
->x
* factor
- marker_size
, p
->y
* -factor
- marker_size
,
735 p
->x
* factor
+ marker_size
, p
->y
* -factor
+ marker_size
,
736 p
->x
* factor
+ marker_size
, p
->y
* -factor
- marker_size
,
737 p
->x
* factor
- marker_size
, p
->y
* -factor
+ marker_size
);
741 SVG::xsect(const img_point
*p
, double angle
, double d1
, double d2
)
743 double s
= sin(rad(angle
));
744 double c
= cos(rad(angle
));
745 fprintf(fh
, "<path d=\"M%.3f %.3fL%.3f %.3f\"/>\n",
746 (p
->x
+ c
* d1
) * factor
, (p
->y
+ s
* d1
) * -factor
,
747 (p
->x
- c
* d2
) * factor
, (p
->y
- s
* d2
) * -factor
);
751 SVG::wall(const img_point
*p
, double angle
, double d
)
754 fprintf(fh
, "<path d=\"M");
759 double s
= sin(rad(angle
));
760 double c
= cos(rad(angle
));
761 fprintf(fh
, "%.3f %.3f", (p
->x
+ c
* d
) * factor
, (p
->y
+ s
* d
) * -factor
);
765 SVG::passage(const img_point
*p
, double angle
, double d1
, double d2
)
767 double s
= sin(rad(angle
));
768 double c
= cos(rad(angle
));
769 double x1
= (p
->x
+ c
* d1
) * factor
;
770 double y1
= (p
->y
+ s
* d1
) * -factor
;
771 double x2
= (p
->x
- c
* d2
) * factor
;
772 double y2
= (p
->y
- s
* d2
) * -factor
;
775 fprintf(fh
, "L%.3f %.3fL%.3f %.3fZ\"/>\n", x2
, y2
, x1
, y1
);
777 sprintf(pending
, "<path d=\"M%.3f %.3fL%.3f %.3f", x1
, y1
, x2
, y2
);
798 fprintf(fh
, "</g>\n");
801 fprintf(fh
, "</g>\n</svg>\n");
804 class PLT
: public ExportFilter
{
807 const char * find_name_plt(const img_point
*p
);
809 double min_N
, max_N
, min_E
, max_E
, min_A
, max_A
;
813 const int * passes() const;
814 void header(const char *, const char *, time_t,
815 double min_x
, double min_y
, double min_z
,
816 double max_x
, double max_y
, double max_z
);
817 void line(const img_point
*, const img_point
*, unsigned, bool);
818 void label(const img_point
*, const char *, bool, int);
825 static const int plt_passes
[] = { LABELS
, LEGS
|SURF
, 0 };
830 PLT::header(const char *title
, const char *, time_t,
831 double min_x
, double min_y
, double min_z
,
832 double max_x
, double max_y
, double max_z
)
834 // FIXME: allow survey to be set from aven somehow!
835 const char *survey
= NULL
;
836 htab
= (point
**)osmalloc(HTAB_SIZE
* ossizeof(point
*));
837 for (size_t i
= 0; i
< HTAB_SIZE
; ++i
) htab
[i
] = NULL
;
838 /* Survex is E, N, Alt - PLT file is N, E, Alt */
839 min_N
= min_y
/ METRES_PER_FOOT
;
840 max_N
= max_y
/ METRES_PER_FOOT
;
841 min_E
= min_x
/ METRES_PER_FOOT
;
842 max_E
= max_x
/ METRES_PER_FOOT
;
843 min_A
= min_z
/ METRES_PER_FOOT
;
844 max_A
= max_z
/ METRES_PER_FOOT
;
845 fprintf(fh
, "Z %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
846 min_N
, max_N
, min_E
, max_E
, min_A
, max_A
);
847 fprintf(fh
, "N%s D 1 1 1 C%s\r\n", survey
? survey
: "X",
848 (title
&& title
[0]) ? title
: "X");
852 PLT::line(const img_point
*p1
, const img_point
*p
, unsigned flags
, bool fPendingMove
)
854 (void)flags
; /* unused */
856 /* Survex is E, N, Alt - PLT file is N, E, Alt */
857 fprintf(fh
, "M %.3f %.3f %.3f ",
858 p1
->y
/ METRES_PER_FOOT
, p1
->x
/ METRES_PER_FOOT
, p1
->z
/ METRES_PER_FOOT
);
859 /* dummy passage dimensions are required to avoid compass bug */
860 fprintf(fh
, "S%s P -9 -9 -9 -9\r\n", find_name_plt(p1
));
862 /* Survex is E, N, Alt - PLT file is N, E, Alt */
863 fprintf(fh
, "D %.3f %.3f %.3f ",
864 p
->y
/ METRES_PER_FOOT
, p
->x
/ METRES_PER_FOOT
, p
->z
/ METRES_PER_FOOT
);
865 /* dummy passage dimensions are required to avoid compass bug */
866 fprintf(fh
, "S%s P -9 -9 -9 -9\r\n", find_name_plt(p
));
870 PLT::find_name_plt(const img_point
*p
)
872 const char * s
= find_name(p
);
875 // PLT format can't handle spaces or control characters, so escape them
876 // like in URLs (an arbitrary choice of escaping, but at least a familiar
877 // one and % isn't likely to occur in station names).
879 for (q
= s
; *q
; ++q
) {
880 unsigned char ch
= *q
;
881 if (ch
<= ' ' || ch
== '%') {
882 escaped
.append(s
, q
- s
);
884 escaped
+= "0123456789abcdef"[ch
>> 4];
885 escaped
+= "0123456789abcdef"[ch
& 0x0f];
889 if (!escaped
.empty()) {
890 escaped
.append(s
, q
- s
);
891 return escaped
.c_str();
897 PLT::label(const img_point
*p
, const char *s
, bool fSurface
, int)
899 (void)fSurface
; /* unused */
906 /* Survex is E, N, Alt - PLT file is N, E, Alt */
907 fprintf(fh
, "X %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
908 min_N
, max_N
, min_E
, max_E
, min_A
, max_A
);
909 /* Yucky DOS "end of textfile" marker */
913 class EPS
: public ExportFilter
{
916 vector
<pair
<double, double>> psg
;
918 explicit EPS(double scale
)
919 : factor(POINTS_PER_MM
* 1000.0 / scale
) { }
920 const int * passes() const;
921 void header(const char *, const char *, time_t,
922 double min_x
, double min_y
, double min_z
,
923 double max_x
, double max_y
, double max_z
);
924 void start_pass(int layer
);
925 void line(const img_point
*, const img_point
*, unsigned, bool);
926 void label(const img_point
*, const char *, bool, int);
927 void cross(const img_point
*, bool);
928 void xsect(const img_point
*, double, double, double);
929 void wall(const img_point
*, double, double);
930 void passage(const img_point
*, double, double, double);
938 static const int eps_passes
[] = {
939 PASG
, XSECT
, WALL1
, WALL2
, LEGS
|SURF
|STNS
|LABELS
, 0
945 EPS::header(const char *title
, const char *, time_t,
946 double min_x
, double min_y
, double /*min_z*/,
947 double max_x
, double max_y
, double /*max_z*/)
949 const char * fontname_labels
= "helvetica"; // FIXME
950 int fontsize_labels
= 10; // FIXME
951 fputs("%!PS-Adobe-2.0 EPSF-1.2\n", fh
);
952 fputs("%%Creator: Survex " VERSION
" EPS Export Filter\n", fh
);
954 if (title
&& title
[0])
955 fprintf(fh
, "%%%%Title: %s\n", title
);
958 time_t now
= time(NULL
);
959 if (strftime(buf
, sizeof(buf
), "%Y-%m-%d %H:%M:%S %Z\n", localtime(&now
))) {
960 fputs("%%CreationDate: ", fh
);
965 #if defined(HAVE_GETPWUID) && !defined(__DJGPP__)
966 struct passwd
* ent
= getpwuid(getuid());
967 if (ent
&& ent
->pw_gecos
[0]) name
= ent
->pw_gecos
;
970 name
= ::wxGetUserName().mb_str();
972 name
= ::wxGetUserId().mb_str();
976 fprintf(fh
, "%%%%For: %s\n", name
.c_str());
979 fprintf(fh
, "%%%%BoundingBox: %d %d %d %d\n",
980 int(floor(min_x
* factor
)), int(floor(min_y
* factor
)),
981 int(ceil(max_x
* factor
)), int(ceil(max_y
* factor
)));
982 fprintf(fh
, "%%%%HiResBoundingBox: %.4f %.4f %.4f %.4f\n",
983 min_x
* factor
, min_y
* factor
, max_x
* factor
, max_y
* factor
);
984 fputs("%%LanguageLevel: 1\n"
985 "%%PageOrder: Ascend\n"
987 "%%Orientation: Portrait\n", fh
);
989 fprintf(fh
, "%%%%DocumentFonts: %s\n", fontname_labels
);
991 fputs("%%EndComments\n"
993 "save countdictstack mark\n", fh
);
995 /* this code adapted from a2ps */
996 fputs("%%BeginResource: encoding ISO88591Encoding\n"
997 "/ISO88591Encoding [\n", fh
);
998 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
999 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1000 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1001 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1002 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1003 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1004 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1005 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1007 "/space /exclam /quotedbl /numbersign\n"
1008 "/dollar /percent /ampersand /quoteright\n"
1009 "/parenleft /parenright /asterisk /plus\n"
1010 "/comma /minus /period /slash\n"
1011 "/zero /one /two /three\n"
1012 "/four /five /six /seven\n"
1013 "/eight /nine /colon /semicolon\n"
1014 "/less /equal /greater /question\n"
1015 "/at /A /B /C /D /E /F /G\n"
1016 "/H /I /J /K /L /M /N /O\n"
1017 "/P /Q /R /S /T /U /V /W\n"
1018 "/X /Y /Z /bracketleft\n"
1019 "/backslash /bracketright /asciicircum /underscore\n"
1020 "/quoteleft /a /b /c /d /e /f /g\n"
1021 "/h /i /j /k /l /m /n /o\n"
1022 "/p /q /r /s /t /u /v /w\n"
1023 "/x /y /z /braceleft\n"
1024 "/bar /braceright /asciitilde /.notdef\n", fh
);
1025 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1026 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1027 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1028 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1029 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1030 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1031 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1032 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
1034 "/space /exclamdown /cent /sterling\n"
1035 "/currency /yen /brokenbar /section\n"
1036 "/dieresis /copyright /ordfeminine /guillemotleft\n"
1037 "/logicalnot /hyphen /registered /macron\n"
1038 "/degree /plusminus /twosuperior /threesuperior\n"
1039 "/acute /mu /paragraph /bullet\n"
1040 "/cedilla /onesuperior /ordmasculine /guillemotright\n"
1041 "/onequarter /onehalf /threequarters /questiondown\n"
1042 "/Agrave /Aacute /Acircumflex /Atilde\n"
1043 "/Adieresis /Aring /AE /Ccedilla\n"
1044 "/Egrave /Eacute /Ecircumflex /Edieresis\n"
1045 "/Igrave /Iacute /Icircumflex /Idieresis\n"
1046 "/Eth /Ntilde /Ograve /Oacute\n"
1047 "/Ocircumflex /Otilde /Odieresis /multiply\n"
1048 "/Oslash /Ugrave /Uacute /Ucircumflex\n"
1049 "/Udieresis /Yacute /Thorn /germandbls\n"
1050 "/agrave /aacute /acircumflex /atilde\n"
1051 "/adieresis /aring /ae /ccedilla\n"
1052 "/egrave /eacute /ecircumflex /edieresis\n"
1053 "/igrave /iacute /icircumflex /idieresis\n"
1054 "/eth /ntilde /ograve /oacute\n"
1055 "/ocircumflex /otilde /odieresis /divide\n"
1056 "/oslash /ugrave /uacute /ucircumflex\n"
1057 "/udieresis /yacute /thorn /ydieresis\n"
1059 "%%EndResource\n", fh
);
1061 /* this code adapted from a2ps */
1063 "/reencode {\n" /* def */
1064 "dup length 5 add dict begin\n"
1067 "{ def }{ pop pop } ifelse\n"
1069 "/Encoding exch def\n"
1071 /* Use the font's bounding box to determine the ascent, descent,
1072 * and overall height; don't forget that these values have to be
1073 * transformed using the font's matrix.
1074 * We use `load' because sometimes BBox is executable, sometimes not.
1075 * Since we need 4 numbers and not an array avoid BBox from being executed
1077 "/FontBBox load aload pop\n"
1078 "FontMatrix transform /Ascent exch def pop\n"
1079 "FontMatrix transform /Descent exch def pop\n"
1080 "/FontHeight Ascent Descent sub def\n"
1082 /* Define these in case they're not in the FontInfo (also, here
1083 * they're easier to get to.
1085 "/UnderlinePosition 1 def\n"
1086 "/UnderlineThickness 1 def\n"
1088 /* Get the underline position and thickness if they're defined. */
1089 "currentdict /FontInfo known {\n"
1092 "dup /UnderlinePosition known {\n"
1093 "dup /UnderlinePosition get\n"
1094 "0 exch FontMatrix transform exch pop\n"
1095 "/UnderlinePosition exch def\n"
1098 "dup /UnderlineThickness known {\n"
1099 "/UnderlineThickness get\n"
1100 "0 exch FontMatrix transform exch pop\n"
1101 "/UnderlineThickness exch def\n"
1107 "} bind def\n", fh
);
1109 fprintf(fh
, "/lab ISO88591Encoding /%s findfont reencode definefont pop\n",
1112 fprintf(fh
, "/lab findfont %d scalefont setfont\n", int(fontsize_labels
));
1115 /* C<digit> changes colour */
1116 /* FIXME: read from ini */
1119 for (i
= 0; i
< sizeof(colour
) / sizeof(colour
[0]); ++i
) {
1120 fprintf(fh
, "/C%u {stroke %.3f %.3f %.3f setrgbcolor} def\n", i
,
1121 (double)(colour
[i
] & 0xff0000) / 0xff0000,
1122 (double)(colour
[i
] & 0xff00) / 0xff00,
1123 (double)(colour
[i
] & 0xff) / 0xff);
1129 /* Postscript definition for drawing a cross */
1130 fprintf(fh
, "/X {stroke moveto %.2f %.2f rmoveto %.2f %.2f rlineto "
1131 "%.2f 0 rmoveto %.2f %.2f rlineto %.2f %.2f rmoveto} def\n",
1132 -marker_size
, -marker_size
, marker_size
* 2, marker_size
* 2,
1133 -marker_size
* 2, marker_size
* 2, -marker_size
* 2,
1134 -marker_size
, marker_size
);
1136 /* define some functions to keep file short */
1137 fputs("/M {stroke moveto} def\n"
1138 "/P {stroke newpath moveto} def\n"
1139 "/F {closepath gsave 0.8 setgray fill grestore} def\n"
1141 "/R {rlineto} def\n"
1142 "/S {show} def\n", fh
);
1144 fprintf(fh
, "gsave %.8f dup scale\n", factor
);
1148 x
= floor(min_x
/ grid
) * grid
+ grid
;
1149 y
= floor(min_y
/ grid
) * grid
+ grid
;
1151 /* horizontal line */
1152 fprintf(fh
, "0\nLINE\n");
1153 fprintf(fh
, "8\nGrid\n"); /* Layer */
1154 fprintf(fh
, "10\n%6.2f\n", x
);
1155 fprintf(fh
, "20\n%6.2f\n", min_y
);
1156 fprintf(fh
, "30\n0\n");
1157 fprintf(fh
, "11\n%6.2f\n", x
);
1158 fprintf(fh
, "21\n%6.2f\n", max_y
);
1159 fprintf(fh
, "31\n0\n");
1164 fprintf(fh
, "0\nLINE\n");
1165 fprintf(fh
, "8\nGrid\n"); /* Layer */
1166 fprintf(fh
, "10\n%6.2f\n", min_x
);
1167 fprintf(fh
, "20\n%6.2f\n", y
);
1168 fprintf(fh
, "30\n0\n");
1169 fprintf(fh
, "11\n%6.2f\n", max_x
);
1170 fprintf(fh
, "21\n%6.2f\n", y
);
1171 fprintf(fh
, "31\n0\n");
1179 EPS::start_pass(int layer
)
1183 case LEGS
|SURF
|STNS
|LABELS
:
1184 fprintf(fh
, "0.1 setlinewidth\n");
1186 case PASG
: case XSECT
: case WALL1
: case WALL2
:
1187 fprintf(fh
, "0.01 setlinewidth\n");
1193 EPS::line(const img_point
*p1
, const img_point
*p
, unsigned flags
, bool fPendingMove
)
1195 (void)flags
; /* unused */
1197 fprintf(fh
, "%.2f %.2f M\n", p1
->x
, p1
->y
);
1199 fprintf(fh
, "%.2f %.2f L\n", p
->x
, p
->y
);
1203 EPS::label(const img_point
*p
, const char *s
, bool /*fSurface*/, int)
1205 fprintf(fh
, "%.2f %.2f M\n", p
->x
, p
->y
);
1208 unsigned char ch
= *s
++;
1210 case '(': case ')': case '\\': /* need to escape these characters */
1223 EPS::cross(const img_point
*p
, bool fSurface
)
1225 (void)fSurface
; /* unused */
1226 fprintf(fh
, "%.2f %.2f X\n", p
->x
, p
->y
);
1230 EPS::xsect(const img_point
*p
, double angle
, double d1
, double d2
)
1232 double s
= sin(rad(angle
));
1233 double c
= cos(rad(angle
));
1234 fprintf(fh
, "%.2f %.2f M %.2f %.2f R\n",
1235 p
->x
- c
* d2
, p
->y
- s
* d2
,
1236 c
* (d1
+ d2
), s
* (d1
+ d2
));
1240 EPS::wall(const img_point
*p
, double angle
, double d
)
1242 double s
= sin(rad(angle
));
1243 double c
= cos(rad(angle
));
1244 fprintf(fh
, "%.2f %.2f %c\n", p
->x
+ c
* d
, p
->y
+ s
* d
, first
? 'M' : 'L');
1249 EPS::passage(const img_point
*p
, double angle
, double d1
, double d2
)
1251 double s
= sin(rad(angle
));
1252 double c
= cos(rad(angle
));
1253 double x1
= p
->x
+ c
* d1
;
1254 double y1
= p
->y
+ s
* d1
;
1255 double x2
= p
->x
- c
* d2
;
1256 double y2
= p
->y
- s
* d2
;
1257 fprintf(fh
, "%.2f %.2f %c\n", x1
, y1
, first
? 'P' : 'L');
1259 psg
.push_back(make_pair(x2
, y2
));
1266 vector
<pair
<double, double>>::const_reverse_iterator i
;
1267 for (i
= psg
.rbegin(); i
!= psg
.rend(); ++i
) {
1268 fprintf(fh
, "%.2f %.2f L\n", i
->first
, i
->second
);
1278 fputs("stroke showpage grestore\n"
1280 "cleartomark countdictstack exch sub { end } repeat restore\n"
1284 class UseNumericCLocale
{
1285 char * current_locale
;
1288 UseNumericCLocale() {
1289 current_locale
= osstrdup(setlocale(LC_NUMERIC
, NULL
));
1290 setlocale(LC_NUMERIC
, "C");
1293 ~UseNumericCLocale() {
1294 setlocale(LC_NUMERIC
, current_locale
);
1295 osfree(current_locale
);
1300 transform_point(const Point
& pos
, const Vector3
* pre_offset
,
1301 double COS
, double SIN
, double COST
, double SINT
,
1304 double x
= pos
.GetX();
1305 double y
= pos
.GetY();
1306 double z
= pos
.GetZ();
1308 x
+= pre_offset
->GetX();
1309 y
+= pre_offset
->GetY();
1310 z
+= pre_offset
->GetZ();
1312 p
->x
= x
* COS
- y
* SIN
;
1313 double tmp
= x
* SIN
+ y
* COS
;
1314 p
->y
= z
* COST
- tmp
* SINT
;
1315 p
->z
= -(z
* SINT
+ tmp
* COST
);
1319 Export(const wxString
&fnm_out
, const wxString
&title
,
1320 const wxString
&datestamp
,
1322 const SurveyFilter
* filter
,
1323 double pan
, double tilt
, int show_mask
, export_format format
,
1324 double grid_
, double text_height
, double marker_size_
,
1327 UseNumericCLocale dummy
;
1328 int fPendingMove
= 0;
1331 double SIN
= sin(rad(pan
));
1332 double COS
= cos(rad(pan
));
1333 double SINT
= sin(rad(tilt
));
1334 double COST
= cos(rad(tilt
));
1337 marker_size
= marker_size_
;
1339 // Do we need to calculate min and max for each dimension?
1340 bool need_bounds
= true;
1341 ExportFilter
* filt
;
1344 filt
= new POS(model
.GetSeparator(), true);
1345 show_mask
|= FULL_COORDS
;
1346 need_bounds
= false;
1349 filt
= new DXF(text_height
);
1352 filt
= new EPS(scale
);
1355 filt
= new GPX(model
.GetCSProj().c_str());
1356 show_mask
|= FULL_COORDS
;
1357 need_bounds
= false;
1361 // factor = POINTS_PER_MM * 1000.0 / scale;
1362 // HPGL doesn't use the bounds itself, but they are needed to set
1363 // the origin to the centre of lower left.
1369 bool clamp_to_ground
= (show_mask
& CLAMP_TO_GROUND
);
1370 filt
= new KML(model
.GetCSProj().c_str(), clamp_to_ground
);
1371 show_mask
|= FULL_COORDS
;
1372 need_bounds
= false;
1377 show_mask
|= FULL_COORDS
;
1380 filt
= new POS(model
.GetSeparator(), false);
1381 show_mask
|= FULL_COORDS
;
1382 need_bounds
= false;
1385 filt
= new Skencil(scale
);
1388 filt
= new SVG(scale
, text_height
);
1394 if (!filt
->fopen(fnm_out
)) {
1399 const Vector3
* pre_offset
= NULL
;
1400 if (show_mask
& FULL_COORDS
) {
1401 pre_offset
= &(model
.GetOffset());
1404 /* Get bounding box */
1405 double min_x
, min_y
, min_z
, max_x
, max_y
, max_z
;
1406 min_x
= min_y
= min_z
= HUGE_VAL
;
1407 max_x
= max_y
= max_z
= -HUGE_VAL
;
1409 for (int f
= 0; f
!= 8; ++f
) {
1410 if ((show_mask
& (f
& img_FLAG_SURFACE
) ? SURF
: LEGS
) == 0) {
1411 // Not showing traverse because of surface/underground status.
1414 if ((f
& img_FLAG_SPLAY
) && (show_mask
& SPLAYS
) == 0) {
1415 // Not showing because it's a splay.
1418 list
<traverse
>::const_iterator trav
= model
.traverses_begin(f
, filter
);
1419 list
<traverse
>::const_iterator tend
= model
.traverses_end(f
);
1420 for ( ; trav
!= tend
; trav
= model
.traverses_next(f
, filter
, trav
)) {
1421 vector
<PointInfo
>::const_iterator pos
= trav
->begin();
1422 vector
<PointInfo
>::const_iterator end
= trav
->end();
1423 for ( ; pos
!= end
; ++pos
) {
1424 transform_point(*pos
, pre_offset
, COS
, SIN
, COST
, SINT
, &p
);
1426 if (p
.x
< min_x
) min_x
= p
.x
;
1427 if (p
.x
> max_x
) max_x
= p
.x
;
1428 if (p
.y
< min_y
) min_y
= p
.y
;
1429 if (p
.y
> max_y
) max_y
= p
.y
;
1430 if (p
.z
< min_z
) min_z
= p
.z
;
1431 if (p
.z
> max_z
) max_z
= p
.z
;
1435 list
<LabelInfo
*>::const_iterator pos
= model
.GetLabels();
1436 list
<LabelInfo
*>::const_iterator end
= model
.GetLabelsEnd();
1437 for ( ; pos
!= end
; ++pos
) {
1438 if (filter
&& !filter
->CheckVisible((*pos
)->GetText()))
1441 transform_point(**pos
, pre_offset
, COS
, SIN
, COST
, SINT
, &p
);
1443 if (p
.x
< min_x
) min_x
= p
.x
;
1444 if (p
.x
> max_x
) max_x
= p
.x
;
1445 if (p
.y
< min_y
) min_y
= p
.y
;
1446 if (p
.y
> max_y
) max_y
= p
.y
;
1447 if (p
.z
< min_z
) min_z
= p
.z
;
1448 if (p
.z
> max_z
) max_z
= p
.z
;
1459 /* Handle empty file and gracefully, and also zero for the !need_bounds
1461 if (min_x
> max_x
) {
1462 min_x
= min_y
= min_z
= 0;
1463 max_x
= max_y
= max_z
= 0;
1466 double x_offset
, y_offset
, z_offset
;
1467 if (show_mask
& FULL_COORDS
) {
1468 // Full coordinates - offset is applied before rotations.
1469 x_offset
= y_offset
= z_offset
= 0.0;
1470 } else if (show_mask
& CENTRED
) {
1472 x_offset
= (min_x
+ max_x
) * -0.5;
1473 y_offset
= (min_y
+ max_y
) * -0.5;
1474 z_offset
= (min_z
+ max_z
) * -0.5;
1476 // Origin at lowest SW corner.
1491 filt
->header(title
.utf8_str(), datestamp
.utf8_str(), model
.GetDateStamp(),
1492 min_x
, min_y
, min_z
, max_x
, max_y
, max_z
);
1494 p1
.x
= p1
.y
= p1
.z
= 0; /* avoid compiler warning */
1496 for (pass
= filt
->passes(); *pass
; ++pass
) {
1497 int pass_mask
= show_mask
& *pass
;
1500 filt
->start_pass(*pass
);
1501 if (pass_mask
& (LEGS
|SURF
)) {
1502 for (int f
= 0; f
!= 8; ++f
) {
1503 unsigned flags
= (f
& img_FLAG_SURFACE
) ? SURF
: LEGS
;
1504 if ((pass_mask
& flags
) == 0) {
1505 // Not showing traverse because of surface/underground status.
1508 if ((f
& img_FLAG_SPLAY
) && (show_mask
& SPLAYS
) == 0) {
1509 // Not showing because it's a splay.
1512 if (f
& img_FLAG_SPLAY
) flags
|= SPLAYS
;
1513 list
<traverse
>::const_iterator trav
= model
.traverses_begin(f
, filter
);
1514 list
<traverse
>::const_iterator tend
= model
.traverses_end(f
);
1515 for ( ; trav
!= tend
; trav
= model
.traverses_next(f
, filter
, trav
)) {
1516 assert(trav
->size() > 1);
1517 vector
<PointInfo
>::const_iterator pos
= trav
->begin();
1518 vector
<PointInfo
>::const_iterator end
= trav
->end();
1519 for ( ; pos
!= end
; ++pos
) {
1520 transform_point(*pos
, pre_offset
, COS
, SIN
, COST
, SINT
, &p
);
1525 if (pos
== trav
->begin()) {
1526 // First point is move...
1529 filt
->line(&p1
, &p
, flags
, fPendingMove
);
1537 if (pass_mask
& (STNS
|LABELS
|ENTS
|FIXES
|EXPORTS
)) {
1538 list
<LabelInfo
*>::const_iterator pos
= model
.GetLabels();
1539 list
<LabelInfo
*>::const_iterator end
= model
.GetLabelsEnd();
1540 for ( ; pos
!= end
; ++pos
) {
1541 if (filter
&& !filter
->CheckVisible((*pos
)->GetText()))
1544 transform_point(**pos
, pre_offset
, COS
, SIN
, COST
, SINT
, &p
);
1550 if ((pass_mask
& ENTS
) && (*pos
)->IsEntrance()) {
1552 } else if ((pass_mask
& FIXES
) && (*pos
)->IsFixedPt()) {
1554 } else if ((pass_mask
& EXPORTS
) && (*pos
)->IsExportedPt()) {
1556 } else if (pass_mask
& LABELS
) {
1559 /* Use !UNDERGROUND as the criterion - we want stations where a
1560 * surface and underground survey meet to be in the underground
1562 bool f_surface
= !(*pos
)->IsUnderground();
1564 const wxString
& text
= (*pos
)->GetText();
1565 filt
->label(&p
, text
.utf8_str(), f_surface
, type
);
1567 if (pass_mask
& STNS
)
1568 filt
->cross(&p
, f_surface
);
1571 if (pass_mask
& (XSECT
|WALLS
|PASG
)) {
1572 bool elevation
= (tilt
== 0.0);
1573 list
<vector
<XSect
>>::const_iterator tube
= model
.tubes_begin();
1574 list
<vector
<XSect
>>::const_iterator tube_end
= model
.tubes_end();
1575 for ( ; tube
!= tube_end
; ++tube
) {
1576 vector
<XSect
>::const_iterator pos
= tube
->begin();
1577 vector
<XSect
>::const_iterator end
= tube
->end();
1578 size_t active_tube_len
= 0;
1579 for ( ; pos
!= end
; ++pos
) {
1580 const XSect
& xs
= *pos
;
1581 // FIXME: This filtering can create tubes containing a single
1582 // cross-section, which otherwise don't exist in aven (the
1583 // Model class currently filters them out). Perhaps we
1584 // should just always include these - a single set of LRUD
1585 // measurements is useful even if a single cross-section
1586 // 3D tube perhaps isn't.
1587 if (filter
&& !filter
->CheckVisible(xs
.GetLabel())) {
1588 // Close any active tube.
1589 if (active_tube_len
> 0) {
1590 active_tube_len
= 0;
1597 transform_point(xs
.GetPoint(), pre_offset
, COS
, SIN
, COST
, SINT
, &p
);
1603 if (pass_mask
& XSECT
)
1604 filt
->xsect(&p
, 90, xs
.GetU(), xs
.GetD());
1605 if (pass_mask
& WALL1
)
1606 filt
->wall(&p
, 90, xs
.GetU());
1607 if (pass_mask
& WALL2
)
1608 filt
->wall(&p
, 270, xs
.GetD());
1609 if (pass_mask
& PASG
)
1610 filt
->passage(&p
, 90, xs
.GetU(), xs
.GetD());
1612 // Should only be enabled in plan or elevation mode.
1613 double angle
= pan
+ xs
.get_right_bearing();
1614 if (pass_mask
& XSECT
)
1615 filt
->xsect(&p
, angle
+ 180, xs
.GetL(), xs
.GetR());
1616 if (pass_mask
& WALL1
)
1617 filt
->wall(&p
, angle
+ 180, xs
.GetL());
1618 if (pass_mask
& WALL2
)
1619 filt
->wall(&p
, angle
, xs
.GetR());
1620 if (pass_mask
& PASG
)
1621 filt
->passage(&p
, angle
+ 180, xs
.GetL(), xs
.GetR());
1624 if (active_tube_len
> 0) {