src/ui/canvas.cpp

changeset 197
0e729e681a2c
parent 191
d355d4c52d51
child 198
eb9d900dc79a
equal deleted inserted replaced
196:6bcb284679d4 197:0e729e681a2c
1 #include <QMouseEvent> 1 #include <QMouseEvent>
2 #include <QPainter> 2 #include <QPainter>
3 #include "modeleditor.h" 3 #include "modeleditor.h"
4 #include "document.h" 4 #include "document.h"
5 #include "canvas.h" 5 #include "canvas.h"
6 #include "linetypes/edge.h"
7 #include "linetypes/triangle.h"
8 #include "linetypes/quadrilateral.h"
9 6
10 Canvas::Canvas( 7 Canvas::Canvas(
11 Model* model, 8 Model* model,
12 Document *document, 9 Document *document,
13 DocumentManager* documents, 10 DocumentManager* documents,
42 if (this->vertexProgram.has_value()) 39 if (this->vertexProgram.has_value())
43 { 40 {
44 this->vertexProgram->build(document); 41 this->vertexProgram->build(document);
45 this->update(); 42 this->update();
46 } 43 }
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));
72 } 44 }
73 45
74 void Canvas::mouseMoveEvent(QMouseEvent* event) 46 void Canvas::mouseMoveEvent(QMouseEvent* event)
75 { 47 {
76 const ldraw::id_t id = this->pick(event->pos()); 48 const ldraw::id_t id = this->pick(event->pos());
92 this->worldPosition = glm::round(*this->worldPosition); 64 this->worldPosition = glm::round(*this->worldPosition);
93 // And finally transform it back to grid coordinates by transforming it with the 65 // And finally transform it back to grid coordinates by transforming it with the
94 // grid matrix. 66 // grid matrix.
95 this->worldPosition = this->gridMatrix * glm::vec4{*this->worldPosition, 1}; 67 this->worldPosition = this->gridMatrix * glm::vec4{*this->worldPosition, 1};
96 } 68 }
97 switch(this->mode) 69 Q_EMIT this->mouseMove(event);
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 }
112 PartRenderer::mouseMoveEvent(event); 70 PartRenderer::mouseMoveEvent(event);
113 this->update(); 71 this->update();
114 } 72 }
115 73
116 void Canvas::mousePressEvent(QMouseEvent* event) 74 void Canvas::mousePressEvent(QMouseEvent* event)
122 80
123 void Canvas::mouseReleaseEvent(QMouseEvent* event) 81 void Canvas::mouseReleaseEvent(QMouseEvent* event)
124 { 82 {
125 if (this->totalMouseMove < (2.0 / sqrt(2)) * 5.0) 83 if (this->totalMouseMove < (2.0 / sqrt(2)) * 5.0)
126 { 84 {
127 switch(this->mode) 85 Q_EMIT this->mouseClick(event);
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 }
166 } 86 }
167 PartRenderer::mouseReleaseEvent(event); 87 PartRenderer::mouseReleaseEvent(event);
168 this->update(); 88 this->update();
169 } 89 }
170 90
206 const QPen pointPen = {QBrush{Qt::black}, 2.0}; 126 const QPen pointPen = {QBrush{Qt::black}, 2.0};
207 const QBrush greenPolygonBrush = {QColor{64, 255, 128, 192}}; 127 const QBrush greenPolygonBrush = {QColor{64, 255, 128, 192}};
208 const QBrush redPolygonBrush = {QColor{255, 96, 96, 192}}; 128 const QBrush redPolygonBrush = {QColor{255, 96, 96, 192}};
209 } pens; 129 } pens;
210 130
131 static void renderDrawState(
132 QPainter* painter,
133 Canvas* canvas,
134 DrawState* drawState);
135
211 void Canvas::paintGL() 136 void Canvas::paintGL()
212 { 137 {
213 PartRenderer::paintGL(); 138 PartRenderer::paintGL();
214 if (this->renderPreferences.style != gl::RenderStyle::PickScene) 139 if (this->renderPreferences.style != gl::RenderStyle::PickScene)
215 { 140 {
252 painter.setRenderHint(QPainter::Antialiasing); 177 painter.setRenderHint(QPainter::Antialiasing);
253 if (this->renderPreferences.drawAxes) 178 if (this->renderPreferences.drawAxes)
254 { 179 {
255 this->renderAxesLabels(painter); 180 this->renderAxesLabels(painter);
256 } 181 }
257 switch(this->mode) 182 if (this->drawState != nullptr) {
258 { 183 renderDrawState(&painter, this, this->drawState);
259 case SelectMode: 184 }
260 break; 185 }
261 case DrawMode: 186 }
187
188 static void renderDrawState(
189 QPainter* painter,
190 Canvas* canvas,
191 DrawState* drawState)
192 {
193 switch(drawState->mode)
194 {
195 case SelectMode:
196 break;
197 case DrawMode:
198 {
199 painter->setPen(drawState->isconcave ? ::pens.badPolygonPen : ::pens.polygonPen);
200 if (drawState->previewPolygon.size() > 2 and not drawState->isconcave)
262 { 201 {
263 painter.setPen(this->drawState.isconcave ? ::pens.badPolygonPen : ::pens.polygonPen); 202 if (canvas->worldPolygonWinding(drawState->previewPolygon) == Winding::Clockwise)
264 if (this->drawState.previewPolygon.size() > 2 and not this->drawState.isconcave)
265 { 203 {
266 if (this->worldPolygonWinding(this->drawState.previewPolygon) == Winding::Clockwise) 204 painter->setBrush(::pens.greenPolygonBrush);
267 {
268 painter.setBrush(::pens.greenPolygonBrush);
269 }
270 else
271 {
272 painter.setBrush(::pens.redPolygonBrush);
273 }
274 this->drawWorldPolygon(&painter, this->drawState.previewPolygon);
275 } 205 }
276 else 206 else
277 { 207 {
278 this->drawWorldPolyline(&painter, this->drawState.previewPolygon); 208 painter->setBrush(::pens.redPolygonBrush);
279 } 209 }
280 painter.setBrush(::pens.pointBrush); 210 canvas->drawWorldPolygon(painter, drawState->previewPolygon);
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);
287 } 211 }
288 break; 212 else
289 } 213 {
214 canvas->drawWorldPolyline(painter, drawState->previewPolygon);
215 }
216 painter->setBrush(::pens.pointBrush);
217 painter->setPen(::pens.pointPen);
218 for (const glm::vec3& point : drawState->polygon)
219 {
220 canvas->drawWorldPoint(painter, point);
221 }
222 canvas->drawWorldPoint(painter, drawState->previewPoint);
223 }
224 break;
290 } 225 }
291 } 226 }
292 227
293 /** 228 /**
294 * @brief Renders labels such as +x at the ends of axes at the screen 229 * @brief Renders labels such as +x at the ends of axes at the screen
412 const glm::mat4 &Canvas::getGridMatrix() const 347 const glm::mat4 &Canvas::getGridMatrix() const
413 { 348 {
414 return this->gridMatrix; 349 return this->gridMatrix;
415 } 350 }
416 351
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);
443 }
444
445 /** 352 /**
446 * @brief Paints a circle at where @c worldPoint is located on the screen. 353 * @brief Paints a circle at where @c worldPoint is located on the screen.
447 * @param painter Painter to use to render 354 * @param painter Painter to use to render
448 * @param worldPoint Point to render 355 * @param worldPoint Point to render
449 */ 356 */

mercurial