aboutsummaryrefslogtreecommitdiff
path: root/src/labeller.cc
blob: 64da2d75c29955c17d7748aeeb03f81a2535a06f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include "labeller.h"
#include "settings.h"
#include <fstream>


namespace backend {
    struct labeller_impl {
        std::vector<std::string> labels;
        std::vector<label> annotations;
        // Bool in undo/redo buffers is whether was applied or deleted
        std::vector<std::pair<bool, label>> undoBuffer;
        std::vector<std::pair<bool, label>> redoBuffer;
        std::filesystem::path savepath;
    };

    labeller::labeller(const std::filesystem::path& savepath) {
        data = std::shared_ptr<labeller_impl>(new labeller_impl);
        data->labels = settings::getLabels();
        data->savepath = savepath;
    }

    std::pair<label, label> labeller::getSurrounding(double time) const {
        std::pair<label, label> labs;
        if(!data->annotations.empty()) {
            auto above = data->annotations.rbegin();
            auto below = above;
            auto rit = above;
            while(++rit != data->annotations.rend()) {
                if(rit->time > time && (above->time <= time || rit->time < above->time)) above = rit;
                if(rit->time <= time && (below->time > time || rit->time > below->time)) below = rit;
            }
            if(below->time <= time) labs.first = *below;
            if(above->time > time) labs.second = *above;
        }
        return labs;
    }

    std::vector<std::string> labeller::getLabels() const {
        return data->labels;
    }

    void appLab(std::vector<label>& annotations, label lab) {
        annotations.push_back(lab);
    }

    void labeller::applyLabel(std::string name, double time) {
        label lab(name, time);
        appLab(data->annotations, lab);
        data->undoBuffer.push_back({true, lab});
        data->redoBuffer.clear();
    }

    double abs(double x) {
        if(x < 0) x *= -1;
        return x;
    }

    label delLab(std::vector<label>& annotations, double time) {
        if(annotations.empty()) {
            return label();
        }
        auto closest = annotations.rbegin();
        auto rit = closest;
        while(++rit != annotations.rend()) {
            if(abs(rit->time - time) < abs(closest->time - time)) {
                closest = rit;
            }
        }
        label c(*closest);
        annotations.erase(closest.base());
        return c;
    }

    void labeller::deleteLabel(double time) {
        label deleted = delLab(data->annotations, time);
        data->undoBuffer.push_back({false, deleted});
        data->redoBuffer.clear();
    }

    // Pops action from 'from', applies, and appends to 'to'.
    void handleUndoRedo(std::vector<std::pair<bool, label>>& from, std::vector<std::pair<bool, label>>& to, std::vector<label>& annotations) {
        if(! from.empty()) {
            auto elem = from.back();
            from.pop_back();
            if(elem.first) { // It was applied, so we must unapply
                delLab(annotations, elem.second.time);
            } else {
                appLab(annotations, elem.second);
            }
            to.push_back({! elem.first, elem.second});
        }
    }

    void labeller::undo() {
        handleUndoRedo(data->undoBuffer, data->redoBuffer, data->annotations);
    }

    void labeller::redo() {
        handleUndoRedo(data->redoBuffer, data->undoBuffer, data->annotations);
    }

    bool compareLabels(label l1, label l2) {
        return (l1.time < l2.time);
    }

    void labeller::save() const {
        std::ofstream out;
        out.open(data->savepath);
        std::vector<label> a(data->annotations);
        std::sort(a.begin(), a.end(), compareLabels);
        for(label l : a) {
            out << l.name << "," << l.time << std::endl;
        }
        out.close();
    }
}