src/ui/canvas.cpp

changeset 191
d355d4c52d51
parent 189
815fbaae9cb2
child 197
0e729e681a2c
equal deleted inserted replaced
190:3dbdc243f053 191:d355d4c52d51
1 #include <QMouseEvent> 1 #include <QMouseEvent>
2 #include <QPainter> 2 #include <QPainter>
3 #include "modeleditor.h"
4 #include "document.h"
3 #include "canvas.h" 5 #include "canvas.h"
6 #include "linetypes/edge.h"
7 #include "linetypes/triangle.h"
8 #include "linetypes/quadrilateral.h"
4 9
5 Canvas::Canvas( 10 Canvas::Canvas(
6 Model* model, 11 Model* model,
12 Document *document,
7 DocumentManager* documents, 13 DocumentManager* documents,
8 const ldraw::ColorTable& colorTable, 14 const ldraw::ColorTable& colorTable,
9 QWidget* parent) : 15 QWidget* parent) :
10 PartRenderer{model, documents, colorTable, parent} 16 PartRenderer{model, documents, colorTable, parent},
17 document{document}
11 { 18 {
12 this->setMouseTracking(true); 19 this->setMouseTracking(true);
13 } 20 }
14 21
15 /** 22 /**
35 if (this->vertexProgram.has_value()) 42 if (this->vertexProgram.has_value())
36 { 43 {
37 this->vertexProgram->build(document); 44 this->vertexProgram->build(document);
38 this->update(); 45 this->update();
39 } 46 }
47 }
48
49 void updatePreviewPolygon(DrawState* drawState)
50 {
51 drawState->previewPolygon = drawState->polygon;
52 drawState->previewPolygon.resize(drawState->polygon.size() + 1);
53 drawState->previewPolygon.back() = drawState->previewPoint;
54 if (drawState->previewPolygon.size() > 2)
55 {
56 drawState->isconcave = not geom::isConvex(drawState->previewPolygon);
57 }
58 }
59
60 void removeLastPoint(DrawState* drawState)
61 {
62 if (drawState->polygon.size() > 0)
63 {
64 drawState->polygon.erase(drawState->polygon.end() - 1);
65 updatePreviewPolygon(drawState);
66 }
67 }
68
69 bool isCloseToExistingPoints(const std::vector<glm::vec3>& points, const glm::vec3 &pos)
70 {
71 return any(points, std::bind(geom::isclose, std::placeholders::_1, pos));
40 } 72 }
41 73
42 void Canvas::mouseMoveEvent(QMouseEvent* event) 74 void Canvas::mouseMoveEvent(QMouseEvent* event)
43 { 75 {
44 const ldraw::id_t id = this->pick(event->pos()); 76 const ldraw::id_t id = this->pick(event->pos());
60 this->worldPosition = glm::round(*this->worldPosition); 92 this->worldPosition = glm::round(*this->worldPosition);
61 // And finally transform it back to grid coordinates by transforming it with the 93 // And finally transform it back to grid coordinates by transforming it with the
62 // grid matrix. 94 // grid matrix.
63 this->worldPosition = this->gridMatrix * glm::vec4{*this->worldPosition, 1}; 95 this->worldPosition = this->gridMatrix * glm::vec4{*this->worldPosition, 1};
64 } 96 }
65 Q_EMIT this->mouseMove(this, event); 97 switch(this->mode)
98 {
99 case SelectMode:
100 break;
101 case DrawMode:
102 const auto& worldPosition = this->getWorldPosition();
103 if (worldPosition.has_value())
104 {
105 this->drawState.previewPoint = worldPosition.value();
106 updatePreviewPolygon(&this->drawState);
107 this->update();
108 }
109 event->accept();
110 break;
111 }
66 PartRenderer::mouseMoveEvent(event); 112 PartRenderer::mouseMoveEvent(event);
67 this->update(); 113 this->update();
68 } 114 }
69 115
70 void Canvas::mousePressEvent(QMouseEvent* event) 116 void Canvas::mousePressEvent(QMouseEvent* event)
76 122
77 void Canvas::mouseReleaseEvent(QMouseEvent* event) 123 void Canvas::mouseReleaseEvent(QMouseEvent* event)
78 { 124 {
79 if (this->totalMouseMove < (2.0 / sqrt(2)) * 5.0) 125 if (this->totalMouseMove < (2.0 / sqrt(2)) * 5.0)
80 { 126 {
81 Q_EMIT this->mouseClick(this, event); 127 switch(this->mode)
128 {
129 case SelectMode:
130 if (event->button() == Qt::LeftButton)
131 {
132 const ldraw::id_t highlighted = this->getHighlightedObject();
133 this->clearSelection();
134 if (highlighted != ldraw::NULL_ID)
135 {
136 this->addToSelection(highlighted);
137 }
138 event->accept();
139 }
140 break;
141 case DrawMode:
142 if (event->button() == Qt::LeftButton and this->worldPosition.has_value())
143 {
144 const glm::vec3& pos = worldPosition.value();
145 if (isCloseToExistingPoints(this->drawState.polygon, pos))
146 {
147 this->closeShape();
148 }
149 else
150 {
151 this->drawState.polygon.push_back(pos);
152 updatePreviewPolygon(&this->drawState);
153 }
154 event->accept();
155 }
156 else if (true
157 and event->button() == Qt::RightButton
158 and this->drawState.polygon.size() > 0
159 ) {
160 this->drawState.polygon.erase(this->drawState.polygon.end() - 1);
161 updatePreviewPolygon(&this->drawState);
162 event->accept();
163 }
164 break;
165 }
82 } 166 }
83 PartRenderer::mouseReleaseEvent(event); 167 PartRenderer::mouseReleaseEvent(event);
84 this->update(); 168 this->update();
85 } 169 }
86 170
112 // Set up XZ grid matrix 196 // Set up XZ grid matrix
113 this->setGridMatrix({{1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}); 197 this->setGridMatrix({{1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}});
114 this->updateCanvasRenderPreferences(); 198 this->updateCanvasRenderPreferences();
115 } 199 }
116 200
201 static const struct
202 {
203 const QBrush pointBrush = {Qt::white};
204 const QPen polygonPen = {QBrush{Qt::black}, 2.0, Qt::DashLine};
205 const QPen badPolygonPen = {QBrush{Qt::red}, 2.0, Qt::DashLine};
206 const QPen pointPen = {QBrush{Qt::black}, 2.0};
207 const QBrush greenPolygonBrush = {QColor{64, 255, 128, 192}};
208 const QBrush redPolygonBrush = {QColor{255, 96, 96, 192}};
209 } pens;
210
117 void Canvas::paintGL() 211 void Canvas::paintGL()
118 { 212 {
119 PartRenderer::paintGL(); 213 PartRenderer::paintGL();
120 if (this->renderPreferences.style != gl::RenderStyle::PickScene) 214 if (this->renderPreferences.style != gl::RenderStyle::PickScene)
121 { 215 {
152 painter.setBrush(Qt::green); 246 painter.setBrush(Qt::green);
153 const QPointF pos = this->modelToScreenCoordinates(*this->worldPosition); 247 const QPointF pos = this->modelToScreenCoordinates(*this->worldPosition);
154 painter.drawEllipse(pos, 5, 5); 248 painter.drawEllipse(pos, 5, 5);
155 painter.drawText(pos + QPointF{5, 5}, vectorToString(*this->worldPosition)); 249 painter.drawText(pos + QPointF{5, 5}, vectorToString(*this->worldPosition));
156 } 250 }
157 { 251 QPainter painter{this};
158 QPainter painter{this}; 252 painter.setRenderHint(QPainter::Antialiasing);
159 painter.setRenderHint(QPainter::Antialiasing); 253 if (this->renderPreferences.drawAxes)
160 if (this->renderPreferences.drawAxes) 254 {
255 this->renderAxesLabels(painter);
256 }
257 switch(this->mode)
258 {
259 case SelectMode:
260 break;
261 case DrawMode:
161 { 262 {
162 this->renderAxesLabels(painter); 263 painter.setPen(this->drawState.isconcave ? ::pens.badPolygonPen : ::pens.polygonPen);
264 if (this->drawState.previewPolygon.size() > 2 and not this->drawState.isconcave)
265 {
266 if (this->worldPolygonWinding(this->drawState.previewPolygon) == Winding::Clockwise)
267 {
268 painter.setBrush(::pens.greenPolygonBrush);
269 }
270 else
271 {
272 painter.setBrush(::pens.redPolygonBrush);
273 }
274 this->drawWorldPolygon(&painter, this->drawState.previewPolygon);
275 }
276 else
277 {
278 this->drawWorldPolyline(&painter, this->drawState.previewPolygon);
279 }
280 painter.setBrush(::pens.pointBrush);
281 painter.setPen(::pens.pointPen);
282 for (const glm::vec3& point : this->drawState.polygon)
283 {
284 this->drawWorldPoint(&painter, point);
285 }
286 this->drawWorldPoint(&painter, this->drawState.previewPoint);
163 } 287 }
164 if (this->overpaintCallback != nullptr) 288 break;
165 {
166 this->overpaintCallback(this, &painter);
167 }
168 } 289 }
169 } 290 }
170 } 291 }
171 292
172 /** 293 /**
262 } 383 }
263 384
264 /** 385 /**
265 * @brief Adjusts the grid to be so that it is perpendicular to the camera. 386 * @brief Adjusts the grid to be so that it is perpendicular to the camera.
266 */ 387 */
267 void Canvas::adjustGridToView() 388 void adjustGridToView(Canvas* canvas)
268 { 389 {
269 const glm::vec3 cameraDirection = this->cameraVector(); 390 const glm::vec3 cameraDirection = canvas->cameraVector();
270 const glm::vec3 vector_x = glm::normalize(this->gridMatrix * glm::vec4{1, 0, 0, 1}); 391 const glm::mat4& grid = canvas->getGridMatrix();
271 const glm::vec3 vector_y = glm::normalize(this->gridMatrix * glm::vec4{0, 1, 0, 1}); 392 const glm::vec3 vector_x = glm::normalize(grid * glm::vec4{1, 0, 0, 1});
393 const glm::vec3 vector_y = glm::normalize(grid * glm::vec4{0, 1, 0, 1});
272 const float angle_x = std::abs(glm::dot(vector_x, cameraDirection)); 394 const float angle_x = std::abs(glm::dot(vector_x, cameraDirection));
273 const float angle_y = std::abs(glm::dot(vector_y, cameraDirection)); 395 const float angle_y = std::abs(glm::dot(vector_y, cameraDirection));
274 if (angle_x < angle_y) 396 canvas->setGridMatrix(glm::rotate(
275 { 397 grid,
276 this->setGridMatrix(glm::rotate(this->gridMatrix, PI<float> / 2, glm::vec3{1, 0, 0})); 398 pi<> * 0.5f,
277 } 399 (angle_x < angle_y) ? glm::vec3{1, 0, 0} : glm::vec3{0, 1, 0}
278 else 400 ));
279 { 401 canvas->update();
280 this->setGridMatrix(glm::rotate(this->gridMatrix, PI<float> / 2, glm::vec3{0, 1, 0}));
281 }
282 this->update();
283 } 402 }
284 403
285 /** 404 /**
286 * @returns the ids of the currently selected objects 405 * @returns the ids of the currently selected objects
287 */ 406 */
291 } 410 }
292 411
293 const glm::mat4 &Canvas::getGridMatrix() const 412 const glm::mat4 &Canvas::getGridMatrix() const
294 { 413 {
295 return this->gridMatrix; 414 return this->gridMatrix;
415 }
416
417 void Canvas::closeShape()
418 {
419 if (this->drawState.polygon.size() >= 2 and this->drawState.polygon.size() <= 4)
420 {
421 std::unique_ptr<ModelEditor> modelEditor = this->document->editModel();
422 switch (this->drawState.polygon.size())
423 {
424 case 2:
425 modelEditor->append<ldraw::Edge>(
426 vectorToArray<2>(this->drawState.polygon),
427 ldraw::EDGE_COLOR);
428 break;
429 case 3:
430 modelEditor->append<ldraw::Triangle>(
431 vectorToArray<3>(this->drawState.polygon),
432 ldraw::MAIN_COLOR);
433 break;
434 case 4:
435 modelEditor->append<ldraw::Quadrilateral>(
436 vectorToArray<4>(this->drawState.polygon),
437 ldraw::MAIN_COLOR);
438 break;
439 }
440 }
441 this->drawState.polygon.clear();
442 updatePreviewPolygon(&this->drawState);
296 } 443 }
297 444
298 /** 445 /**
299 * @brief Paints a circle at where @c worldPoint is located on the screen. 446 * @brief Paints a circle at where @c worldPoint is located on the screen.
300 * @param painter Painter to use to render 447 * @param painter Painter to use to render

mercurial