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

paulmthompson / WhiskerToolbox / 18685379784

21 Oct 2025 01:25PM UTC coverage: 72.522% (+0.1%) from 72.391%
18685379784

push

github

paulmthompson
fix failing tests

18 of 40 new or added lines in 1 file covered. (45.0%)

1765 existing lines in 32 files now uncovered.

53998 of 74457 relevant lines covered (72.52%)

46177.73 hits per line

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

69.59
/src/WhiskerToolbox/Analysis_Dashboard/Selection/PolygonSelectionHandler.cpp
1
#include "PolygonSelectionHandler.hpp"
2

3
#include "ShaderManager/ShaderManager.hpp"
4

5
#include <QDebug>
6
#include <QKeyEvent>
7
#include <QMouseEvent>
8
#include <QOpenGLShaderProgram>
9

10
PolygonSelectionHandler::PolygonSelectionHandler()
2✔
11
    : _polygon_vertex_buffer(QOpenGLBuffer::VertexBuffer),
2✔
12
      _polygon_line_buffer(QOpenGLBuffer::VertexBuffer),
2✔
13
      _is_polygon_selecting(false) {
6✔
14

15
    initializeOpenGLFunctions();
2✔
16

17
    initializeOpenGLResources();
2✔
18
}
2✔
19

20
PolygonSelectionHandler::~PolygonSelectionHandler() {
4✔
21
    cleanupOpenGLResources();
2✔
22
}
4✔
23

24
void PolygonSelectionHandler::setNotificationCallback(NotificationCallback callback) {
2✔
25
    _notification_callback = callback;
2✔
26
}
2✔
27

28
void PolygonSelectionHandler::clearNotificationCallback() {
×
29
    _notification_callback = nullptr;
×
30
}
×
31

32
void PolygonSelectionHandler::initializeOpenGLResources() {
2✔
33

34
    ShaderManager & shader_manager = ShaderManager::instance();
2✔
35
    if (!shader_manager.getProgram("line")) {
6✔
36
        bool success = shader_manager.loadProgram("line",
24✔
37
                                                  ":/shaders/line.vert",
38
                                                  ":/shaders/line.frag",
39
                                                  "",
40
                                                  ShaderSourceType::Resource);
8✔
41
        if (!success) {
2✔
42
            qDebug() << "Failed to load line shader!";
×
43
        }
44
    }
45

46
    // Create polygon vertex array object and buffer
47
    _polygon_vertex_array_object.create();
2✔
48
    _polygon_vertex_array_object.bind();
2✔
49

50
    _polygon_vertex_buffer.create();
2✔
51
    _polygon_vertex_buffer.bind();
2✔
52
    _polygon_vertex_buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
2✔
53

54
    // Pre-allocate polygon vertex buffer (initially empty)
55
    glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
2✔
56

57
    // Set up vertex attributes for polygon vertices
58
    glEnableVertexAttribArray(0);
2✔
59
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr);
2✔
60

61
    _polygon_vertex_array_object.release();
2✔
62
    _polygon_vertex_buffer.release();
2✔
63

64
    // Create polygon line array object and buffer
65
    _polygon_line_array_object.create();
2✔
66
    _polygon_line_array_object.bind();
2✔
67

68
    _polygon_line_buffer.create();
2✔
69
    _polygon_line_buffer.bind();
2✔
70
    _polygon_line_buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
2✔
71

72
    // Pre-allocate polygon line buffer (initially empty)
73
    glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
2✔
74

75
    // Set up vertex attributes for polygon lines
76
    glEnableVertexAttribArray(0);
2✔
77
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr);
2✔
78

79
    _polygon_line_array_object.release();
2✔
80
    _polygon_line_buffer.release();
2✔
81

82
    qDebug() << "PolygonSelectionHandler: OpenGL resources initialized successfully";
2✔
83
}
2✔
84

