6 // Copyright (C) 2000-2002,2005,2006 Mark R. Shinwell
7 // Copyright (C) 2001-2003,2004,2005,2006,2010,2011,2012,2013,2014,2015,2016,2018 Olly Betts
8 // Copyright (C) 2005 Martin Green
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 #include "img_hosted.h"
38 const static int img2aven_tab
[] = {
45 flags
&= (sizeof(img2aven_tab
) / sizeof(img2aven_tab
[0]) - 1);
46 return img2aven_tab
[flags
];
49 int Model::Load(const wxString
& file
, const wxString
& prefix
)
51 // Load the processed survey data.
52 img
* survey
= img_read_stream_survey(wxFopen(file
, wxT("rb")),
57 return img_error2msg(img_error());
60 m_IsExtendedElevation
= survey
->is_extended_elevation
;
62 // Create a list of all the leg vertices, counting them and finding the
63 // extent of the survey at the same time.
68 m_HasUndergroundLegs
= false;
71 m_HasSurfaceLegs
= false;
72 m_HasErrorInformation
= false;
74 // FIXME: discard existing presentation? ask user about saving if we do!
76 // Delete any existing list entries.
79 double xmin
= DBL_MAX
;
80 double xmax
= -DBL_MAX
;
81 double ymin
= DBL_MAX
;
82 double ymax
= -DBL_MAX
;
83 double zmin
= DBL_MAX
;
84 double zmax
= -DBL_MAX
;
87 double depthmax
= -DBL_MAX
;
91 complete_dateinfo
= true;
93 for (unsigned f
= 0; f
!= sizeof(traverses
) / sizeof(traverses
[0]); ++f
) {
98 // Ultimately we probably want different types (subclasses perhaps?) for
99 // underground and surface data, so we don't need to store LRUD for surface
101 traverse
* current_traverse
= NULL
;
102 vector
<XSect
> * current_tube
= NULL
;
104 map
<wxString
, LabelInfo
*> labelmap
;
105 list
<LabelInfo
*>::const_iterator last_mapped_label
= m_Labels
.begin();
108 img_point prev_pt
= {0,0,0};
109 bool current_polyline_is_surface
= false;
110 int current_flags
= 0;
111 bool pending_move
= false;
112 // When legs within a traverse have different surface/splay/duplicate
113 // flags, we split it into contiguous traverses of each flag combination,
114 // but we need to track these so we can assign the error statistics to all
115 // of them. So we keep counts of how many of each combination we've
116 // generated for the current traverse.
117 size_t n_traverses
[8];
118 memset(n_traverses
, 0, sizeof(n_traverses
));
121 if (++items
% 200 == 0) {
122 long pos
= ftell(survey
->fh
);
123 int progress
= int((double(pos
) / double(file_size
)) * 100.0);
124 // SetProgress(progress);
129 result
= img_read_item(survey
, &pt
);
132 memset(n_traverses
, 0, sizeof(n_traverses
));
138 // Update survey extents.
139 if (pt
.x
< xmin
) xmin
= pt
.x
;
140 if (pt
.x
> xmax
) xmax
= pt
.x
;
141 if (pt
.y
< ymin
) ymin
= pt
.y
;
142 if (pt
.y
> ymax
) ymax
= pt
.y
;
143 if (pt
.z
< zmin
) zmin
= pt
.z
;
144 if (pt
.z
> zmax
) zmax
= pt
.z
;
146 int date
= survey
->days1
;
148 date
+= (survey
->days2
- date
) / 2;
149 if (date
< m_DateMin
) m_DateMin
= date
;
150 if (date
> datemax
) datemax
= date
;
152 complete_dateinfo
= false;
155 int flags
= survey
->flags
&
156 (img_FLAG_SURFACE
|img_FLAG_SPLAY
|img_FLAG_DUPLICATE
);
157 bool is_surface
= (flags
& img_FLAG_SURFACE
);
158 bool is_splay
= (flags
& img_FLAG_SPLAY
);
159 bool is_dupe
= (flags
& img_FLAG_DUPLICATE
);
162 if (pt
.z
< m_DepthMin
) m_DepthMin
= pt
.z
;
163 if (pt
.z
> depthmax
) depthmax
= pt
.z
;
170 current_flags
!= flags
) {
171 if (!current_polyline_is_surface
&& current_traverse
) {
172 //FixLRUD(*current_traverse);
175 ++n_traverses
[flags
];
176 // Start new traverse (surface or underground).
178 m_HasSurfaceLegs
= true;
180 m_HasUndergroundLegs
= true;
181 // The previous point was at a surface->ug transition.
182 if (current_polyline_is_surface
) {
183 if (prev_pt
.z
< m_DepthMin
) m_DepthMin
= prev_pt
.z
;
184 if (prev_pt
.z
> depthmax
) depthmax
= prev_pt
.z
;
187 traverses
[flags
].push_back(traverse());
188 current_traverse
= &traverses
[flags
].back();
189 current_traverse
->flags
= survey
->flags
;
191 current_polyline_is_surface
= is_surface
;
192 current_flags
= flags
;
195 // Update survey extents. We only need to do this if
196 // there's a pending move, since for a surface <->
197 // underground transition, we'll already have handled
199 if (prev_pt
.x
< xmin
) xmin
= prev_pt
.x
;
200 if (prev_pt
.x
> xmax
) xmax
= prev_pt
.x
;
201 if (prev_pt
.y
< ymin
) ymin
= prev_pt
.y
;
202 if (prev_pt
.y
> ymax
) ymax
= prev_pt
.y
;
203 if (prev_pt
.z
< zmin
) zmin
= prev_pt
.z
;
204 if (prev_pt
.z
> zmax
) zmax
= prev_pt
.z
;
207 current_traverse
->push_back(PointInfo(prev_pt
));
210 current_traverse
->push_back(PointInfo(pt
, date
));
213 pending_move
= false;
218 wxString
s(survey
->label
, wxConvUTF8
);
220 // If label isn't valid UTF-8 then this conversion will
221 // give an empty string. In this case, assume that the
222 // label is CP1252 (the Microsoft superset of ISO8859-1).
223 static wxCSConv
ConvCP1252(wxFONTENCODING_CP1252
);
224 s
= wxString(survey
->label
, ConvCP1252
);
226 // Or if that doesn't work (ConvCP1252 doesn't like
227 // strings with some bytes in) let's just go for
229 s
= wxString(survey
->label
, wxConvISO8859_1
);
232 int flags
= img2aven(survey
->flags
);
233 LabelInfo
* label
= new LabelInfo(pt
, s
, flags
);
234 if (label
->IsEntrance()) {
237 if (label
->IsFixedPt()) {
240 if (label
->IsExportedPt()) {
243 m_Labels
.push_back(label
);
249 // Start new current_tube.
250 tubes
.push_back(vector
<XSect
>());
251 current_tube
= &tubes
.back();
255 wxString
label(survey
->label
, wxConvUTF8
);
256 map
<wxString
, LabelInfo
*>::const_iterator p
;
257 p
= labelmap
.find(label
);
258 if (p
!= labelmap
.end()) {
261 // Initialise labelmap lazily - we may have no
263 list
<LabelInfo
*>::const_iterator i
;
264 if (labelmap
.empty()) {
265 i
= m_Labels
.begin();
267 i
= last_mapped_label
;
270 while (i
!= m_Labels
.end() && (*i
)->GetText() != label
) {
271 labelmap
[(*i
)->GetText()] = *i
;
274 last_mapped_label
= i
;
275 if (i
== m_Labels
.end()) {
276 // Unattached cross-section - ignore for now.
277 printf("unattached cross-section\n");
278 if (current_tube
->size() <= 1)
279 tubes
.resize(tubes
.size() - 1);
281 if (!m_Labels
.empty())
286 labelmap
[label
] = lab
;
289 int date
= survey
->days1
;
291 date
+= (survey
->days2
- date
) / 2;
292 if (date
< m_DateMin
) m_DateMin
= date
;
293 if (date
> datemax
) datemax
= date
;
296 current_tube
->push_back(XSect(*lab
, date
, survey
->l
, survey
->r
, survey
->u
, survey
->d
));
301 // Finish off current_tube.
302 // If there's only one cross-section in the tube, just
303 // discard it for now. FIXME: we should handle this
304 // when we come to skinning the tubes.
305 if (current_tube
&& current_tube
->size() <= 1)
306 tubes
.resize(tubes
.size() - 1);
310 case img_ERROR_INFO
: {
311 if (survey
->E
== 0.0) {
312 // Currently cavern doesn't spot all articulating traverses
313 // so we assume that any traverse with no error isn't part
314 // of a loop. FIXME: fix cavern!
317 m_HasErrorInformation
= true;
318 for (size_t f
= 0; f
!= sizeof(traverses
) / sizeof(traverses
[0]); ++f
) {
319 list
<traverse
>::reverse_iterator t
= traverses
[f
].rbegin();
320 size_t n
= n_traverses
[f
];
323 assert(t
!= traverses
[f
].rend());
324 t
->n_legs
= survey
->n_legs
;
325 t
->length
= survey
->length
;
339 // FIXME: Do we need to reset all these? - Olly
341 m_NumExportedPts
= 0;
343 m_HasUndergroundLegs
= false;
345 m_HasSurfaceLegs
= false;
349 return img_error2msg(img_error());
355 } while (result
!= img_STOP
);
357 if (!current_polyline_is_surface
&& current_traverse
) {
358 //FixLRUD(*current_traverse);
361 // Finish off current_tube.
362 // If there's only one cross-section in the tube, just
363 // discard it for now. FIXME: we should handle this
364 // when we come to skinning the tubes.
365 if (current_tube
&& current_tube
->size() <= 1)
366 tubes
.resize(tubes
.size() - 1);
368 m_separator
= survey
->separator
;
369 m_Title
= wxString(survey
->title
, wxConvUTF8
);
370 m_DateStamp_numeric
= survey
->datestamp_numeric
;
372 m_cs_proj
= wxString(survey
->cs
, wxConvUTF8
);
374 m_cs_proj
= wxString();
376 if (strcmp(survey
->datestamp
, "?") == 0) {
377 /* TRANSLATORS: used a processed survey with no processing date/time info */
378 m_DateStamp
= wmsg(/*Date and time not available.*/108);
379 } else if (survey
->datestamp
[0] == '@') {
380 const struct tm
* tm
= localtime(&m_DateStamp_numeric
);
382 /* TRANSLATORS: This is the date format string used to timestamp .3d
383 * files internally. Probably best to keep it the same for all
385 strftime(buf
, 256, msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107), tm
);
386 m_DateStamp
= wxString(buf
, wxConvUTF8
);
388 if (m_DateStamp
.empty()) {
389 m_DateStamp
= wxString(survey
->datestamp
, wxConvUTF8
);
393 // Check we've actually loaded some legs or stations!
394 if (!m_HasUndergroundLegs
&& !m_HasSurfaceLegs
&& m_Labels
.empty()) {
395 return (/*No survey data in 3d file ā%sā*/202);
398 if (traverses
[0].empty() &&
399 traverses
[1].empty() &&
400 traverses
[2].empty() &&
401 traverses
[3].empty() &&
402 traverses
[4].empty() &&
403 traverses
[5].empty() &&
404 traverses
[6].empty() &&
405 traverses
[7].empty()) {
406 // No legs, so get survey extents from stations
407 list
<LabelInfo
*>::const_iterator i
;
408 for (i
= m_Labels
.begin(); i
!= m_Labels
.end(); ++i
) {
409 if ((*i
)->GetX() < xmin
) xmin
= (*i
)->GetX();
410 if ((*i
)->GetX() > xmax
) xmax
= (*i
)->GetX();
411 if ((*i
)->GetY() < ymin
) ymin
= (*i
)->GetY();
412 if ((*i
)->GetY() > ymax
) ymax
= (*i
)->GetY();
413 if ((*i
)->GetZ() < zmin
) zmin
= (*i
)->GetZ();
414 if ((*i
)->GetZ() > zmax
) zmax
= (*i
)->GetZ();
418 m_Ext
.assign(xmax
- xmin
, ymax
- ymin
, zmax
- zmin
);
420 if (datemax
< m_DateMin
) m_DateMin
= datemax
;
421 m_DateExt
= datemax
- m_DateMin
;
423 // Centre the dataset around the origin.
424 CentreDataset(Vector3(xmin
, ymin
, zmin
));
426 if (depthmax
< m_DepthMin
) {
430 m_DepthExt
= depthmax
- m_DepthMin
;
431 m_DepthMin
-= GetOffset().GetZ();
435 printf("time to load = %.3f\n", (double)timer
.Time());
441 void Model::CentreDataset(const Vector3
& vmin
)
443 // Centre the dataset around the origin.
445 m_Offset
= vmin
+ (m_Ext
* 0.5);
447 for (unsigned f
= 0; f
!= sizeof(traverses
) / sizeof(traverses
[0]); ++f
) {
448 list
<traverse
>::iterator t
= traverses
[f
].begin();
449 while (t
!= traverses
[f
].end()) {
450 assert(t
->size() > 1);
451 vector
<PointInfo
>::iterator pos
= t
->begin();
452 while (pos
!= t
->end()) {
453 Point
& point
= *pos
++;
460 list
<vector
<XSect
> >::iterator i
= tubes
.begin();
461 while (i
!= tubes
.end()) {
462 assert(i
->size() > 1);
463 vector
<XSect
>::iterator pos
= i
->begin();
464 while (pos
!= i
->end()) {
465 Point
& point
= *pos
++;
471 list
<LabelInfo
*>::iterator lpos
= m_Labels
.begin();
472 while (lpos
!= m_Labels
.end()) {
473 Point
& point
= **lpos
++;