2 * Export to GIS formats, CAD formats, and other formats.
5 /* Copyright (C) 1994-2024 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
30 #include "exportfilter.h"
31 #include "gdalexport.h"
55 #include "img_hosted.h"
59 #define SQRT_2 1.41421356237309504880168872420969
61 // Order here needs to match order of export_format enum in export.h.
63 const format_info export_format_info
[] = {
64 { ".3d", /*Survex 3d files*/207,
65 LABELS
|LEGS
|SURF
|SPLAYS
|ENTS
|FIXES
|EXPORTS
, /* FIXME: expand... */
66 LABELS
|LEGS
|SURF
|SPLAYS
|ENTS
|FIXES
|EXPORTS
},
67 { ".csv", /*CSV files*/101,
68 LABELS
|ENTS
|FIXES
|EXPORTS
,
70 { ".dxf", /*DXF files*/411,
71 LABELS
|LEGS
|SURF
|SPLAYS
|STNS
|PASG
|XSECT
|WALLS
|MARKER_SIZE
|TEXT_HEIGHT
|GRID
|FULL_COORDS
|ORIENTABLE
,
73 { ".eps", /*EPS files*/412,
74 LABELS
|LEGS
|SURF
|SPLAYS
|STNS
|PASG
|XSECT
|WALLS
|ORIENTABLE
,
76 { ".gpx", /*GPX files*/413,
77 LABELS
|LEGS
|SURF
|SPLAYS
|ENTS
|FIXES
|EXPORTS
|PROJ
,
79 /* TRANSLATORS: Here "plotter" refers to a machine which draws a printout
80 * on a (usually large) sheet of paper using a pen mounted in a motorised
82 { ".hpgl", /*HPGL for plotters*/414,
83 LABELS
|LEGS
|SURF
|SPLAYS
|STNS
|CENTRED
|SCALE
|ORIENTABLE
,
85 { ".json", /*JSON files*/445,
86 LEGS
|SURF
|SPLAYS
|CENTRED
,
88 { ".kml", /*KML files*/444,
89 LABELS
|LEGS
|SURF
|SPLAYS
|PASG
|XSECT
|WALLS
|ENTS
|FIXES
|EXPORTS
|PROJ
|CLAMP_TO_GROUND
,
91 /* TRANSLATORS: "Compass" and "Carto" are the names of software packages,
92 * so should not be translated:
93 * https://www.fountainware.com/compass/ */
94 { ".plt", /*Compass PLT for use with Carto*/415,
95 LABELS
|LEGS
|SURF
|SPLAYS
|ORIENTABLE
,
97 /* TRANSLATORS: Survex is the name of the software, and "pos" refers to a
98 * file extension, so neither should be translated. */
99 { ".pos", /*Survex pos files*/166,
100 LABELS
|ENTS
|FIXES
|EXPORTS
,
102 { ".svg", /*SVG files*/417,
103 LABELS
|LEGS
|SURF
|SPLAYS
|STNS
|PASG
|XSECT
|WALLS
|MARKER_SIZE
|TEXT_HEIGHT
|SCALE
|ORIENTABLE
,
105 { ".shp", /*Shapefiles (lines)*/523,
108 { ".shp", /*Shapefiles (points)*/524,
109 LABELS
|ENTS
|FIXES
|EXPORTS
|STNS
,
113 static_assert(sizeof(export_format_info
) == FMT_MAX_PLUS_ONE_
* sizeof(export_format_info
[0]),
114 "export_format_info[] matches enum export_format");
117 html_escape(FILE *fh
, const char *s
)
138 static const char *layer_name(int mask
) {
140 case LEGS
: case LEGS
|SURF
:
149 return "Cross-sections";
150 case WALL1
: case WALL2
: case WALLS
:
158 static double marker_size
; /* for station markers */
159 static double grid
; /* grid spacing (or 0 for no grid) */
162 ExportFilter::passes() const
164 static const int default_passes
[] = { LEGS
|SURF
|STNS
|LABELS
, 0 };
165 return default_passes
;
168 class DXF
: public ExportFilter
{
169 const char * to_close
= nullptr;
170 /* for station labels */
175 explicit DXF(double text_height_
)
176 : text_height(text_height_
) { pending
[0] = '\0'; }
177 const int * passes() const override
;
178 bool fopen(const wxString
& fnm_out
) override
;
179 void header(const char *, const char *, time_t,
180 double min_x
, double min_y
, double min_z
,
181 double max_x
, double max_y
, double max_z
) override
;
182 void line(const img_point
*, const img_point
*, unsigned, bool) override
;
183 void label(const img_point
*, const wxString
&, int, int) override
;
184 void cross(const img_point
*, const wxString
&, int) override
;
185 void xsect(const img_point
*, double, double, double) override
;
186 void wall(const img_point
*, double, double) override
;
187 void passage(const img_point
*, double, double, double) override
;
188 void tube_end() override
;
189 void footer() override
;
195 static const int dxf_passes
[] = {
196 PASG
, XSECT
, WALL1
, WALL2
, LEGS
|SURF
|STNS
|LABELS
, 0
202 DXF::fopen(const wxString
& fnm_out
)
204 // DXF gets written as text rather than binary.
205 fh
= wxFopen(fnm_out
.fn_str(), wxT("w"));
210 DXF::header(const char *, const char *, time_t,
211 double min_x
, double min_y
, double min_z
,
212 double max_x
, double max_y
, double max_z
)
214 fprintf(fh
, "0\nSECTION\n"
216 fprintf(fh
, "9\n$EXTMIN\n"); /* lower left corner of drawing */
217 fprintf(fh
, "10\n%#-.2f\n", min_x
); /* x */
218 fprintf(fh
, "20\n%#-.2f\n", min_y
); /* y */
219 fprintf(fh
, "30\n%#-.2f\n", min_z
); /* min z */
220 fprintf(fh
, "9\n$EXTMAX\n"); /* upper right corner of drawing */
221 fprintf(fh
, "10\n%#-.2f\n", max_x
); /* x */
222 fprintf(fh
, "20\n%#-.2f\n", max_y
); /* y */
223 fprintf(fh
, "30\n%#-.2f\n", max_z
); /* max z */
224 fprintf(fh
, "9\n$PDMODE\n70\n3\n"); /* marker style as CROSS */
225 fprintf(fh
, "9\n$PDSIZE\n40\n%6.2f\n", marker_size
); /* marker size */
226 fprintf(fh
, "0\nENDSEC\n");
228 fprintf(fh
, "0\nSECTION\n"
230 fprintf(fh
, "0\nTABLE\n" /* Define CONTINUOUS and DASHED line types. */
249 "0\nLTYPE\n" /* define DOT line type */
259 fprintf(fh
, "0\nTABLE\n"
261 fprintf(fh
, "70\n10\n"); /* max # off layers in this DXF file : 10 */
262 /* First Layer: CentreLine */
263 fprintf(fh
, "0\nLAYER\n2\nCentreLine\n");
264 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
265 fprintf(fh
, "62\n5\n"); /* color: kept the same used by SpeleoGen */
266 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
267 /* Next Layer: Stations */
268 fprintf(fh
, "0\nLAYER\n2\nStations\n");
269 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
270 fprintf(fh
, "62\n7\n"); /* color: kept the same used by SpeleoGen */
271 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
272 /* Next Layer: Labels */
273 fprintf(fh
, "0\nLAYER\n2\nLabels\n");
274 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
275 fprintf(fh
, "62\n7\n"); /* color: kept the same used by SpeleoGen */
276 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
277 /* Next Layer: Surface */
278 fprintf(fh
, "0\nLAYER\n2\nSurface\n");
279 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
280 fprintf(fh
, "62\n5\n"); /* color */
281 fprintf(fh
, "6\nDASHED\n"); /* linetype */
282 /* Next Layer: SurfaceStations */
283 fprintf(fh
, "0\nLAYER\n2\nSurfaceStations\n");
284 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
285 fprintf(fh
, "62\n7\n"); /* color */
286 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
287 /* Next Layer: SurfaceLabels */
288 fprintf(fh
, "0\nLAYER\n2\nSurfaceLabels\n");
289 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
290 fprintf(fh
, "62\n7\n"); /* color */
291 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
292 /* Next Layer: Splays */
293 fprintf(fh
, "0\nLAYER\n2\nSplays\n");
294 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
295 fprintf(fh
, "62\n5\n"); /* color */
296 fprintf(fh
, "6\nDOT\n"); /* linetype; */
298 /* Next Layer: Grid */
299 fprintf(fh
, "0\nLAYER\n2\nGrid\n");
300 fprintf(fh
, "70\n64\n"); /* shows layer is referenced by entities */
301 fprintf(fh
, "62\n7\n"); /* color: kept the same used by SpeleoGen */
302 fprintf(fh
, "6\nCONTINUOUS\n"); /* linetype */
304 fprintf(fh
, "0\nENDTAB\n"
307 fprintf(fh
, "0\nSECTION\n"
312 x
= floor(min_x
/ grid
) * grid
+ grid
;
313 y
= floor(min_y
/ grid
) * grid
+ grid
;
315 /* horizontal line */
316 fprintf(fh
, "0\nLINE\n");
317 fprintf(fh
, "8\nGrid\n"); /* Layer */
318 fprintf(fh
, "10\n%6.2f\n", x
);
319 fprintf(fh
, "20\n%6.2f\n", min_y
);
320 fprintf(fh
, "30\n0\n");
321 fprintf(fh
, "11\n%6.2f\n", x
);
322 fprintf(fh
, "21\n%6.2f\n", max_y
);
323 fprintf(fh
, "31\n0\n");
328 fprintf(fh
, "0\nLINE\n");
329 fprintf(fh
, "8\nGrid\n"); /* Layer */
330 fprintf(fh
, "10\n%6.2f\n", min_x
);
331 fprintf(fh
, "20\n%6.2f\n", y
);
332 fprintf(fh
, "30\n0\n");
333 fprintf(fh
, "11\n%6.2f\n", max_x
);
334 fprintf(fh
, "21\n%6.2f\n", y
);
335 fprintf(fh
, "31\n0\n");
342 DXF::line(const img_point
*p1
, const img_point
*p
, unsigned flags
, bool fPendingMove
)
344 bool fSurface
= (flags
& SURF
);
345 bool fSplay
= (flags
& SPLAYS
);
346 (void)fPendingMove
; /* unused */
347 fprintf(fh
, "0\nLINE\n");
348 if (fSurface
) { /* select layer */
349 fprintf(fh
, "8\nSurface\n" );
351 fprintf(fh
, "8\nSplays\n");
353 fprintf(fh
, "8\nCentreLine\n");
355 fprintf(fh
, "10\n%6.2f\n", p1
->x
);
356 fprintf(fh
, "20\n%6.2f\n", p1
->y
);
357 fprintf(fh
, "30\n%6.2f\n", p1
->z
);
358 fprintf(fh
, "11\n%6.2f\n", p
->x
);
359 fprintf(fh
, "21\n%6.2f\n", p
->y
);
360 fprintf(fh
, "31\n%6.2f\n", p
->z
);
364 DXF::label(const img_point
*p
, const wxString
& str
, int sflags
, int)
366 // Use !UNDERGROUND as the criterion - we want stations where a surface and
367 // underground survey meet to be in the underground layer.
368 bool surface
= !(sflags
& img_SFLAG_UNDERGROUND
);
369 /* write station label to dxf file */
370 const char* s
= str
.utf8_str();
371 fprintf(fh
, "0\nTEXT\n");
372 fprintf(fh
, surface
? "8\nSurfaceLabels\n" : "8\nLabels\n"); /* Layer */
373 fprintf(fh
, "10\n%6.2f\n", p
->x
);
374 fprintf(fh
, "20\n%6.2f\n", p
->y
);
375 fprintf(fh
, "30\n%6.2f\n", p
->z
);
376 fprintf(fh
, "40\n%6.2f\n", text_height
);
377 fprintf(fh
, "1\n%s\n", s
);
381 DXF::cross(const img_point
*p
, const wxString
&, int sflags
)
383 // Use !UNDERGROUND as the criterion - we want stations where a surface and
384 // underground survey meet to be in the underground layer.
385 bool surface
= !(sflags
& img_SFLAG_UNDERGROUND
);
386 /* write station marker to dxf file */
387 fprintf(fh
, "0\nPOINT\n");
388 fprintf(fh
, surface
? "8\nSurfaceStations\n" : "8\nStations\n"); /* Layer */
389 fprintf(fh
, "10\n%6.2f\n", p
->x
);
390 fprintf(fh
, "20\n%6.2f\n", p
->y
);
391 fprintf(fh
, "30\n%6.2f\n", p
->z
);
395 DXF::xsect(const img_point
*p
, double angle
, double d1
, double d2
)
397 double s
= sin(rad(angle
));
398 double c
= cos(rad(angle
));
399 fprintf(fh
, "0\nLINE\n");
400 fprintf(fh
, "8\nCross-sections\n"); /* Layer */
401 fprintf(fh
, "10\n%6.2f\n", p
->x
+ s
* d1
);
402 fprintf(fh
, "20\n%6.2f\n", p
->y
+ c
* d1
);
403 fprintf(fh
, "30\n%6.2f\n", p
->z
);
404 fprintf(fh
, "11\n%6.2f\n", p
->x
- s
* d2
);
405 fprintf(fh
, "21\n%6.2f\n", p
->y
- c
* d2
);
406 fprintf(fh
, "31\n%6.2f\n", p
->z
);
410 DXF::wall(const img_point
*p
, double angle
, double d
)
413 fprintf(fh
, "0\nPOLYLINE\n");
414 fprintf(fh
, "8\nWalls\n"); /* Layer */
415 fprintf(fh
, "70\n0\n"); /* bit 0 == 0 => Open polyline */
416 to_close
= "0\nSEQEND\n";
418 double s
= sin(rad(angle
));
419 double c
= cos(rad(angle
));
420 fprintf(fh
, "0\nVERTEX\n");
421 fprintf(fh
, "8\nWalls\n"); /* Layer */
422 fprintf(fh
, "10\n%6.2f\n", p
->x
+ s
* d
);
423 fprintf(fh
, "20\n%6.2f\n", p
->y
+ c
* d
);
424 fprintf(fh
, "30\n%6.2f\n", p
->z
);
428 DXF::passage(const img_point
*p
, double angle
, double d1
, double d2
)
430 fprintf(fh
, "0\nSOLID\n");
431 fprintf(fh
, "8\nPassages\n"); /* Layer */
432 double s
= sin(rad(angle
));
433 double c
= cos(rad(angle
));
434 double x1
= p
->x
+ s
* d1
;
435 double y1
= p
->y
+ c
* d1
;
436 double x2
= p
->x
- s
* d2
;
437 double y2
= p
->y
- c
* d2
;
440 fprintf(fh
, "12\n%6.2f\n22\n%6.2f\n32\n%6.2f\n"
441 "13\n%6.2f\n23\n%6.2f\n33\n%6.2f\n",
445 snprintf(pending
, sizeof(pending
),
446 "10\n%6.2f\n20\n%6.2f\n30\n%6.2f\n"
447 "11\n%6.2f\n21\n%6.2f\n31\n%6.2f\n",
465 fprintf(fh
, "000\nENDSEC\n");
466 fprintf(fh
, "000\nEOF\n");
469 typedef struct point
{
475 #define HTAB_SIZE 0x2000
480 set_name(const img_point
*p
, const char *s
)
485 char data
[sizeof(int) * 3];
489 u
.x
[0] = (int)(p
->x
* 100);
490 u
.x
[1] = (int)(p
->y
* 100);
491 u
.x
[2] = (int)(p
->z
* 100);
492 hash
= (hash_data(u
.data
, sizeof(int) * 3) & (HTAB_SIZE
- 1));
493 for (pt
= htab
[hash
]; pt
; pt
= pt
->next
) {
494 if (pt
->p
.x
== p
->x
&& pt
->p
.y
== p
->y
&& pt
->p
.z
== p
->z
) {
495 /* already got name for these coordinates */
496 /* FIXME: what about multiple names for the same station? */
502 pt
->label
= osstrdup(s
);
504 pt
->next
= htab
[hash
];
511 find_name(const img_point
*p
)
516 char data
[sizeof(int) * 3];
521 u
.x
[0] = (int)(p
->x
* 100);
522 u
.x
[1] = (int)(p
->y
* 100);
523 u
.x
[2] = (int)(p
->z
* 100);
524 hash
= (hash_data(u
.data
, sizeof(int) * 3) & (HTAB_SIZE
- 1));
525 for (pt
= htab
[hash
]; pt
; pt
= pt
->next
) {
526 if (pt
->p
.x
== p
->x
&& pt
->p
.y
== p
->y
&& pt
->p
.z
== p
->z
)
532 class SVG
: public ExportFilter
{
533 const char * to_close
= nullptr;
534 bool close_g
= false;
536 /* for station labels */
541 SVG(double scale
, double text_height_
)
542 : factor(1000.0 / scale
),
543 text_height(text_height_
) {
546 const int * passes() const override
;
547 void header(const char *, const char *, time_t,
548 double min_x
, double min_y
, double min_z
,
549 double max_x
, double max_y
, double max_z
) override
;
550 void start_pass(int layer
) override
;
551 void line(const img_point
*, const img_point
*, unsigned, bool) override
;
552 void label(const img_point
*, const wxString
&, int, int) override
;
553 void cross(const img_point
*, const wxString
&, int) override
;
554 void xsect(const img_point
*, double, double, double) override
;
555 void wall(const img_point
*, double, double) override
;
556 void passage(const img_point
*, double, double, double) override
;
557 void tube_end() override
;
558 void footer() override
;
564 static const int svg_passes
[] = {
565 PASG
, LEGS
|SURF
, XSECT
, WALL1
, WALL2
, LABELS
, STNS
, 0
571 SVG::header(const char * title
, const char *, time_t,
572 double min_x
, double min_y
, double /*min_z*/,
573 double max_x
, double max_y
, double /*max_z*/)
575 const char *unit
= "mm";
576 const double SVG_MARGIN
= 5.0; // In units of "unit".
577 htab
= (point
**)osmalloc(HTAB_SIZE
* ossizeof(point
*));
578 for (size_t i
= 0; i
< HTAB_SIZE
; ++i
) htab
[i
] = NULL
;
579 fprintf(fh
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
580 double width
= (max_x
- min_x
) * factor
+ SVG_MARGIN
* 2;
581 double height
= (max_y
- min_y
) * factor
+ SVG_MARGIN
* 2;
582 fprintf(fh
, "<svg version=\"1.1\" baseProfile=\"full\"\n"
583 "xmlns=\"http://www.w3.org/2000/svg\"\n"
584 "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
585 "xmlns:ev=\"http://www.w3.org/2001/xml-events\"\n"
586 "width=\"%.3f%s\" height=\"%.3f%s\"\n"
587 "viewBox=\"0 0 %0.3f %0.3f\">\n",
588 width
, unit
, height
, unit
, width
, height
);
589 if (title
&& title
[0]) {
590 fputs("<title>", fh
);
591 html_escape(fh
, title
);
592 fputs("</title>\n", fh
);
594 fprintf(fh
, "<g transform=\"translate(%.3f %.3f)\">\n",
595 SVG_MARGIN
- min_x
* factor
, SVG_MARGIN
+ max_y
* factor
);
601 SVG::start_pass(int layer
)
608 fprintf(fh
, "</g>\n");
610 fprintf(fh
, "<g id=\"%s\"", layer_name(layer
));
612 fprintf(fh
, " stroke=\"black\" fill=\"none\" stroke-width=\"0.4px\"");
613 else if (layer
& STNS
)
614 fprintf(fh
, " stroke=\"black\" fill=\"none\" stroke-width=\"0.05px\"");
615 else if (layer
& LABELS
)
616 fprintf(fh
, " font-size=\"%.3fem\"", text_height
);
617 else if (layer
& XSECT
)
618 fprintf(fh
, " stroke=\"grey\" fill=\"none\" stroke-width=\"0.1px\"");
619 else if (layer
& WALLS
)
620 fprintf(fh
, " stroke=\"black\" fill=\"none\" stroke-width=\"0.1px\"");
621 else if (layer
& PASG
)
622 fprintf(fh
, " stroke=\"none\" fill=\"peru\"");
629 SVG::line(const img_point
*p1
, const img_point
*p
, unsigned flags
, bool fPendingMove
)
631 bool splay
= (flags
& SPLAYS
);
636 fprintf(fh
, "<path ");
637 if (splay
) fprintf(fh
, "stroke=\"grey\" stroke-width=\"0.1px\" ");
638 fprintf(fh
, "d=\"M%.3f %.3f", p1
->x
* factor
, p1
->y
* -factor
);
640 fprintf(fh
, "L%.3f %.3f", p
->x
* factor
, p
->y
* -factor
);
645 SVG::label(const img_point
*p
, const wxString
& str
, int sflags
, int)
647 const char* s
= str
.utf8_str();
648 (void)sflags
; /* unused */
649 fprintf(fh
, "<text transform=\"translate(%.3f %.3f)\">",
650 p
->x
* factor
, p
->y
* -factor
);
652 fputs("</text>\n", fh
);
657 SVG::cross(const img_point
*p
, const wxString
& str
, int sflags
)
659 const char* s
= str
.utf8_str();
660 (void)sflags
; /* unused */
661 fprintf(fh
, "<circle id=\"%s\" cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\"/>\n",
662 s
, p
->x
* factor
, p
->y
* -factor
, marker_size
* SQRT_2
);
663 fprintf(fh
, "<path d=\"M%.3f %.3fL%.3f %.3fM%.3f %.3fL%.3f %.3f\"/>\n",
664 p
->x
* factor
- marker_size
, p
->y
* -factor
- marker_size
,
665 p
->x
* factor
+ marker_size
, p
->y
* -factor
+ marker_size
,
666 p
->x
* factor
+ marker_size
, p
->y
* -factor
- marker_size
,
667 p
->x
* factor
- marker_size
, p
->y
* -factor
+ marker_size
);
671 SVG::xsect(const img_point
*p
, double angle
, double d1
, double d2
)
673 double s
= sin(rad(angle
));
674 double c
= cos(rad(angle
));
675 fprintf(fh
, "<path d=\"M%.3f %.3fL%.3f %.3f\"/>\n",
676 (p
->x
+ s
* d1
) * factor
, (p
->y
+ c
* d1
) * -factor
,
677 (p
->x
- s
* d2
) * factor
, (p
->y
- c
* d2
) * -factor
);
681 SVG::wall(const img_point
*p
, double angle
, double d
)
684 fprintf(fh
, "<path d=\"M");
689 double s
= sin(rad(angle
));
690 double c
= cos(rad(angle
));
691 fprintf(fh
, "%.3f %.3f", (p
->x
+ s
* d
) * factor
, (p
->y
+ c
* d
) * -factor
);
695 SVG::passage(const img_point
*p
, double angle
, double d1
, double d2
)
697 double s
= sin(rad(angle
));
698 double c
= cos(rad(angle
));
699 double x1
= (p
->x
+ s
* d1
) * factor
;
700 double y1
= (p
->y
+ c
* d1
) * -factor
;
701 double x2
= (p
->x
- s
* d2
) * factor
;
702 double y2
= (p
->y
- c
* d2
) * -factor
;
705 fprintf(fh
, "L%.3f %.3fL%.3f %.3fZ\"/>\n", x2
, y2
, x1
, y1
);
707 snprintf(pending
, sizeof(pending
),
708 "<path d=\"M%.3f %.3fL%.3f %.3f", x1
, y1
, x2
, y2
);
729 fprintf(fh
, "</g>\n");
732 fprintf(fh
, "</g>\n</svg>\n");
735 class PLT
: public ExportFilter
{
738 const char * find_name_plt(const img_point
*p
);
740 double min_N
, max_N
, min_E
, max_E
, min_A
, max_A
;
742 unsigned anon_counter
= 0;
746 const int * passes() const override
;
747 void header(const char *, const char *, time_t,
748 double min_x
, double min_y
, double min_z
,
749 double max_x
, double max_y
, double max_z
) override
;
750 void line(const img_point
*, const img_point
*, unsigned, bool) override
;
751 void label(const img_point
*, const wxString
&, int, int) override
;
752 void footer() override
;
758 static const int plt_passes
[] = { LABELS
, LEGS
|SURF
, 0 };
763 PLT::header(const char *title
, const char *, time_t,
764 double min_x
, double min_y
, double min_z
,
765 double max_x
, double max_y
, double max_z
)
767 // FIXME: allow survey to be set from aven somehow!
768 const char *survey
= NULL
;
769 htab
= (point
**)osmalloc(HTAB_SIZE
* ossizeof(point
*));
770 for (size_t i
= 0; i
< HTAB_SIZE
; ++i
) htab
[i
] = NULL
;
771 /* Survex is E, N, Alt - PLT file is N, E, Alt */
772 min_N
= min_y
/ METRES_PER_FOOT
;
773 max_N
= max_y
/ METRES_PER_FOOT
;
774 min_E
= min_x
/ METRES_PER_FOOT
;
775 max_E
= max_x
/ METRES_PER_FOOT
;
776 min_A
= min_z
/ METRES_PER_FOOT
;
777 max_A
= max_z
/ METRES_PER_FOOT
;
778 fprintf(fh
, "Z %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
779 min_N
, max_N
, min_E
, max_E
, min_A
, max_A
);
780 fprintf(fh
, "N%s D 1 1 1 C%s\r\n", survey
? survey
: "X",
781 (title
&& title
[0]) ? title
: "X");
785 PLT::line(const img_point
*p1
, const img_point
*p
, unsigned flags
, bool fPendingMove
)
788 /* Survex is E, N, Alt - PLT file is N, E, Alt */
789 fprintf(fh
, "M %.3f %.3f %.3f ",
790 p1
->y
/ METRES_PER_FOOT
, p1
->x
/ METRES_PER_FOOT
, p1
->z
/ METRES_PER_FOOT
);
791 /* dummy passage dimensions are required to avoid compass bug */
792 fprintf(fh
, "S%s P -9 -9 -9 -9\r\n", find_name_plt(p1
));
794 /* Survex is E, N, Alt - PLT file is N, E, Alt */
795 fprintf(fh
, "D %.3f %.3f %.3f ",
796 p
->y
/ METRES_PER_FOOT
, p
->x
/ METRES_PER_FOOT
, p
->z
/ METRES_PER_FOOT
);
797 /* dummy passage dimensions are required to avoid compass bug */
798 fprintf(fh
, "S%s P -9 -9 -9 -9", find_name_plt(p
));
799 if (flags
& (MASK_
)) {
801 if (flags
& img_FLAG_DUPLICATE
) PUTC('L', fh
);
802 if (flags
& img_FLAG_SURFACE
) PUTC('P', fh
);
803 if (flags
& img_FLAG_SPLAY
) PUTC('S', fh
);
809 PLT::find_name_plt(const img_point
*p
)
811 const char * s
= find_name(p
);
814 // Anonymous station - number sequentially using a counter. We start
815 // the name with "%:" since we escape any % in a real station name
816 // below, but only insert % followed by two hex digits.
818 snprintf(buf
, sizeof(buf
), "%%:%u", ++anon_counter
);
820 return escaped
.c_str();
823 // PLT format can't handle spaces or control characters, so escape them
824 // like in URLs (an arbitrary choice of escaping, but at least a familiar
825 // one and % isn't likely to occur in station names).
827 for (q
= s
; *q
; ++q
) {
828 unsigned char ch
= *q
;
829 if (ch
<= ' ' || ch
== '%') {
830 escaped
.append(s
, q
- s
);
832 escaped
+= "0123456789abcdef"[ch
>> 4];
833 escaped
+= "0123456789abcdef"[ch
& 0x0f];
837 if (!escaped
.empty()) {
838 escaped
.append(s
, q
- s
);
839 return escaped
.c_str();
845 PLT::label(const img_point
*p
, const wxString
& str
, int sflags
, int)
847 const char* s
= str
.utf8_str();
848 (void)sflags
; /* unused */
855 /* Survex is E, N, Alt - PLT file is N, E, Alt */
856 fprintf(fh
, "X %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
857 min_N
, max_N
, min_E
, max_E
, min_A
, max_A
);
858 /* Yucky DOS "end of textfile" marker */
862 class EPS
: public ExportFilter
{
865 vector
<pair
<double, double>> psg
;
867 explicit EPS(double scale
)
868 : factor(POINTS_PER_MM
* 1000.0 / scale
) { }
869 const int * passes() const override
;
870 void header(const char *, const char *, time_t,
871 double min_x
, double min_y
, double min_z
,
872 double max_x
, double max_y
, double max_z
) override
;
873 void start_pass(int layer
) override
;
874 void line(const img_point
*, const img_point
*, unsigned, bool) override
;
875 void label(const img_point
*, const wxString
&, int, int) override
;
876 void cross(const img_point
*, const wxString
&, int) override
;
877 void xsect(const img_point
*, double, double, double) override
;
878 void wall(const img_point
*, double, double) override
;
879 void passage(const img_point
*, double, double, double) override
;
880 void tube_end() override
;
881 void footer() override
;
887 static const int eps_passes
[] = {
888 PASG
, XSECT
, WALL1
, WALL2
, LEGS
|SURF
|STNS
|LABELS
, 0
894 EPS::header(const char *title
, const char *, time_t,
895 double min_x
, double min_y
, double /*min_z*/,
896 double max_x
, double max_y
, double /*max_z*/)
898 const char * fontname_labels
= "helvetica"; // FIXME
899 int fontsize_labels
= 10; // FIXME
900 fputs("%!PS-Adobe-2.0 EPSF-1.2\n", fh
);
901 fputs("%%Creator: Survex " VERSION
" EPS Export Filter\n", fh
);
903 if (title
&& title
[0])
904 fprintf(fh
, "%%%%Title: %s\n", title
);
907 time_t now
= time(NULL
);
908 if (strftime(buf
, sizeof(buf
), "%Y-%m-%d %H:%M:%S %Z\n", localtime(&now
))) {
909 fputs("%%CreationDate: ", fh
);
915 name
= ::wxGetUserName().mb_str();
917 name
= ::wxGetUserId().mb_str();
921 fprintf(fh
, "%%%%For: %s\n", name
.c_str());
924 fprintf(fh
, "%%%%BoundingBox: %d %d %d %d\n",
925 int(floor(min_x
* factor
)), int(floor(min_y
* factor
)),
926 int(ceil(max_x
* factor
)), int(ceil(max_y
* factor
)));
927 fprintf(fh
, "%%%%HiResBoundingBox: %.4f %.4f %.4f %.4f\n",
928 min_x
* factor
, min_y
* factor
, max_x
* factor
, max_y
* factor
);
929 fputs("%%LanguageLevel: 1\n"
930 "%%PageOrder: Ascend\n"
932 "%%Orientation: Portrait\n", fh
);
934 fprintf(fh
, "%%%%DocumentFonts: %s\n", fontname_labels
);
936 fputs("%%EndComments\n"
938 "save countdictstack mark\n", fh
);
940 /* this code adapted from a2ps */
941 fputs("%%BeginResource: encoding ISO88591Encoding\n"
942 "/ISO88591Encoding [\n", fh
);
943 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
944 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
945 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
946 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
947 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
948 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
949 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
950 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
952 "/space /exclam /quotedbl /numbersign\n"
953 "/dollar /percent /ampersand /quoteright\n"
954 "/parenleft /parenright /asterisk /plus\n"
955 "/comma /minus /period /slash\n"
956 "/zero /one /two /three\n"
957 "/four /five /six /seven\n"
958 "/eight /nine /colon /semicolon\n"
959 "/less /equal /greater /question\n"
960 "/at /A /B /C /D /E /F /G\n"
961 "/H /I /J /K /L /M /N /O\n"
962 "/P /Q /R /S /T /U /V /W\n"
963 "/X /Y /Z /bracketleft\n"
964 "/backslash /bracketright /asciicircum /underscore\n"
965 "/quoteleft /a /b /c /d /e /f /g\n"
966 "/h /i /j /k /l /m /n /o\n"
967 "/p /q /r /s /t /u /v /w\n"
968 "/x /y /z /braceleft\n"
969 "/bar /braceright /asciitilde /.notdef\n", fh
);
970 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
971 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
972 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
973 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
974 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
975 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
976 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
977 fputs("/.notdef /.notdef /.notdef /.notdef\n", fh
);
979 "/space /exclamdown /cent /sterling\n"
980 "/currency /yen /brokenbar /section\n"
981 "/dieresis /copyright /ordfeminine /guillemotleft\n"
982 "/logicalnot /hyphen /registered /macron\n"
983 "/degree /plusminus /twosuperior /threesuperior\n"
984 "/acute /mu /paragraph /bullet\n"
985 "/cedilla /onesuperior /ordmasculine /guillemotright\n"
986 "/onequarter /onehalf /threequarters /questiondown\n"
987 "/Agrave /Aacute /Acircumflex /Atilde\n"
988 "/Adieresis /Aring /AE /Ccedilla\n"
989 "/Egrave /Eacute /Ecircumflex /Edieresis\n"
990 "/Igrave /Iacute /Icircumflex /Idieresis\n"
991 "/Eth /Ntilde /Ograve /Oacute\n"
992 "/Ocircumflex /Otilde /Odieresis /multiply\n"
993 "/Oslash /Ugrave /Uacute /Ucircumflex\n"
994 "/Udieresis /Yacute /Thorn /germandbls\n"
995 "/agrave /aacute /acircumflex /atilde\n"
996 "/adieresis /aring /ae /ccedilla\n"
997 "/egrave /eacute /ecircumflex /edieresis\n"
998 "/igrave /iacute /icircumflex /idieresis\n"
999 "/eth /ntilde /ograve /oacute\n"
1000 "/ocircumflex /otilde /odieresis /divide\n"
1001 "/oslash /ugrave /uacute /ucircumflex\n"
1002 "/udieresis /yacute /thorn /ydieresis\n"
1004 "%%EndResource\n", fh
);
1006 /* this code adapted from a2ps */
1008 "/reencode {\n" /* def */
1009 "dup length 5 add dict begin\n"
1012 "{ def }{ pop pop } ifelse\n"
1014 "/Encoding exch def\n"
1016 /* Use the font's bounding box to determine the ascent, descent,
1017 * and overall height; don't forget that these values have to be
1018 * transformed using the font's matrix.
1019 * We use `load' because sometimes BBox is executable, sometimes not.
1020 * Since we need 4 numbers and not an array avoid BBox from being executed
1022 "/FontBBox load aload pop\n"
1023 "FontMatrix transform /Ascent exch def pop\n"
1024 "FontMatrix transform /Descent exch def pop\n"
1025 "/FontHeight Ascent Descent sub def\n"
1027 /* Define these in case they're not in the FontInfo (also, here
1028 * they're easier to get to.
1030 "/UnderlinePosition 1 def\n"
1031 "/UnderlineThickness 1 def\n"
1033 /* Get the underline position and thickness if they're defined. */
1034 "currentdict /FontInfo known {\n"
1037 "dup /UnderlinePosition known {\n"
1038 "dup /UnderlinePosition get\n"
1039 "0 exch FontMatrix transform exch pop\n"
1040 "/UnderlinePosition exch def\n"
1043 "dup /UnderlineThickness known {\n"
1044 "/UnderlineThickness get\n"
1045 "0 exch FontMatrix transform exch pop\n"
1046 "/UnderlineThickness exch def\n"
1052 "} bind def\n", fh
);
1054 fprintf(fh
, "/lab ISO88591Encoding /%s findfont reencode definefont pop\n",
1057 fprintf(fh
, "/lab findfont %d scalefont setfont\n", int(fontsize_labels
));
1060 /* C<digit> changes colour */
1061 /* FIXME: read from ini */
1064 for (i
= 0; i
< sizeof(colour
) / sizeof(colour
[0]); ++i
) {
1065 fprintf(fh
, "/C%u {stroke %.3f %.3f %.3f setrgbcolor} def\n", i
,
1066 (double)(colour
[i
] & 0xff0000) / 0xff0000,
1067 (double)(colour
[i
] & 0xff00) / 0xff00,
1068 (double)(colour
[i
] & 0xff) / 0xff);
1074 /* Postscript definition for drawing a cross */
1075 fprintf(fh
, "/X {stroke moveto %.2f %.2f rmoveto %.2f %.2f rlineto "
1076 "%.2f 0 rmoveto %.2f %.2f rlineto %.2f %.2f rmoveto} def\n",
1077 -marker_size
, -marker_size
, marker_size
* 2, marker_size
* 2,
1078 -marker_size
* 2, marker_size
* 2, -marker_size
* 2,
1079 -marker_size
, marker_size
);
1081 /* define some functions to keep file short */
1082 fputs("/M {stroke moveto} def\n"
1083 "/P {stroke newpath moveto} def\n"
1084 "/F {closepath gsave 0.8 setgray fill grestore} def\n"
1086 "/R {rlineto} def\n"
1087 "/S {show} def\n", fh
);
1089 fprintf(fh
, "gsave %.8f dup scale\n", factor
);
1093 x
= floor(min_x
/ grid
) * grid
+ grid
;
1094 y
= floor(min_y
/ grid
) * grid
+ grid
;
1096 /* horizontal line */
1097 fprintf(fh
, "0\nLINE\n");
1098 fprintf(fh
, "8\nGrid\n"); /* Layer */
1099 fprintf(fh
, "10\n%6.2f\n", x
);
1100 fprintf(fh
, "20\n%6.2f\n", min_y
);
1101 fprintf(fh
, "30\n0\n");
1102 fprintf(fh
, "11\n%6.2f\n", x
);
1103 fprintf(fh
, "21\n%6.2f\n", max_y
);
1104 fprintf(fh
, "31\n0\n");
1109 fprintf(fh
, "0\nLINE\n");
1110 fprintf(fh
, "8\nGrid\n"); /* Layer */
1111 fprintf(fh
, "10\n%6.2f\n", min_x
);
1112 fprintf(fh
, "20\n%6.2f\n", y
);
1113 fprintf(fh
, "30\n0\n");
1114 fprintf(fh
, "11\n%6.2f\n", max_x
);
1115 fprintf(fh
, "21\n%6.2f\n", y
);
1116 fprintf(fh
, "31\n0\n");
1124 EPS::start_pass(int layer
)
1128 case LEGS
|SURF
|STNS
|LABELS
:
1129 fprintf(fh
, "0.1 setlinewidth\n");
1131 case PASG
: case XSECT
: case WALL1
: case WALL2
:
1132 fprintf(fh
, "0.01 setlinewidth\n");
1138 EPS::line(const img_point
*p1
, const img_point
*p
, unsigned flags
, bool fPendingMove
)
1140 (void)flags
; /* unused */
1142 fprintf(fh
, "%.2f %.2f M\n", p1
->x
, p1
->y
);
1144 fprintf(fh
, "%.2f %.2f L\n", p
->x
, p
->y
);
1148 EPS::label(const img_point
*p
, const wxString
& str
, int /*sflags*/, int)
1150 const char* s
= str
.utf8_str();
1151 fprintf(fh
, "%.2f %.2f M\n", p
->x
, p
->y
);
1154 unsigned char ch
= *s
++;
1156 case '(': case ')': case '\\': /* need to escape these characters */
1169 EPS::cross(const img_point
*p
, const wxString
&, int sflags
)
1171 (void)sflags
; /* unused */
1172 fprintf(fh
, "%.2f %.2f X\n", p
->x
, p
->y
);
1176 EPS::xsect(const img_point
*p
, double angle
, double d1
, double d2
)
1178 double s
= sin(rad(angle
));
1179 double c
= cos(rad(angle
));
1180 fprintf(fh
, "%.2f %.2f M %.2f %.2f R\n",
1181 p
->x
- s
* d2
, p
->y
- c
* d2
,
1182 s
* (d1
+ d2
), c
* (d1
+ d2
));
1186 EPS::wall(const img_point
*p
, double angle
, double d
)
1188 double s
= sin(rad(angle
));
1189 double c
= cos(rad(angle
));
1190 fprintf(fh
, "%.2f %.2f %c\n", p
->x
+ s
* d
, p
->y
+ c
* d
, first
? 'M' : 'L');
1195 EPS::passage(const img_point
*p
, double angle
, double d1
, double d2
)
1197 double s
= sin(rad(angle
));
1198 double c
= cos(rad(angle
));
1199 double x1
= p
->x
+ s
* d1
;
1200 double y1
= p
->y
+ c
* d1
;
1201 double x2
= p
->x
- s
* d2
;
1202 double y2
= p
->y
- c
* d2
;
1203 fprintf(fh
, "%.2f %.2f %c\n", x1
, y1
, first
? 'P' : 'L');
1205 psg
.push_back(make_pair(x2
, y2
));
1212 vector
<pair
<double, double>>::const_reverse_iterator i
;
1213 for (i
= psg
.rbegin(); i
!= psg
.rend(); ++i
) {
1214 fprintf(fh
, "%.2f %.2f L\n", i
->first
, i
->second
);
1224 fputs("stroke showpage grestore\n"
1226 "cleartomark countdictstack exch sub { end } repeat restore\n"
1230 class UseNumericCLocale
{
1231 char * current_locale
;
1234 UseNumericCLocale() {
1235 current_locale
= osstrdup(setlocale(LC_NUMERIC
, NULL
));
1236 setlocale(LC_NUMERIC
, "C");
1239 ~UseNumericCLocale() {
1240 setlocale(LC_NUMERIC
, current_locale
);
1241 osfree(current_locale
);
1246 transform_point(const Point
& pos
, const Vector3
* pre_offset
,
1247 double COS
, double SIN
, double COST
, double SINT
,
1250 double x
= pos
.GetX();
1251 double y
= pos
.GetY();
1252 double z
= pos
.GetZ();
1254 x
+= pre_offset
->GetX();
1255 y
+= pre_offset
->GetY();
1256 z
+= pre_offset
->GetZ();
1258 p
->x
= x
* COS
- y
* SIN
;
1259 double tmp
= x
* SIN
+ y
* COS
;
1260 p
->y
= z
* COST
- tmp
* SINT
;
1261 p
->z
= -(z
* SINT
+ tmp
* COST
);
1265 Export(const wxString
&fnm_out
, const wxString
&title
,
1266 const wxString
&datestamp
,
1268 const SurveyFilter
* filter
,
1269 double pan
, double tilt
, int show_mask
, export_format format
,
1270 double grid_
, double text_height
, double marker_size_
,
1273 UseNumericCLocale dummy
;
1274 int fPendingMove
= 0;
1277 double SIN
= sin(rad(pan
));
1278 double COS
= cos(rad(pan
));
1279 double SINT
= sin(rad(tilt
));
1280 double COST
= cos(rad(tilt
));
1282 grid
= (show_mask
& GRID
) ? grid_
: 0.0;
1283 marker_size
= marker_size_
;
1285 // Do we need to calculate min and max for each dimension?
1286 bool need_bounds
= true;
1287 ExportFilter
* filt
;
1290 filt
= new Export3D(model
.GetCSProj(), model
.GetSeparator());
1291 show_mask
|= FULL_COORDS
;
1292 need_bounds
= false;
1295 filt
= new POS(model
.GetSeparator(), true);
1296 show_mask
|= FULL_COORDS
;
1297 need_bounds
= false;
1300 filt
= new DXF(text_height
);
1303 filt
= new EPS(scale
);
1306 filt
= new GPX(model
.GetCSProj().c_str());
1307 show_mask
|= FULL_COORDS
;
1308 need_bounds
= false;
1311 filt
= new HPGL(scale
);
1312 // HPGL doesn't use the bounds itself, but they are needed to set
1313 // the origin to the centre of lower left.
1319 bool clamp_to_ground
= (show_mask
& CLAMP_TO_GROUND
);
1320 filt
= new KML(model
.GetCSProj().c_str(), clamp_to_ground
);
1321 show_mask
|= FULL_COORDS
;
1322 need_bounds
= false;
1327 show_mask
|= FULL_COORDS
;
1330 filt
= new POS(model
.GetSeparator(), false);
1331 show_mask
|= FULL_COORDS
;
1332 need_bounds
= false;
1335 filt
= new SVG(scale
, text_height
);
1338 filt
= new ShapefileLines(fnm_out
.utf8_str(),
1339 model
.GetCSProj().c_str());
1340 show_mask
|= FULL_COORDS
;
1341 need_bounds
= false;
1343 case FMT_SHP_POINTS
:
1344 filt
= new ShapefilePoints(fnm_out
.utf8_str(),
1345 model
.GetCSProj().c_str());
1346 show_mask
|= FULL_COORDS
;
1347 need_bounds
= false;
1353 if (!filt
->fopen(fnm_out
)) {
1358 const Vector3
* pre_offset
= NULL
;
1359 if (show_mask
& FULL_COORDS
) {
1360 pre_offset
= &(model
.GetOffset());
1363 /* Get bounding box */
1364 double min_x
, min_y
, min_z
, max_x
, max_y
, max_z
;
1365 min_x
= min_y
= min_z
= HUGE_VAL
;
1366 max_x
= max_y
= max_z
= -HUGE_VAL
;
1368 for (int f
= 0; f
!= 8; ++f
) {
1369 if ((show_mask
& (f
& img_FLAG_SURFACE
) ? SURF
: LEGS
) == 0) {
1370 // Not showing traverse because of surface/underground status.
1373 if ((f
& show_mask
& SPLAYS
) == 0) {
1374 // Not showing because it's a splay.
1377 list
<traverse
>::const_iterator trav
= model
.traverses_begin(f
, filter
);
1378 list
<traverse
>::const_iterator tend
= model
.traverses_end(f
);
1379 for ( ; trav
!= tend
; trav
= model
.traverses_next(f
, filter
, trav
)) {
1380 vector
<PointInfo
>::const_iterator pos
= trav
->begin();
1381 vector
<PointInfo
>::const_iterator end
= trav
->end();
1382 for ( ; pos
!= end
; ++pos
) {
1383 transform_point(*pos
, pre_offset
, COS
, SIN
, COST
, SINT
, &p
);
1385 if (p
.x
< min_x
) min_x
= p
.x
;
1386 if (p
.x
> max_x
) max_x
= p
.x
;
1387 if (p
.y
< min_y
) min_y
= p
.y
;
1388 if (p
.y
> max_y
) max_y
= p
.y
;
1389 if (p
.z
< min_z
) min_z
= p
.z
;
1390 if (p
.z
> max_z
) max_z
= p
.z
;
1394 list
<LabelInfo
*>::const_iterator pos
= model
.GetLabels();
1395 list
<LabelInfo
*>::const_iterator end
= model
.GetLabelsEnd();
1396 for ( ; pos
!= end
; ++pos
) {
1397 if (filter
&& !filter
->CheckVisible((*pos
)->GetText()))
1400 transform_point(**pos
, pre_offset
, COS
, SIN
, COST
, SINT
, &p
);
1402 if (p
.x
< min_x
) min_x
= p
.x
;
1403 if (p
.x
> max_x
) max_x
= p
.x
;
1404 if (p
.y
< min_y
) min_y
= p
.y
;
1405 if (p
.y
> max_y
) max_y
= p
.y
;
1406 if (p
.z
< min_z
) min_z
= p
.z
;
1407 if (p
.z
> max_z
) max_z
= p
.z
;
1418 /* Handle empty file and gracefully, and also zero for the !need_bounds
1420 if (min_x
> max_x
) {
1421 min_x
= min_y
= min_z
= 0;
1422 max_x
= max_y
= max_z
= 0;
1425 double x_offset
, y_offset
, z_offset
;
1426 if (show_mask
& FULL_COORDS
) {
1427 // Full coordinates - offset is applied before rotations.
1428 x_offset
= y_offset
= z_offset
= 0.0;
1429 } else if (show_mask
& CENTRED
) {
1431 x_offset
= (min_x
+ max_x
) * -0.5;
1432 y_offset
= (min_y
+ max_y
) * -0.5;
1433 z_offset
= (min_z
+ max_z
) * -0.5;
1435 // Origin at lowest SW corner.
1450 filt
->header(title
.utf8_str(), datestamp
.utf8_str(), model
.GetDateStamp(),
1451 min_x
, min_y
, min_z
, max_x
, max_y
, max_z
);
1453 p1
.x
= p1
.y
= p1
.z
= 0; /* avoid compiler warning */
1455 for (pass
= filt
->passes(); *pass
; ++pass
) {
1456 int pass_mask
= show_mask
& *pass
;
1459 filt
->start_pass(*pass
);
1460 int leg_mask
= pass_mask
& (LEGS
|SURF
);
1462 for (int f
= 0; f
!= 8; ++f
) {
1464 if (!(flags
& img_FLAG_SURFACE
)) flags
|= LEGS
;
1465 if ((leg_mask
& flags
) == 0) {
1466 // Not showing traverse because of surface/underground status.
1469 if ((flags
& SPLAYS
) && (show_mask
& SPLAYS
) == 0) {
1470 // Not showing because it's a splay.
1473 list
<traverse
>::const_iterator trav
= model
.traverses_begin(f
, filter
);
1474 list
<traverse
>::const_iterator tend
= model
.traverses_end(f
);
1475 for ( ; trav
!= tend
; trav
= model
.traverses_next(f
, filter
, trav
)) {
1476 assert(trav
->size() > 1);
1477 vector
<PointInfo
>::const_iterator pos
= trav
->begin();
1478 vector
<PointInfo
>::const_iterator end
= trav
->end();
1479 for ( ; pos
!= end
; ++pos
) {
1480 transform_point(*pos
, pre_offset
, COS
, SIN
, COST
, SINT
, &p
);
1485 if (pos
== trav
->begin()) {
1486 // First point is move...
1489 filt
->line(&p1
, &p
, flags
, fPendingMove
);
1497 if (pass_mask
& (STNS
|LABELS
|ENTS
|FIXES
|EXPORTS
)) {
1498 list
<LabelInfo
*>::const_iterator pos
= model
.GetLabels();
1499 list
<LabelInfo
*>::const_iterator end
= model
.GetLabelsEnd();
1500 for ( ; pos
!= end
; ++pos
) {
1501 if (filter
&& !filter
->CheckVisible((*pos
)->GetText()))
1504 transform_point(**pos
, pre_offset
, COS
, SIN
, COST
, SINT
, &p
);
1510 if ((pass_mask
& ENTS
) && (*pos
)->IsEntrance()) {
1512 } else if ((pass_mask
& FIXES
) && (*pos
)->IsFixedPt()) {
1514 } else if ((pass_mask
& EXPORTS
) && (*pos
)->IsExportedPt()) {
1516 } else if (pass_mask
& LABELS
) {
1519 int sflags
= (*pos
)->get_flags();
1521 filt
->label(&p
, (*pos
)->GetText(), sflags
, type
);
1523 if (pass_mask
& STNS
) {
1524 filt
->cross(&p
, (*pos
)->GetText(), sflags
);
1528 if (pass_mask
& (XSECT
|WALLS
|PASG
)) {
1529 bool elevation
= (tilt
== 0.0);
1530 list
<vector
<XSect
>>::const_iterator tube
= model
.tubes_begin();
1531 list
<vector
<XSect
>>::const_iterator tube_end
= model
.tubes_end();
1532 for ( ; tube
!= tube_end
; ++tube
) {
1533 vector
<XSect
>::const_iterator pos
= tube
->begin();
1534 vector
<XSect
>::const_iterator end
= tube
->end();
1535 size_t active_tube_len
= 0;
1536 for ( ; pos
!= end
; ++pos
) {
1537 const XSect
& xs
= *pos
;
1538 // FIXME: This filtering can create tubes containing a single
1539 // cross-section, which otherwise don't exist in aven (the
1540 // Model class currently filters them out). Perhaps we
1541 // should just always include these - a single set of LRUD
1542 // measurements is useful even if a single cross-section
1543 // 3D tube perhaps isn't.
1544 if (filter
&& !filter
->CheckVisible(xs
.GetLabel())) {
1545 // Close any active tube.
1546 if (active_tube_len
> 0) {
1547 active_tube_len
= 0;
1554 transform_point(xs
.GetPoint(), pre_offset
, COS
, SIN
, COST
, SINT
, &p
);
1560 if (pass_mask
& XSECT
)
1561 filt
->xsect(&p
, 90, xs
.GetU(), xs
.GetD());
1562 if (pass_mask
& WALL1
)
1563 filt
->wall(&p
, 90, xs
.GetU());
1564 if (pass_mask
& WALL2
)
1565 filt
->wall(&p
, 270, xs
.GetD());
1566 if (pass_mask
& PASG
)
1567 filt
->passage(&p
, 90, xs
.GetU(), xs
.GetD());
1569 // Should only be enabled in plan or elevation mode.
1570 double angle
= xs
.get_right_bearing() - pan
;
1571 if (pass_mask
& XSECT
)
1572 filt
->xsect(&p
, angle
+ 180, xs
.GetL(), xs
.GetR());
1573 if (pass_mask
& WALL1
)
1574 filt
->wall(&p
, angle
+ 180, xs
.GetL());
1575 if (pass_mask
& WALL2
)
1576 filt
->wall(&p
, angle
, xs
.GetR());
1577 if (pass_mask
& PASG
)
1578 filt
->passage(&p
, angle
+ 180, xs
.GetL(), xs
.GetR());
1581 if (active_tube_len
> 0) {