85
void PolygonSelectionHandler::cleanupOpenGLResources() {
2✔
86
    _polygon_vertex_buffer.destroy();
2✔
87
    _polygon_vertex_array_object.destroy();
2✔
88

89
    _polygon_line_buffer.destroy();
2✔
90
    _polygon_line_array_object.destroy();
2✔
91
}
2✔
92

93
void PolygonSelectionHandler::startPolygonSelection(int world_x, int world_y) {
2✔
94

95
    qDebug() << "PolygonSelectionHandler: Starting polygon selection at" << world_x << "," << world_y;
2✔
96

97
    _is_polygon_selecting = true;
2✔
98
    _polygon_vertices.clear();
2✔
99

100
    // Add first vertex
101
    _polygon_vertices.push_back(Point2D<float>(world_x, world_y));
2✔
102

103
    qDebug() << "PolygonSelectionHandler: Added first polygon vertex at world:" << world_x << "," << world_y;
2✔
104

105
    // Update polygon buffers
106
    updatePolygonBuffers();
2✔
107
}
2✔
108

109
void PolygonSelectionHandler::addPolygonVertex(int world_x, int world_y) {
5✔
110
    if (!_is_polygon_selecting) {
5✔
111
        return;
×
112
    }
113

114
    _polygon_vertices.push_back(Point2D<float>(world_x, world_y));
5✔
115

116
    qDebug() << "PolygonSelectionHandler: Added polygon vertex" << _polygon_vertices.size()
10✔
117
             << "at" << world_x << "," << world_y;
10✔
118

119
    // Update polygon buffers
120
    updatePolygonBuffers();
5✔
121
}
122

123
void PolygonSelectionHandler::completePolygonSelection() {
2✔
124
    if (!_is_polygon_selecting || _polygon_vertices.size() < 3) {
2✔
125
        qDebug() << "PolygonSelectionHandler: Cannot complete polygon selection - insufficient vertices";
×
126
        cancelPolygonSelection();
×
127
        return;
×
128
    }
129

130
    qDebug() << "PolygonSelectionHandler: Completing polygon selection with"
4✔
131
             << _polygon_vertices.size() << "vertices";
4✔
132

133
    // Create selection region and apply it
134
    auto polygon_region = std::make_unique<PolygonSelectionRegion>(_polygon_vertices);
2✔
135
    _active_selection_region = std::move(polygon_region);
2✔
136

137
    // Call notification callback if set
138
    if (_notification_callback) {
2✔
139
        _notification_callback();
2✔
140
    }
141

142
    // Clean up polygon selection state
143
    _is_polygon_selecting = false;
2✔
144
    _polygon_vertices.clear();
2✔
145
}
2✔
146

147
void PolygonSelectionHandler::cancelPolygonSelection() {
×
148
    qDebug() << "PolygonSelectionHandler: Cancelling polygon selection";
×
149

150
    _is_polygon_selecting = false;
×
151
    _polygon_vertices.clear();
×
152

153
    // Clear polygon buffers
154

155
    _polygon_vertex_buffer.bind();
×
156
    glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
×
157
    _polygon_vertex_buffer.release();
×
158

159
    _polygon_line_buffer.bind();
×
160
    glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
×
161
    _polygon_line_buffer.release();
×
162
}
×
163

164
void PolygonSelectionHandler::render(QMatrix4x4 const & mvp_matrix) {
1✔
165
    if (!_is_polygon_selecting || _polygon_vertices.empty()) {
1✔
166
        return;
1✔
167
    }
168

UNCOV
169
    ShaderManager & shader_manager = ShaderManager::instance();
×
UNCOV
170
    _line_shader_program = shader_manager.getProgram("line")->getNativeProgram();
×
171

UNCOV
172
    qDebug() << "PolygonSelectionHandler: Rendering polygon overlay with" << _polygon_vertices.size() << "vertices";
×
173

174
    // Use line shader program
UNCOV
175
    if (!_line_shader_program->bind()) {
×
176
        qDebug() << "PolygonSelectionHandler: Failed to bind line shader program";
×
177
        return;
×
178
    }
179

180
    // Set uniform matrices
UNCOV
181
    _line_shader_program->setUniformValue("u_mvp_matrix", mvp_matrix);
×
182

183
    // Enable line smoothing
UNCOV
184
    glEnable(GL_LINE_SMOOTH);
×
UNCOV
185
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
×
186

187
    // Set line width
UNCOV
188
    glLineWidth(2.0f);
×
189

190
    // === DRAW CALL 1: Render polygon vertices as points ===
UNCOV
191
    _polygon_vertex_array_object.bind();
×
UNCOV
192
    _polygon_vertex_buffer.bind();
×
193

194
    // Set vertex attributes
UNCOV
195
    glEnableVertexAttribArray(0);
×
UNCOV
196
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr);
×
197

