1 #include <QMessageBox> |
|
2 #include <document.h> |
|
3 #include "linetypes/edge.h" |
|
4 #include "linetypes/triangle.h" |
|
5 #include "linetypes/quadrilateral.h" |
|
6 #include "drawtool.h" |
|
7 #include "modeleditor.h" |
|
8 |
|
9 static const QBrush pointBrush = {Qt::white}; |
|
10 static const QPen polygonPen = {QBrush{Qt::black}, 2.0, Qt::DashLine}; |
|
11 static const QPen badPolygonPen = {QBrush{Qt::red}, 2.0, Qt::DashLine}; |
|
12 static const QPen pointPen = {QBrush{Qt::black}, 2.0}; |
|
13 static const QBrush greenPolygonBrush = {QColor{64, 255, 128, 192}}; |
|
14 static const QBrush redPolygonBrush = {QColor{255, 96, 96, 192}}; |
|
15 |
|
16 AbstractDrawTool::AbstractDrawTool(Document *document) : |
|
17 BaseTool{document} |
|
18 { |
|
19 } |
|
20 |
|
21 DrawTool::DrawTool(Document* document) : |
|
22 AbstractDrawTool{document} |
|
23 { |
|
24 } |
|
25 |
|
26 QString DrawTool::name() const |
|
27 { |
|
28 static const QString result = tr("Draw"); |
|
29 return result; |
|
30 } |
|
31 |
|
32 QString DrawTool::toolTip() const |
|
33 { |
|
34 static const QString result = tr("Draw new elements into the model."); |
|
35 return result; |
|
36 } |
|
37 |
|
38 bool AbstractDrawTool::mouseClick(Canvas* canvas, QMouseEvent* event) |
|
39 { |
|
40 if (event->button() == Qt::LeftButton) |
|
41 { |
|
42 this->addCurrentPoint(canvas); |
|
43 return true; |
|
44 } |
|
45 else if (event->button() == Qt::RightButton) |
|
46 { |
|
47 this->removeLastPoint(); |
|
48 return true; |
|
49 } |
|
50 else |
|
51 { |
|
52 return false; |
|
53 } |
|
54 } |
|
55 |
|
56 bool AbstractDrawTool::mouseMove(Document* document, Canvas* canvas, QMouseEvent *event) |
|
57 { |
|
58 static_cast<void>(document); |
|
59 static_cast<void>(event); |
|
60 const auto& worldPosition = canvas->getWorldPosition(); |
|
61 if (worldPosition.has_value()) |
|
62 { |
|
63 this->previewPoint = worldPosition.value(); |
|
64 this->updatePreviewPolygon(); |
|
65 } |
|
66 return false; |
|
67 } |
|
68 |
|
69 bool AbstractDrawTool::keyReleased(Document*, Canvas* canvas, QKeyEvent* event) |
|
70 { |
|
71 if (event->key() == Qt::Key_Escape) |
|
72 { |
|
73 this->polygon.clear(); |
|
74 this->updatePreviewPolygon(); |
|
75 canvas->update(); |
|
76 return true; |
|
77 } |
|
78 else |
|
79 { |
|
80 return false; |
|
81 } |
|
82 } |
|
83 |
|
84 void AbstractDrawTool::addCurrentPoint(Canvas* canvas) |
|
85 { |
|
86 const auto& worldPosition = canvas->getWorldPosition(); |
|
87 if (worldPosition.has_value()) |
|
88 { |
|
89 const glm::vec3& pos = worldPosition.value(); |
|
90 if (this->isCloseToExistingPoints(pos)) |
|
91 { |
|
92 this->closeShape(); |
|
93 } |
|
94 else |
|
95 { |
|
96 this->addPoint(pos); |
|
97 } |
|
98 } |
|
99 } |
|
100 |
|
101 void AbstractDrawTool::updatePreviewPolygon() |
|
102 { |
|
103 this->previewPolygon = this->polygon; |
|
104 this->previewPolygon.resize(this->polygon.size() + 1); |
|
105 this->previewPolygon.back() = this->previewPoint; |
|
106 if (this->previewPolygon.size() > 2) |
|
107 { |
|
108 this->isconcave = not geom::isConvex(this->previewPolygon); |
|
109 } |
|
110 } |
|
111 |
|
112 void AbstractDrawTool::reset() |
|
113 { |
|
114 this->polygon.clear(); |
|
115 } |
|
116 |
|
117 void AbstractDrawTool::overpaint(Canvas* canvas, QPainter* painter) const |
|
118 { |
|
119 painter->setPen(this->isconcave ? ::badPolygonPen : ::polygonPen); |
|
120 if (this->previewPolygon.size() > 2 and not this->isconcave) |
|
121 { |
|
122 if (canvas->worldPolygonWinding(this->previewPolygon) == Winding::Clockwise) |
|
123 { |
|
124 painter->setBrush(::greenPolygonBrush); |
|
125 } |
|
126 else |
|
127 { |
|
128 painter->setBrush(::redPolygonBrush); |
|
129 } |
|
130 canvas->drawWorldPolygon(painter, this->previewPolygon); |
|
131 } |
|
132 else |
|
133 { |
|
134 canvas->drawWorldPolyline(painter, this->previewPolygon); |
|
135 } |
|
136 painter->setBrush(::pointBrush); |
|
137 painter->setPen(::pointPen); |
|
138 for (const glm::vec3& point : this->polygon) |
|
139 { |
|
140 canvas->drawWorldPoint(painter, point); |
|
141 } |
|
142 canvas->drawWorldPoint(painter, this->previewPoint); |
|
143 } |
|
144 |
|
145 void AbstractDrawTool::addPoint(const glm::vec3 &pos) |
|
146 { |
|
147 this->polygon.push_back(pos); |
|
148 this->updatePreviewPolygon(); |
|
149 } |
|
150 |
|
151 void AbstractDrawTool::removeLastPoint() |
|
152 { |
|
153 if (this->polygon.size() > 0) |
|
154 { |
|
155 this->polygon.erase(this->polygon.end() - 1); |
|
156 this->updatePreviewPolygon(); |
|
157 } |
|
158 } |
|
159 |
|
160 void AbstractDrawTool::clearPoints() |
|
161 { |
|
162 this->polygon.clear(); |
|
163 this->updatePreviewPolygon(); |
|
164 } |
|
165 |
|
166 bool AbstractDrawTool::isCloseToExistingPoints(const glm::vec3 &pos) const |
|
167 { |
|
168 const auto isCloseToPos = [&](const glm::vec3& x) |
|
169 { |
|
170 return geom::isclose(x, pos); |
|
171 }; |
|
172 return any(this->polygon, isCloseToPos); |
|
173 } |
|
174 |
|
175 QString DrawTool::iconName() const |
|
176 { |
|
177 return ":/icons/pencil-outline.png"; |
|
178 } |
|
179 |
|
180 void DrawTool::addPoint(const glm::vec3 &pos) |
|
181 { |
|
182 AbstractDrawTool::addPoint(pos); |
|
183 if (this->polygon.size() == 4) |
|
184 { |
|
185 this->closeShape(); |
|
186 } |
|
187 } |
|
188 |
|
189 template<std::size_t N, typename T> |
|
190 std::array<T, N> vectorToArray(const std::vector<T>& x) |
|
191 { |
|
192 std::array<T, N> result; |
|
193 for (std::size_t i = 0; i < x.size() and i < N; i += 1) |
|
194 { |
|
195 result[i] = x[i]; |
|
196 } |
|
197 return result; |
|
198 } |
|
199 |
|
200 void DrawTool::closeShape() |
|
201 { |
|
202 if (this->polygon.size() >= 2 and this->polygon.size() <= 4) |
|
203 { |
|
204 std::unique_ptr<ModelEditor> modelEditor = this->document->editModel(); |
|
205 switch (this->polygon.size()) |
|
206 { |
|
207 case 2: |
|
208 modelEditor->append<ldraw::Edge>(vectorToArray<2>(this->polygon), ldraw::EDGE_COLOR); |
|
209 break; |
|
210 case 3: |
|
211 modelEditor->append<ldraw::Triangle>(vectorToArray<3>(this->polygon), ldraw::MAIN_COLOR); |
|
212 break; |
|
213 case 4: |
|
214 modelEditor->append<ldraw::Quadrilateral>(vectorToArray<4>(this->polygon), ldraw::MAIN_COLOR); |
|
215 break; |
|
216 } |
|
217 } |
|
218 this->clearPoints(); |
|
219 } |
|