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 */ |