198
    // Set uniforms for vertices (red points)
UNCOV
199
    _line_shader_program->setUniformValue("u_color", QVector4D(1.0f, 0.0f, 0.0f, 1.0f));// Red
×
UNCOV
200
    _line_shader_program->setUniformValue("u_point_size", 8.0f);
×
201

202
    // Draw vertices as points
UNCOV
203
    glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(_polygon_vertices.size()));
×
204

UNCOV
205
    _polygon_vertex_buffer.release();
×
UNCOV
206
    _polygon_vertex_array_object.release();
×
207

208
    // === DRAW CALL 2: Render polygon lines ===
UNCOV
209
    if (_polygon_vertices.size() >= 2) {
×
UNCOV
210
        _polygon_line_array_object.bind();
×
UNCOV
211
        _polygon_line_buffer.bind();
×
212

213
        // Set vertex attributes
UNCOV
214
        glEnableVertexAttribArray(0);
×
UNCOV
215
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr);
×
216

217
        // Set uniforms for lines (blue dashed appearance)
UNCOV
218
        _line_shader_program->setUniformValue("u_color", QVector4D(0.2f, 0.6f, 1.0f, 1.0f));// Blue
×
219

220
        // Draw lines
UNCOV
221
        glDrawArrays(GL_LINES, 0, static_cast<GLsizei>((_polygon_vertices.size() - 1) * 2));
×
222

223
        // Draw closure line if we have 3+ vertices
UNCOV
224
        if (_polygon_vertices.size() >= 3) {
×
225
            // Draw closure line with different color
226
            _line_shader_program->setUniformValue("u_color", QVector4D(1.0f, 0.6f, 0.2f, 1.0f));// Orange
×
227
            glDrawArrays(GL_LINES, static_cast<GLint>((_polygon_vertices.size() - 1) * 2), 2);
×
228
        }
229

UNCOV
230
        _polygon_line_buffer.release();
×
UNCOV
231
        _polygon_line_array_object.release();
×
232
    }
233

234
    // Reset line width
UNCOV
235
    glLineWidth(1.0f);
×
UNCOV
236
    glDisable(GL_LINE_SMOOTH);
×
237

UNCOV
238
    _line_shader_program->release();
×
239

UNCOV
240
    qDebug() << "PolygonSelectionHandler: Finished rendering polygon overlay";
×
241
}
242

243
void PolygonSelectionHandler::mousePressEvent(QMouseEvent * event, QVector2D const & world_pos) {
7✔
244
    if (event->button() == Qt::LeftButton && event->modifiers().testFlag(Qt::ControlModifier)) {
7✔
245
        if (!isPolygonSelecting()) {
7✔
246
            startPolygonSelection(static_cast<int>(world_pos.x()), static_cast<int>(world_pos.y()));
2✔
247
        } else {
248
            addPolygonVertex(static_cast<int>(world_pos.x()), static_cast<int>(world_pos.y()));
5✔
249
        }
250
    }
251
}
7✔
252

253
void PolygonSelectionHandler::keyPressEvent(QKeyEvent * event) {
2✔
254
    if (event->key() == Qt::Key_Escape) {
2✔
255
        cancelPolygonSelection();
×
256
    } else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
2✔
257
        if (isPolygonSelecting()) {
2✔
258
            completePolygonSelection();
2✔
259
        }
260
    }
