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

paulmthompson / WhiskerToolbox / 17920603410

22 Sep 2025 03:39PM UTC coverage: 71.97% (-0.05%) from 72.02%
17920603410

push

github

paulmthompson
all tests pass

277 of 288 new or added lines in 8 files covered. (96.18%)

520 existing lines in 35 files now uncovered.

40275 of 55961 relevant lines covered (71.97%)

1225.8 hits per line

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

89.53
/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 "utils/TableView/interfaces/IEntityProvider.h"
7
#include "CoreGeometry/line_geometry.hpp"
8

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

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

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

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

65
        std::vector<std::vector<double>> results(static_cast<size_t>(outputs));
30✔
66
        for (auto & vec : results) {
70✔
67
            vec.resize(rowCount);
60✔
68
        }
69

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

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

81
        std::vector<EntityId> entityIds;
10✔
82

83
        for (size_t r = 0; r < rowCount; ++r) {
65✔
84
            TimeFrameIndex const tfIndex = indices[r];
55✔
85
            auto const this_time_entityIds = m_lineSource->getEntityIdsAtTime(tfIndex, targetTF);
55✔
86
            // Prefer direct entity access if entity index is present
87
            Line2D const* linePtr = nullptr;
55✔
88
            if (entityIdx[r].has_value()) {
55✔
89
                linePtr = m_lineSource->getLineAt(tfIndex, *entityIdx[r]);
49✔
90
                entityIds.push_back(this_time_entityIds[*entityIdx[r]]);
49✔
91
            }
92
            Line2D lineFallback;
55✔
93
            if (!linePtr) {
55✔
94
                auto lines = m_lineSource->getLinesInRange(tfIndex, tfIndex, targetTF);
6✔
95
                if (lines.empty()) {
6✔
96
                    for (int p = 0; p < positions; ++p) {
12✔
97
                        results[static_cast<size_t>(2 * p)][r] = 0.0;
9✔
98
                        results[static_cast<size_t>(2 * p + 1)][r] = 0.0;
9✔
99
                    }
100
                    entityIds.push_back(0);
3✔
101
                    continue;
3✔
102
                }
3✔
103
                lineFallback = lines.front();
3✔
104
                linePtr = &lineFallback;
3✔
105
                entityIds.push_back(this_time_entityIds[0]);
3✔
106
            }
6✔
107

108
            Line2D const & line = *linePtr;
52✔
109
            for (int p = 0; p < positions; ++p) {
211✔
110
                auto optPoint = point_at_fractional_position(line, fractions[static_cast<size_t>(p)], true);
159✔
111
                if (optPoint.has_value()) {
159✔
112
                    results[static_cast<size_t>(2 * p)][r] = static_cast<double>(optPoint->x);
159✔
113
                    results[static_cast<size_t>(2 * p + 1)][r] = static_cast<double>(optPoint->y);
159✔
114
                } else {
UNCOV
115
                    results[static_cast<size_t>(2 * p)][r] = 0.0;
×
UNCOV
116
                    results[static_cast<size_t>(2 * p + 1)][r] = 0.0;
×
117
                }
118
            }
119
        }
58✔
120

121
        return {results, entityIds};
20✔
122
    }
10✔
123

124
    [[nodiscard]] auto getOutputNames() const -> std::vector<std::string> override {
32✔
125
        std::vector<std::string> suffixes;
32✔
126
        int const positions = m_segments + 1;
32✔
127
        suffixes.reserve(static_cast<size_t>(positions * 2));
32✔
128
        for (int i = 0; i <= m_segments; ++i) {
124✔
129
            double frac = static_cast<double>(i) / static_cast<double>(m_segments);
92✔
130
            // Fixed width to 3 decimals for readability
131
            char buf[32];
92✔
132
            std::snprintf(buf, sizeof(buf), "@%.3f", frac);
92✔
133
            suffixes.emplace_back(std::string{".x"} + buf);
276✔
134
            suffixes.emplace_back(std::string{".y"} + buf);
276✔
135
        }
136
        return suffixes;
32✔
UNCOV
137
    }
×
138

139
    [[nodiscard]] auto getDependencies() const -> std::vector<std::string> override {
176✔
140
        return {};
176✔
141
    }
142

143
    [[nodiscard]] auto getSourceDependency() const -> std::string override {
364✔
144
        return m_sourceName;
364✔
145
    }
146

147
    [[nodiscard]] auto getEntityIdStructure() const -> EntityIdStructure override {
280✔
148
        return EntityIdStructure::Simple;
280✔
149
    }
150

151
private:
152
    std::shared_ptr<ILineSource> m_lineSource;
153
    std::string m_sourceName;
154
    std::shared_ptr<TimeFrame> m_sourceTimeFrame;
155
    int m_segments;
156
};
157

158
#endif // LINE_SAMPLING_MULTI_COMPUTER_H
159

160

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