source: git/src/model.cc

walls-data
Last change on this file was 4c83f84, checked in by Olly Betts <olly@…>, 7 days ago

Don't check HAVE_CONFIG_H in most cases

This check is only useful for img.c, which is intended to be usable
outside of Survex (and had fallbacks for functions which may not be
available which will get used if built in a non-autotools project).
For all the other source files it's just useless boilerplate.

  • Property mode set to 100644
File size: 19.9 KB
RevLine 
[bec42b2]1//
2//  model.cc
3//
4//  Cave survey model.
5//
6//  Copyright (C) 2000-2002,2005,2006 Mark R. Shinwell
[1330b4d]7//  Copyright (C) 2001-2024 Olly Betts
[bec42b2]8//  Copyright (C) 2005 Martin Green
9//
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.
14//
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.
19//
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
23//
24
25#include <config.h>
26
27#include "model.h"
28
29#include "img_hosted.h"
[46a9883]30#include "useful.h"
[bec42b2]31
32#include <cfloat>
33#include <map>
34
35using namespace std;
36
37const static int img2aven_tab[] = {
38#include "img2aven.h"
39};
40
41inline int
42img2aven(int flags)
43{
44    flags &= (sizeof(img2aven_tab) / sizeof(img2aven_tab[0]) - 1);
45    return img2aven_tab[flags];
46}
47
48int Model::Load(const wxString& file, const wxString& prefix)
49{
50    // Load the processed survey data.
51    img* survey = img_read_stream_survey(wxFopen(file, wxT("rb")),
52                                         fclose,
53                                         file.c_str(),
54                                         prefix.utf8_str());
55    if (!survey) {
56        return img_error2msg(img_error());
57    }
58
59    m_IsExtendedElevation = survey->is_extended_elevation;
60
61    // Create a list of all the leg vertices, counting them and finding the
62    // extent of the survey at the same time.
63
64    m_NumFixedPts = 0;
65    m_NumExportedPts = 0;
66    m_NumEntrances = 0;
67    m_HasUndergroundLegs = false;
68    m_HasSplays = false;
69    m_HasDupes = false;
70    m_HasSurfaceLegs = false;
71    m_HasErrorInformation = false;
72
73    // FIXME: discard existing presentation? ask user about saving if we do!
74
75    // Delete any existing list entries.
76    m_Labels.clear();
77
78    double xmin = DBL_MAX;
79    double xmax = -DBL_MAX;
80    double ymin = DBL_MAX;
81    double ymax = -DBL_MAX;
82    double zmin = DBL_MAX;
83    double zmax = -DBL_MAX;
84
85    m_DepthMin = DBL_MAX;
86    double depthmax = -DBL_MAX;
87
88    m_DateMin = INT_MAX;
89    int datemax = -1;
90    complete_dateinfo = true;
91
92    for (unsigned f = 0; f != sizeof(traverses) / sizeof(traverses[0]); ++f) {
93        traverses[f].clear();
94    }
95    tubes.clear();
96
97    // Ultimately we probably want different types (subclasses perhaps?) for
98    // underground and surface data, so we don't need to store LRUD for surface
99    // stuff.
100    traverse * current_traverse = NULL;
101    vector<XSect> * current_tube = NULL;
102
103    map<wxString, LabelInfo *> labelmap;
104    list<LabelInfo*>::const_iterator last_mapped_label = m_Labels.begin();
105
106    int result;
107    img_point prev_pt = {0,0,0};
108    bool current_polyline_is_surface = false;
109    int current_flags = 0;
[36f0d86]110    int current_style = 0;
[49c58872]111    string current_label;
[bec42b2]112    bool pending_move = false;
113    // When legs within a traverse have different surface/splay/duplicate
114    // flags, we split it into contiguous traverses of each flag combination,
115    // but we need to track these so we can assign the error statistics to all
116    // of them.  So we keep counts of how many of each combination we've
117    // generated for the current traverse.
118    size_t n_traverses[8];
119    memset(n_traverses, 0, sizeof(n_traverses));
120    do {
121#if 0
122        if (++items % 200 == 0) {
123            long pos = ftell(survey->fh);
124            int progress = int((double(pos) / double(file_size)) * 100.0);
125            // SetProgress(progress);
126        }
127#endif
128
129        img_point pt;
130        result = img_read_item(survey, &pt);
131        switch (result) {
132            case img_MOVE:
133                memset(n_traverses, 0, sizeof(n_traverses));
134                pending_move = true;
135                prev_pt = pt;
136                break;
137
138            case img_LINE: {
139                // Update survey extents.
140                if (pt.x < xmin) xmin = pt.x;
141                if (pt.x > xmax) xmax = pt.x;
142                if (pt.y < ymin) ymin = pt.y;
143                if (pt.y > ymax) ymax = pt.y;
144                if (pt.z < zmin) zmin = pt.z;
145                if (pt.z > zmax) zmax = pt.z;
146
147                int date = survey->days1;
148                if (date != -1) {
149                    date += (survey->days2 - date) / 2;
150                    if (date < m_DateMin) m_DateMin = date;
151                    if (date > datemax) datemax = date;
152                } else {
153                    complete_dateinfo = false;
154                }
155
156                int flags = survey->flags &
157                    (img_FLAG_SURFACE|img_FLAG_SPLAY|img_FLAG_DUPLICATE);
158                bool is_surface = (flags & img_FLAG_SURFACE);
159                bool is_splay = (flags & img_FLAG_SPLAY);
160                bool is_dupe = (flags & img_FLAG_DUPLICATE);
161
162                if (!is_surface) {
163                    if (pt.z < m_DepthMin) m_DepthMin = pt.z;
164                    if (pt.z > depthmax) depthmax = pt.z;
165                }
166                if (is_splay)
167                    m_HasSplays = true;
168                if (is_dupe)
169                    m_HasDupes = true;
170                if (pending_move ||
[49c58872]171                    current_flags != flags ||
[36f0d86]172                    current_label != survey->label ||
173                    current_style != survey->style) {
[bec42b2]174                    if (!current_polyline_is_surface && current_traverse) {
175                        //FixLRUD(*current_traverse);
176                    }
177
178                    ++n_traverses[flags];
179                    // Start new traverse (surface or underground).
180                    if (is_surface) {
181                        m_HasSurfaceLegs = true;
182                    } else {
183                        m_HasUndergroundLegs = true;
184                        // The previous point was at a surface->ug transition.
185                        if (current_polyline_is_surface) {
186                            if (prev_pt.z < m_DepthMin) m_DepthMin = prev_pt.z;
187                            if (prev_pt.z > depthmax) depthmax = prev_pt.z;
188                        }
189                    }
[49c58872]190                    traverses[flags].push_back(traverse(survey->label));
[bec42b2]191                    current_traverse = &traverses[flags].back();
192                    current_traverse->flags = survey->flags;
[36f0d86]193                    current_traverse->style = survey->style;
[bec42b2]194
195                    current_polyline_is_surface = is_surface;
196                    current_flags = flags;
[49c58872]197                    current_label = survey->label;
[36f0d86]198                    current_style = survey->style;
[bec42b2]199
200                    if (pending_move) {
201                        // Update survey extents.  We only need to do this if
202                        // there's a pending move, since for a surface <->
203                        // underground transition, we'll already have handled
204                        // this point.
205                        if (prev_pt.x < xmin) xmin = prev_pt.x;
206                        if (prev_pt.x > xmax) xmax = prev_pt.x;
207                        if (prev_pt.y < ymin) ymin = prev_pt.y;
208                        if (prev_pt.y > ymax) ymax = prev_pt.y;
209                        if (prev_pt.z < zmin) zmin = prev_pt.z;
210                        if (prev_pt.z > zmax) zmax = prev_pt.z;
211                    }
212
213                    current_traverse->push_back(PointInfo(prev_pt));
214                }
215
216                current_traverse->push_back(PointInfo(pt, date));
217
218                prev_pt = pt;
219                pending_move = false;
220                break;
221            }
222
223            case img_LABEL: {
224                wxString s(survey->label, wxConvUTF8);
225                if (s.empty()) {
226                    // If label isn't valid UTF-8 then this conversion will
227                    // give an empty string.  In this case, assume that the
228                    // label is CP1252 (the Microsoft superset of ISO8859-1).
229                    static wxCSConv ConvCP1252(wxFONTENCODING_CP1252);
230                    s = wxString(survey->label, ConvCP1252);
231                    if (s.empty()) {
232                        // Or if that doesn't work (ConvCP1252 doesn't like
233                        // strings with some bytes in) let's just go for
234                        // ISO8859-1.
235                        s = wxString(survey->label, wxConvISO8859_1);
236                    }
237                }
238                int flags = img2aven(survey->flags);
239                LabelInfo* label = new LabelInfo(pt, s, flags);
240                if (label->IsEntrance()) {
241                    m_NumEntrances++;
242                }
243                if (label->IsFixedPt()) {
244                    m_NumFixedPts++;
245                }
246                if (label->IsExportedPt()) {
247                    m_NumExportedPts++;
248                }
249                m_Labels.push_back(label);
250                break;
251            }
252
253            case img_XSECT: {
254                if (!current_tube) {
255                    // Start new current_tube.
256                    tubes.push_back(vector<XSect>());
257                    current_tube = &tubes.back();
258                }
259
260                LabelInfo * lab;
261                wxString label(survey->label, wxConvUTF8);
262                map<wxString, LabelInfo *>::const_iterator p;
263                p = labelmap.find(label);
264                if (p != labelmap.end()) {
265                    lab = p->second;
266                } else {
267                    // Initialise labelmap lazily - we may have no
268                    // cross-sections.
269                    list<LabelInfo*>::const_iterator i;
270                    if (labelmap.empty()) {
271                        i = m_Labels.begin();
272                    } else {
273                        i = last_mapped_label;
274                        ++i;
275                    }
276                    while (i != m_Labels.end() && (*i)->GetText() != label) {
277                        labelmap[(*i)->GetText()] = *i;
278                        ++i;
279                    }
280                    last_mapped_label = i;
281                    if (i == m_Labels.end()) {
282                        // Unattached cross-section - ignore for now.
283                        printf("unattached cross-section\n");
284                        if (current_tube->size() <= 1)
285                            tubes.resize(tubes.size() - 1);
286                        current_tube = NULL;
287                        if (!m_Labels.empty())
288                            --last_mapped_label;
289                        break;
290                    }
291                    lab = *i;
292                    labelmap[label] = lab;
293                }
294
295                int date = survey->days1;
296                if (date != -1) {
297                    date += (survey->days2 - date) / 2;
298                    if (date < m_DateMin) m_DateMin = date;
299                    if (date > datemax) datemax = date;
300                }
301
[672459c]302                current_tube->emplace_back(lab, date, survey->l, survey->r, survey->u, survey->d);
[bec42b2]303                break;
304            }
305
306            case img_XSECT_END:
307                // Finish off current_tube.
308                // If there's only one cross-section in the tube, just
309                // discard it for now.  FIXME: we should handle this
310                // when we come to skinning the tubes.
311                if (current_tube && current_tube->size() <= 1)
312                    tubes.resize(tubes.size() - 1);
313                current_tube = NULL;
314                break;
315
316            case img_ERROR_INFO: {
317                if (survey->E == 0.0) {
318                    // Currently cavern doesn't spot all articulating traverses
319                    // so we assume that any traverse with no error isn't part
320                    // of a loop.  FIXME: fix cavern!
321                    break;
322                }
323                m_HasErrorInformation = true;
324                for (size_t f = 0; f != sizeof(traverses) / sizeof(traverses[0]); ++f) {
325                    list<traverse>::reverse_iterator t = traverses[f].rbegin();
326                    size_t n = n_traverses[f];
327                    n_traverses[f] = 0;
328                    while (n) {
329                        assert(t != traverses[f].rend());
330                        t->n_legs = survey->n_legs;
331                        t->length = survey->length;
[fc43dda]332                        t->errors[traverse::ERROR_3D] = survey->E;
333                        t->errors[traverse::ERROR_H] = survey->H;
334                        t->errors[traverse::ERROR_V] = survey->V;
[bec42b2]335                        --n;
336                        ++t;
337                    }
338                }
339                break;
340            }
341
342            case img_BAD: {
343                m_Labels.clear();
344
345                // FIXME: Do we need to reset all these? - Olly
346                m_NumFixedPts = 0;
347                m_NumExportedPts = 0;
348                m_NumEntrances = 0;
349                m_HasUndergroundLegs = false;
350                m_HasSplays = false;
351                m_HasSurfaceLegs = false;
352
353                img_close(survey);
354
355                return img_error2msg(img_error());
356            }
357
358            default:
359                break;
360        }
361    } while (result != img_STOP);
362
363    if (!current_polyline_is_surface && current_traverse) {
364        //FixLRUD(*current_traverse);
365    }
366
367    // Finish off current_tube.
368    // If there's only one cross-section in the tube, just
369    // discard it for now.  FIXME: we should handle this
370    // when we come to skinning the tubes.
371    if (current_tube && current_tube->size() <= 1)
372        tubes.resize(tubes.size() - 1);
373
374    m_separator = survey->separator;
375    m_Title = wxString(survey->title, wxConvUTF8);
376    m_DateStamp_numeric = survey->datestamp_numeric;
377    if (survey->cs) {
378        m_cs_proj = wxString(survey->cs, wxConvUTF8);
379    } else {
380        m_cs_proj = wxString();
381    }
382    if (strcmp(survey->datestamp, "?") == 0) {
383        /* TRANSLATORS: used a processed survey with no processing date/time info */
384        m_DateStamp = wmsg(/*Date and time not available.*/108);
385    } else if (survey->datestamp[0] == '@') {
386        const struct tm * tm = localtime(&m_DateStamp_numeric);
387        char buf[256];
388        /* TRANSLATORS: This is the date format string used to timestamp .3d
389         * files internally.  Probably best to keep it the same for all
390         * translations. */
391        strftime(buf, 256, msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107), tm);
392        m_DateStamp = wxString(buf, wxConvUTF8);
393    }
394    if (m_DateStamp.empty()) {
395        m_DateStamp = wxString(survey->datestamp, wxConvUTF8);
396    }
397    img_close(survey);
398
399    // Check we've actually loaded some legs or stations!
400    if (!m_HasUndergroundLegs && !m_HasSurfaceLegs && m_Labels.empty()) {
401        return (/*No survey data in 3d file “%s”*/202);
402    }
403
404    if (traverses[0].empty() &&
405        traverses[1].empty() &&
406        traverses[2].empty() &&
407        traverses[3].empty() &&
408        traverses[4].empty() &&
409        traverses[5].empty() &&
410        traverses[6].empty() &&
411        traverses[7].empty()) {
412        // No legs, so get survey extents from stations
413        list<LabelInfo*>::const_iterator i;
414        for (i = m_Labels.begin(); i != m_Labels.end(); ++i) {
415            if ((*i)->GetX() < xmin) xmin = (*i)->GetX();
416            if ((*i)->GetX() > xmax) xmax = (*i)->GetX();
417            if ((*i)->GetY() < ymin) ymin = (*i)->GetY();
418            if ((*i)->GetY() > ymax) ymax = (*i)->GetY();
419            if ((*i)->GetZ() < zmin) zmin = (*i)->GetZ();
420            if ((*i)->GetZ() > zmax) zmax = (*i)->GetZ();
421        }
422    }
423
424    m_Ext.assign(xmax - xmin, ymax - ymin, zmax - zmin);
425
426    if (datemax < m_DateMin) m_DateMin = datemax;
427    m_DateExt = datemax - m_DateMin;
428
429    // Centre the dataset around the origin.
430    CentreDataset(Vector3(xmin, ymin, zmin));
431
432    if (depthmax < m_DepthMin) {
433        m_DepthMin = 0;
434        m_DepthExt = 0;
435    } else {
436        m_DepthExt = depthmax - m_DepthMin;
437        m_DepthMin -= GetOffset().GetZ();
438    }
439
440#if 0
441    printf("time to load = %.3f\n", (double)timer.Time());
442#endif
443
444    return 0; // OK
445}
446
447void Model::CentreDataset(const Vector3& vmin)
448{
449    // Centre the dataset around the origin.
450
451    m_Offset = vmin + (m_Ext * 0.5);
452
453    for (unsigned f = 0; f != sizeof(traverses) / sizeof(traverses[0]); ++f) {
454        list<traverse>::iterator t = traverses[f].begin();
455        while (t != traverses[f].end()) {
456            assert(t->size() > 1);
457            vector<PointInfo>::iterator pos = t->begin();
458            while (pos != t->end()) {
459                Point & point = *pos++;
460                point -= m_Offset;
461            }
462            ++t;
463        }
464    }
465
466    list<LabelInfo*>::iterator lpos = m_Labels.begin();
467    while (lpos != m_Labels.end()) {
468        Point & point = **lpos++;
469        point -= m_Offset;
470    }
471}
[1a46879]472
[46a9883]473void
474Model::do_prepare_tubes() const
475{
476    // Fill in "right_bearing" for each cross-section.
477    for (auto&& tube : tubes) {
478        assert(tube.size() > 1);
479        Vector3 U[4];
480        XSect* prev_pt_v = NULL;
481        Vector3 last_right(1.0, 0.0, 0.0);
482
483        vector<XSect>::iterator i = tube.begin();
484        vector<XSect>::size_type segment = 0;
485        while (i != tube.end()) {
486            // get the coordinates of this vertex
487            XSect & pt_v = *i++;
488
489            bool cover_end = false;
490
491            Vector3 right, up;
492
493            const Vector3 up_v(0.0, 0.0, 1.0);
494
495            if (segment == 0) {
496                assert(i != tube.end());
497                // first segment
498
499                // get the coordinates of the next vertex
500                const XSect & next_pt_v = *i;
501
502                // calculate vector from this pt to the next one
503                Vector3 leg_v = next_pt_v - pt_v;
504
505                // obtain a vector in the LRUD plane
506                right = leg_v * up_v;
507                if (right.magnitude() == 0) {
508                    right = last_right;
509                    // Obtain a second vector in the LRUD plane,
510                    // perpendicular to the first.
511                    //up = right * leg_v;
512                    up = up_v;
513                } else {
514                    last_right = right;
515                    up = up_v;
516                }
517
518                cover_end = true;
519            } else if (segment + 1 == tube.size()) {
520                // last segment
521
522                // Calculate vector from the previous pt to this one.
523                Vector3 leg_v = pt_v - *prev_pt_v;
524
525                // Obtain a horizontal vector in the LRUD plane.
526                right = leg_v * up_v;
527                if (right.magnitude() == 0) {
528                    right = Vector3(last_right.GetX(), last_right.GetY(), 0.0);
529                    // Obtain a second vector in the LRUD plane,
530                    // perpendicular to the first.
531                    //up = right * leg_v;
532                    up = up_v;
533                } else {
534                    last_right = right;
535                    up = up_v;
536                }
537
538                cover_end = true;
539            } else {
540                assert(i != tube.end());
541                // Intermediate segment.
542
543                // Get the coordinates of the next vertex.
544                const XSect & next_pt_v = *i;
545
546                // Calculate vectors from this vertex to the
547                // next vertex, and from the previous vertex to
548                // this one.
549                Vector3 leg1_v = pt_v - *prev_pt_v;
550                Vector3 leg2_v = next_pt_v - pt_v;
551
552                // Obtain horizontal vectors perpendicular to
553                // both legs, then normalise and average to get
554                // a horizontal bisector.
555                Vector3 r1 = leg1_v * up_v;
556                Vector3 r2 = leg2_v * up_v;
557                r1.normalise();
558                r2.normalise();
559                right = r1 + r2;
560                if (right.magnitude() == 0) {
561                    // This is the "mid-pitch" case...
562                    right = last_right;
563                }
564                if (r1.magnitude() == 0) {
565                    up = up_v;
566
567                    // Rotate pitch section to minimise the
[cbe7dde]568                    // "torsional stress" - FIXME: use
[46a9883]569                    // triangles instead of rectangles?
570                    int shift = 0;
571                    double maxdotp = 0;
572
573                    // Scale to unit vectors in the LRUD plane.
574                    right.normalise();
575                    up.normalise();
576                    Vector3 vec = up - right;
577                    for (int orient = 0; orient <= 3; ++orient) {
578                        Vector3 tmp = U[orient] - prev_pt_v->GetPoint();
579                        tmp.normalise();
580                        double dotp = dot(vec, tmp);
581                        if (dotp > maxdotp) {
582                            maxdotp = dotp;
583                            shift = orient;
584                        }
585                    }
586                    if (shift) {
587                        if (shift != 2) {
588                            Vector3 temp(U[0]);
589                            U[0] = U[shift];
590                            U[shift] = U[2];
591                            U[2] = U[shift ^ 2];
592                            U[shift ^ 2] = temp;
593                        } else {
594                            swap(U[0], U[2]);
595                            swap(U[1], U[3]);
596                        }
597                    }
598#if 0
599                    // Check that the above code actually permuted
600                    // the vertices correctly.
601                    shift = 0;
602                    maxdotp = 0;
603                    for (int j = 0; j <= 3; ++j) {
604                        Vector3 tmp = U[j] - *prev_pt_v;
605                        tmp.normalise();
606                        double dotp = dot(vec, tmp);
607                        if (dotp > maxdotp) {
608                            maxdotp = dotp + 1e-6; // Add small tolerance to stop 45 degree offset cases being flagged...
609                            shift = j;
610                        }
611                    }
612                    if (shift) {
613                        printf("New shift = %d!\n", shift);
614                        shift = 0;
615                        maxdotp = 0;
616                        for (int j = 0; j <= 3; ++j) {
617                            Vector3 tmp = U[j] - *prev_pt_v;
618                            tmp.normalise();
619                            double dotp = dot(vec, tmp);
620                            printf("    %d : %.8f\n", j, dotp);
621                        }
622                    }
623#endif
624                } else {
625                    up = up_v;
626                }
627                last_right = right;
628            }
629
630            // Scale to unit vectors in the LRUD plane.
631            right.normalise();
632            up.normalise();
633
634            double l = fabs(pt_v.GetL());
635            double r = fabs(pt_v.GetR());
636            double u = fabs(pt_v.GetU());
637            double d = fabs(pt_v.GetD());
638
639            // Produce coordinates of the corners of the LRUD "plane".
640            Vector3 v[4];
641            v[0] = pt_v.GetPoint() - right * l + up * u;
642            v[1] = pt_v.GetPoint() + right * r + up * u;
643            v[2] = pt_v.GetPoint() + right * r - up * d;
644            v[3] = pt_v.GetPoint() - right * l - up * d;
645
646            prev_pt_v = &pt_v;
647            U[0] = v[0];
648            U[1] = v[1];
649            U[2] = v[2];
650            U[3] = v[3];
651
[64fba42]652            // FIXME: Store rather than recomputing on each draw?
653            (void)cover_end;
654
[76cf7f1]655            pt_v.set_right_bearing(deg(atan2(right.GetX(), right.GetY())));
[46a9883]656
657            ++segment;
658        }
659    }
660}
661
[09ad6d7]662void
663SurveyFilter::add(const wxString& name)
664{
665    auto it = filters.lower_bound(name);
666    if (it != filters.end()) {
667        // It's invalid to add a survey which is already present.
668        assert(*it != name);
669        // Check if a survey prefixing name is visible.
670        if (name.StartsWith(*it) && name[it->size()] == separator) {
671            redundant_filters.insert(name);
672            return;
673        }
674    }
675    while (it != filters.begin()) {
676        --it;
677        const wxString& s = *it;
678        if (s.size() <= name.size()) break;
679        if (s.StartsWith(name) && s[name.size()] == separator) {
680            redundant_filters.insert(s);
681            it = filters.erase(it);
682        }
683    }
684    filters.insert(name);
685}
686
687void
688SurveyFilter::remove(const wxString& name)
689{
690    if (filters.erase(name) == 0) {
691        redundant_filters.erase(name);
692        return;
693    }
694    if (redundant_filters.empty()) {
695        return;
696    }
697    auto it = redundant_filters.upper_bound(name);
698    while (it != redundant_filters.begin()) {
699        --it;
700        // Check if a survey prefixed by name should be made visible.
701        const wxString& s = *it;
702        if (s.size() <= name.size()) {
703            break;
704        }
705        if (!(s.StartsWith(name) && s[name.size()] == separator))
706            break;
707        filters.insert(s);
708        it = redundant_filters.erase(it);
709    }
710}
711
[16193ae]712void
713SurveyFilter::SetSeparator(wxChar separator_)
714{
715    if (separator_ == separator) return;
716
717    separator = separator_;
718
719    if (filters.empty()) {
720        return;
721    }
722
723    // Move aside all the filters already set and re-add() them so they get
724    // split into redundant_filters appropriately.
725    std::set<wxString, std::greater<wxString>> old_filters;
726    std::set<wxString, std::greater<wxString>> old_redundant_filters;
727    swap(filters, old_filters);
728    swap(redundant_filters, old_redundant_filters);
[1330b4d]729    for (auto& s : old_filters) {
[16193ae]730        add(s);
731    }
[1330b4d]732    for (auto& s : old_redundant_filters) {
[16193ae]733        add(s);
734    }
735}
736
[1a46879]737bool
738SurveyFilter::CheckVisible(const wxString& name) const
739{
740    auto it = filters.lower_bound(name);
741    if (it == filters.end()) {
742        // There's no filter <= name so name is excluded.
743        return false;
744    }
745    if (*it == name) {
746        // Exact match.
747        return true;
748    }
749    // Check if a survey prefixing name is visible.
750    if (name.StartsWith(*it) && name[it->size()] == separator)
751        return true;
752    return false;
753}
Note: See TracBrowser for help on using the repository browser.