261
}
2✔
262

263
void PolygonSelectionHandler::deactivate() {
×
264
    cancelPolygonSelection();
×
265
    //cleanupOpenGLResources();
266
}
×
267

268
void PolygonSelectionHandler::updatePolygonBuffers() {
7✔
269
    if (_polygon_vertices.empty()) {
7✔
270
        return;
×
271
    }
272

273
    // Update vertex buffer (for drawing vertices as points)
274
    std::vector<float> vertex_data;
7✔
275
    vertex_data.reserve(_polygon_vertices.size() * 2);
7✔
276

277
    for (auto const & vertex: _polygon_vertices) {
23✔
278
        vertex_data.push_back(vertex.x);
16✔
279
        vertex_data.push_back(vertex.y);
16✔
280
    }
281

282
    _polygon_vertex_array_object.bind();
7✔
283
    _polygon_vertex_buffer.bind();
7✔
284
    _polygon_vertex_buffer.allocate(vertex_data.data(), static_cast<int>(vertex_data.size() * sizeof(float)));
7✔
285

286
    // Set vertex attributes
287
    glEnableVertexAttribArray(0);
7✔
288
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr);
7✔
289

290
    _polygon_vertex_buffer.release();
7✔
291
    _polygon_vertex_array_object.release();
7✔
292

293
    // Update line buffer (for drawing lines between vertices)
294
    if (_polygon_vertices.size() >= 2) {
7✔
295
        std::vector<float> line_data;
5✔
296
        line_data.reserve((_polygon_vertices.size() * 2 + 2) * 2);// Extra space for closure line
5✔
297

298
        // Add lines between consecutive vertices
299
        for (size_t i = 1; i < _polygon_vertices.size(); ++i) {
14✔
300
            // Line from previous vertex to current vertex
301
            line_data.push_back(_polygon_vertices[i - 1].x);
9✔
302
            line_data.push_back(_polygon_vertices[i - 1].y);
9✔
303
            line_data.push_back(_polygon_vertices[i].x);
9✔
304
            line_data.push_back(_polygon_vertices[i].y);
9✔
305
        }
306

307
        // Add closure line if we have 3+ vertices
308
        if (_polygon_vertices.size() >= 3) {
5✔
309
            line_data.push_back(_polygon_vertices.back().x);
3✔
310
            line_data.push_back(_polygon_vertices.back().y);
3✔
311
            line_data.push_back(_polygon_vertices.front().x);
3✔
312
            line_data.push_back(_polygon_vertices.front().y);
3✔
313
        }
314

315
        _polygon_line_array_object.bind();
5✔
316
        _polygon_line_buffer.bind();
5✔
317
        _polygon_line_buffer.allocate(line_data.data(), static_cast<int>(line_data.size() * sizeof(float)));
5✔
318

319
        // Set vertex attributes
320
        glEnableVertexAttribArray(0);
5✔
321
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr);
5✔
322

323
        _polygon_line_buffer.release();
5✔
324
        _polygon_line_array_object.release();
5✔
325
    }
5✔
326
}
7✔
327

328
PolygonSelectionRegion::PolygonSelectionRegion(std::vector<Point2D<float>> const & vertices)
2✔
329
    : _polygon(vertices) {
2✔
330
}
2✔
331

332
bool PolygonSelectionRegion::containsPoint(Point2D<float> point) const {
4✔
333
    return _polygon.containsPoint(point);
4✔
334
}
335

336
void PolygonSelectionRegion::getBoundingBox(float & min_x, float & min_y, float & max_x, float & max_y) const {
2✔
337
    auto bbox = _polygon.getBoundingBox();
2✔
338
    min_x = bbox.min_x;
2✔
339
    min_y = bbox.min_y;
2✔
340
    max_x = bbox.max_x;
2✔
341
    max_y = bbox.max_y;
2✔
342
}
2✔
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