• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 17270491352

27 Aug 2025 02:57PM UTC coverage: 65.333%. Remained the same
17270491352

push

github

paulmthompson
Merge branch 'main' of https://github.com/paulmthompson/WhiskerToolbox

352 of 628 new or added lines in 92 files covered. (56.05%)

357 existing lines in 24 files now uncovered.

26429 of 40453 relevant lines covered (65.33%)

1119.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.61
/src/DataManager/utils/TableView/computers/LineSamplingMultiComputer.h
1
#ifndef LINE_SAMPLING_MULTI_COMPUTER_H
2
#define LINE_SAMPLING_MULTI_COMPUTER_H
3

4
#include "utils/TableView/interfaces/IMultiColumnComputer.h"
5
#include "utils/TableView/interfaces/ILineSource.h"
6
#include "CoreGeometry/line_geometry.hpp"
7

8
#include <memory>
9
#include <string>
10
#include <vector>
11

12
/**
13
 * @brief Multi-output computer that samples x and y at equally spaced positions along a line.
14
 *
15
 * Source type: ILineSource
16
 * Selector type: Timestamp
17
 * Output type: double
18
 * 
19
 * Given a line source and a Timestamp-based ExecutionPlan, divides the [0,1] fractional
20
 * length into (segments) equal parts, yielding (segments+1) sample positions. For each
21
 * position, outputs two columns: x and y, in that order, resulting in 2*(segments+1)
22
 * outputs.
23
 */
24
class LineSamplingMultiComputer : public IMultiColumnComputer<double> {
25
public:
26
    LineSamplingMultiComputer(std::shared_ptr<ILineSource> lineSource,
29✔
27
                              std::string sourceName,
28
                              std::shared_ptr<TimeFrame> sourceTimeFrame,
29
                              int segments)
30
        : m_lineSource(std::move(lineSource)),
29✔
31
          m_sourceName(std::move(sourceName)),
29✔
32
          m_sourceTimeFrame(std::move(sourceTimeFrame)),
29✔
33
          m_segments(segments < 1 ? 1 : segments) {}
29✔
34

35
    [[nodiscard]] auto computeBatch(ExecutionPlan const & plan) const -> std::vector<std::vector<double>> override {
8✔
36
        // Determine rows: entity-expanded rows take precedence
37
        std::vector<TimeFrameIndex> indices;
8✔
38
        std::vector<std::optional<int>> entityIdx;
8✔
39
        if (!plan.getRows().empty()) {
8✔
40
            auto const& rows = plan.getRows();
6✔
41
            indices.reserve(rows.size());
6✔
42
            entityIdx.reserve(rows.size());
6✔
43
            for (auto const& r : rows) {
43✔
44
                indices.push_back(r.timeIndex);
37✔
45
                entityIdx.push_back(r.entityIndex);
37✔
46
            }
47
        } else if (plan.hasIndices()) {
2✔
48
            indices = plan.getIndices();
2✔
49
            entityIdx.resize(indices.size());
2✔
50
        } else {
51
            auto const & intervals = plan.getIntervals();
×
52
            indices.reserve(intervals.size());
×
53
            entityIdx.reserve(intervals.size());
×
54
            for (auto const & interval : intervals) {
×
55
                indices.push_back(interval.start);
×
56
                entityIdx.emplace_back(std::nullopt);
×
57
            }
58
        }
59

60
        size_t const rowCount = indices.size();
8✔
61
        int const positions = m_segments + 1;
8✔
62
        int const outputs = positions * 2; // x then y per position
8✔
63

64
        std::vector<std::vector<double>> results(static_cast<size_t>(outputs));
24✔
65
        for (auto & vec : results) {
54✔
66
            vec.resize(rowCount);
46✔
67
        }
68

69
        // Precompute fractional positions
70
        std::vector<float> fractions;
8✔
71
        fractions.reserve(static_cast<size_t>(positions));
8✔
72
        for (int i = 0; i <= m_segments; ++i) {
31✔
73
            fractions.push_back(static_cast<float>(i) / static_cast<float>(m_segments));
23✔
74
        }
75

76
        // For each row, obtain the representative line and sample
77
        // Use the plan's timeframe (rows are expressed in this timeframe)
78
        auto targetTF = plan.getTimeFrame().get();
8✔
79

80
        for (size_t r = 0; r < rowCount; ++r) {
51✔
81
            TimeFrameIndex const tfIndex = indices[r];
43✔
82
            // Prefer direct entity access if entity index is present
83
            Line2D const* linePtr = nullptr;
43✔
84
            if (entityIdx[r].has_value()) {
43✔
85
                linePtr = m_lineSource->getLineAt(tfIndex, *entityIdx[r]);
34✔
86
            }
87
            Line2D lineFallback;
43✔
88
            if (!linePtr) {
43✔
89
                auto lines = m_lineSource->getLinesInRange(tfIndex, tfIndex, targetTF);
9✔
90
                if (lines.empty()) {
9✔
91
                    for (int p = 0; p < positions; ++p) {
16✔
92
                        results[static_cast<size_t>(2 * p)][r] = 0.0;
12✔
93
                        results[static_cast<size_t>(2 * p + 1)][r] = 0.0;
12✔
94
                    }
95
                    continue;
4✔
96
                }
4✔
97
                lineFallback = lines.front();
5✔
98
                linePtr = &lineFallback;
5✔
99
            }
9✔
100

101
            Line2D const & line = *linePtr;
39✔
102
            for (int p = 0; p < positions; ++p) {
154✔
103
                auto optPoint = point_at_fractional_position(line, fractions[static_cast<size_t>(p)], true);
115✔
104
                if (optPoint.has_value()) {
115✔
105
                    results[static_cast<size_t>(2 * p)][r] = static_cast<double>(optPoint->x);
115✔
106
                    results[static_cast<size_t>(2 * p + 1)][r] = static_cast<double>(optPoint->y);
115✔
107
                } else {
NEW
108
                    results[static_cast<size_t>(2 * p)][r] = 0.0;
×
NEW
109
                    results[static_cast<size_t>(2 * p + 1)][r] = 0.0;
×
110
                }
111
            }
112
        }
43✔
113

114
        return results;
16✔
115
    }
8✔
116

117
    [[nodiscard]] auto getOutputNames() const -> std::vector<std::string> override {
29✔
118
        std::vector<std::string> suffixes;
29✔
119
        int const positions = m_segments + 1;
29✔
120
        suffixes.reserve(static_cast<size_t>(positions * 2));
29✔
121
        for (int i = 0; i <= m_segments; ++i) {
111✔
122
            double frac = static_cast<double>(i) / static_cast<double>(m_segments);
82✔
123
            // Fixed width to 3 decimals for readability
124
            char buf[32];
82✔
125
            std::snprintf(buf, sizeof(buf), "@%.3f", frac);
82✔
126
            suffixes.emplace_back(std::string{".x"} + buf);
246✔
127
            suffixes.emplace_back(std::string{".y"} + buf);
246✔
128
        }
129
        return suffixes;
29✔
130
    }
×
131

132
    [[nodiscard]] auto getDependencies() const -> std::vector<std::string> override {
156✔
133
        return {};
156✔
134
    }
135

136
    [[nodiscard]] auto getSourceDependency() const -> std::string override {
303✔
137
        return m_sourceName;
303✔
138
    }
139

140
private:
141
    std::shared_ptr<ILineSource> m_lineSource;
142
    std::string m_sourceName;
143
    std::shared_ptr<TimeFrame> m_sourceTimeFrame;
144
    int m_segments;
145
};
146

147
#endif // LINE_SAMPLING_MULTI_COMPUTER_H
148

149

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc