src/document.cpp

changeset 217
6d95c1a41e6e
parent 214
8e1fe64ce4e3
child 222
72b456f2f3c2
equal deleted inserted replaced
216:c7241f504117 217:6d95c1a41e6e
15 * You should have received a copy of the GNU General Public License 15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */ 17 */
18 18
19 #include <QMouseEvent> 19 #include <QMouseEvent>
20 #include <QMessageBox> 20 #include <QPainter>
21 #include <QVBoxLayout>
22 #include "document.h" 21 #include "document.h"
23 #include "model.h" 22 #include "model.h"
24 #include "ui/objecteditor.h" 23 #include "ui/objecteditor.h"
25 24 #include "gl/partrenderer.h"
26 EditTools::EditTools( 25
27 Model* model, 26 EditTools::EditTools(QObject* parent) :
28 const ColorTable& colorTable,
29 QObject* parent) :
30 QObject{parent}, 27 QObject{parent},
31 colorTable{colorTable}, 28 RenderLayer{}
32 model{model}, 29 {
33 vertexMap{model} 30 }
34 { 31
35 #if 0 32 EditTools::~EditTools()
36 connect(this->canvas, &Canvas::mouseClick, this, &EditTools::canvasMouseClick); 33 {
37 connect(this->canvas, &Canvas::mouseMove, this, &EditTools::canvasMouseMove); 34 }
38 connect(this->canvas, &Canvas::newStatusText, this, &EditTools::newStatusText); 35
39 connect(this->model, &Model::dataChanged, this->canvas, qOverload<>(&Canvas::update)); 36 void EditTools::setEditMode(EditingMode mode)
40 connect(&this->vertexMap, &VertexMap::verticesChanged, [&]() 37 {
41 { 38 this->mode = mode;
42 this->canvas->rebuildVertices(&this->vertexMap); 39 }
40
41 void EditTools::setGridMatrix(const glm::mat4& gridMatrix)
42 {
43 this->gridMatrix = gridMatrix;
44 this->gridPlane = planeFromTriangle({
45 this->gridMatrix * glm::vec4{0, 0, 0, 1},
46 this->gridMatrix * glm::vec4{1, 0, 0, 1},
47 this->gridMatrix * glm::vec4{0, 1, 0, 1},
43 }); 48 });
44 this->canvas->drawState = &this->drawState; 49 }
45 #endif 50
46 } 51 void EditTools::mvpMatrixChanged(const glm::mat4& matrix)
47 52 {
48 EditTools::~EditTools() 53 this->mvpMatrix = matrix;
49 { 54 }
50 } 55
51 56 void EditTools::mouseMoved(const QMouseEvent* event)
52 void EditTools::applyToVertices(VertexMap::ApplyFunction fn) const 57 {
53 { 58 this->worldPosition = this->renderer->screenToModelCoordinates(event->pos(), this->gridPlane);
54 this->vertexMap.apply(fn); 59 if (this->worldPosition.has_value())
55 } 60 {
56 61 // Snap the position to grid. This procedure is basically the "change of basis" and almost follows the
57 void EditTools::setEditMode(EditingMode mode) 62 // A⁻¹ × M × A formula which is used to perform a transformation in some other coordinate system, except
58 { 63 // we actually use the inverted matrix first and the regular one last to perform the transformation of
59 this->drawState.mode = mode; 64 // grid coordinates in our XY coordinate system. Also, we're rounding the coordinates which is obviously
60 } 65 // not a linear transformation, but fits the pattern anyway.
61 66 // First transform the coordinates to the XY plane...
62 void updatePreviewPolygon(DrawState* drawState) 67 this->worldPosition = glm::inverse(this->gridMatrix) * glm::vec4{*this->worldPosition, 1};
63 { 68 // Then round the coordinates to integer precision...
64 drawState->previewPolygon = drawState->polygon; 69 this->worldPosition = glm::round(*this->worldPosition);
65 drawState->previewPolygon.resize(drawState->polygon.size() + 1); 70 // And finally transform it back to grid coordinates by transforming it with the
66 drawState->previewPolygon.back() = drawState->previewPoint; 71 // grid matrix.
67 if (drawState->previewPolygon.size() > 2) 72 this->worldPosition = this->gridMatrix * glm::vec4{*this->worldPosition, 1};
68 { 73 }
69 drawState->isconcave = not isConvex(drawState->previewPolygon); 74 this->updatePreviewPolygon();
70 } 75 }
71 } 76
72 77 static QVector<QPointF> convertWorldPointsToScreenPoints(
73 void removeLastPoint(DrawState* drawState) 78 const std::vector<glm::vec3> &worldPoints,
74 { 79 const PartRenderer* renderer)
75 if (drawState->polygon.size() > 0) 80 {
76 { 81 QVector<QPointF> points2d;
77 drawState->polygon.erase(drawState->polygon.end() - 1); 82 points2d.reserve(worldPoints.size());
78 updatePreviewPolygon(drawState); 83 for (const glm::vec3& point : worldPoints)
84 {
85 points2d.push_back(renderer->modelToScreenCoordinates(point));
86 }
87 return points2d;
88 }
89
90 static Winding worldPolygonWinding(
91 const std::vector<glm::vec3> &points,
92 const PartRenderer* renderer)
93 {
94 return winding(QPolygonF{convertWorldPointsToScreenPoints(points, renderer)});
95 }
96
97 static void drawWorldPoint(
98 QPainter* painter,
99 const glm::vec3& worldPoint,
100 const PartRenderer* renderer)
101 {
102 const QPointF center = renderer->modelToScreenCoordinates(worldPoint);
103 painter->drawEllipse(inscribe(CircleF{center, 5}));
104 }
105
106 static void drawWorldPolyline(
107 QPainter *painter,
108 const std::vector<glm::vec3> &points,
109 const PartRenderer* renderer)
110 {
111 painter->drawPolyline(QPolygonF{convertWorldPointsToScreenPoints(points, renderer)});
112 }
113
114 static void drawWorldPolygon(
115 QPainter* painter,
116 const std::vector<glm::vec3> &points,
117 const PartRenderer* renderer)
118 {
119 painter->drawPolygon(QPolygonF{convertWorldPointsToScreenPoints(points, renderer)});
120 }
121
122 void EditTools::overpaint(QPainter* painter)
123 {
124 struct Pens
125 {
126 const QBrush pointBrush;
127 const QPen pointPen;
128 const QPen polygonPen;
129 const QPen badPolygonPen;
130 const QBrush greenPolygonBrush;
131 const QBrush redPolygonBrush;
132 };
133 static const Pens brightPens{
134 .pointBrush = {Qt::white},
135 .pointPen = {QBrush{Qt::black}, 2.0},
136 .polygonPen = {QBrush{Qt::black}, 2.0, Qt::DashLine},
137 .badPolygonPen = {QBrush{Qt::red}, 2.0, Qt::DashLine},
138 .greenPolygonBrush = {QColor{64, 255, 128, 192}},
139 .redPolygonBrush = {QColor{255, 96, 96, 192}},
140 };
141 static const Pens darkPens{
142 .pointBrush = {Qt::black},
143 .pointPen = {QBrush{Qt::white}, 2.0},
144 .polygonPen = {QBrush{Qt::white}, 2.0, Qt::DashLine},
145 .badPolygonPen = {QBrush{Qt::red}, 2.0, Qt::DashLine},
146 .greenPolygonBrush = {QColor{64, 255, 128, 192}},
147 .redPolygonBrush = {QColor{255, 96, 96, 192}},
148 };
149 const Pens& pens = (this->renderer->isDark() ? darkPens : brightPens);
150 switch(this->mode) {
151 case SelectMode:
152 break;
153 case DrawMode:
154 {
155 painter->setPen(this->isconcave ? pens.badPolygonPen : pens.polygonPen);
156 if (this->previewPolygon.size() > 2 and not this->isconcave)
157 {
158 if (worldPolygonWinding(this->previewPolygon, this->renderer) == Winding::Clockwise) {
159 painter->setBrush(pens.greenPolygonBrush);
160 }
161 else {
162 painter->setBrush(pens.redPolygonBrush);
163 }
164 drawWorldPolygon(painter, this->previewPolygon, this->renderer);
165 }
166 else {
167 drawWorldPolyline(painter, this->previewPolygon, this->renderer);
168 }
169 painter->setBrush(pens.pointBrush);
170 painter->setPen(pens.pointPen);
171 for (const glm::vec3& point : this->polygon) {
172 drawWorldPoint(painter, point, this->renderer);
173 }
174 }
175 break;
176 }
177 if (this->worldPosition.has_value())
178 {
179 painter->setRenderHint(QPainter::Antialiasing);
180 painter->setPen(Qt::white);
181 painter->setBrush(Qt::green);
182 const QPointF pos = this->renderer->modelToScreenCoordinates(*this->worldPosition);
183 painter->drawEllipse(pos, 5, 5);
184 painter->drawText(pos + QPointF{5, 5}, vectorToString(*this->worldPosition));
185 }
186 }
187
188 void EditTools::updatePreviewPolygon()
189 {
190 this->previewPolygon = this->polygon;
191 if (this->worldPosition.has_value()) {
192 this->previewPolygon.resize(this->polygon.size() + 1);
193 this->previewPolygon.back() = *this->worldPosition;
194 }
195 if (this->previewPolygon.size() > 2)
196 {
197 this->isconcave = not isConvex(this->previewPolygon);
198 }
199 }
200
201 void EditTools::removeLastPoint()
202 {
203 if (this->polygon.size() > 0)
204 {
205 this->polygon.erase(this->polygon.end() - 1);
206 this->updatePreviewPolygon();
79 } 207 }
80 } 208 }
81 209
82 bool isCloseToExistingPoints(const std::vector<glm::vec3>& points, const glm::vec3 &pos) 210 bool isCloseToExistingPoints(const std::vector<glm::vec3>& points, const glm::vec3 &pos)
83 { 211 {
84 return any(points, std::bind(isclose, std::placeholders::_1, pos)); 212 return any(points, std::bind(isclose, std::placeholders::_1, pos));
85 } 213 }
86 214
87 void EditTools::canvasMouseClick(QMouseEvent*) 215 EditingMode EditTools::currentEditingMode() const
88 { 216 {
89 #if 0 217 return this->mode;
90 switch(this->drawState.mode) 218 }
91 { 219
220 void EditTools::mouseClick(const QMouseEvent* event)
221 {
222 switch(this->mode) {
92 case SelectMode: 223 case SelectMode:
93 if (event->button() == Qt::LeftButton) 224 if (event->button() == Qt::LeftButton) {
94 { 225 const ModelId highlighted = this->renderer->pick(event->pos());
95 const ModelId highlighted = this->canvas->getHighlightedObject(); 226 Q_EMIT this->select({highlighted}, false);
96 QSet<ModelId> selected;
97 if (highlighted != ModelId{0}) {
98 selected.insert(highlighted);
99 }
100 //this->select(selected);
101 event->accept();
102 } 227 }
103 break; 228 break;
104 case DrawMode: 229 case DrawMode:
105 if (event->button() == Qt::LeftButton) { 230 if (event->button() == Qt::LeftButton and this->worldPosition.has_value()) {
106 if (isCloseToExistingPoints(this->drawState.polygon, worldPosition)) { 231 if (isCloseToExistingPoints(this->polygon, *this->worldPosition)) {
107 this->closeShape(); 232 this->closeShape();
108 } 233 }
109 else { 234 else {
110 this->drawState.polygon.push_back(pos); 235 this->polygon.push_back(*this->worldPosition);
111 updatePreviewPolygon(&this->drawState); 236 this->updatePreviewPolygon();
112 } 237 }
113 event->accept();
114 } 238 }
115 else if (true 239 else if (true
116 and event->button() == Qt::RightButton 240 and event->button() == Qt::RightButton
117 and this->drawState.polygon.size() > 0 241 and this->polygon.size() > 0
118 ) { 242 ) {
119 this->drawState.polygon.erase(this->drawState.polygon.end() - 1); 243 this->polygon.erase(this->polygon.end() - 1);
120 updatePreviewPolygon(&this->drawState); 244 updatePreviewPolygon();
121 event->accept(); 245 }
122 } 246 break;
123 break; 247 }
124 } 248 }
125 #endif 249
126 }
127 void EditTools::canvasMouseMove(QMouseEvent*)
128 {
129 #if 0
130 switch(this->drawState.mode)
131 {
132 case SelectMode:
133 break;
134 case DrawMode:
135 if (this->canvas->worldPosition.has_value())
136 {
137 this->drawState.previewPoint = this->canvas->worldPosition.value();
138 updatePreviewPolygon(&this->drawState);
139 this->update();
140 }
141 event->accept();
142 break;
143 }
144 #endif
145 }
146 #if 0
147 /*
148
149 void EditorTabWidget::select(const QSet<ModelId> &selected)
150 {
151 QItemSelectionModel* selectionModel = this->ui.listView->selectionModel();
152 QItemSelection itemSelection;
153 for (const ModelId id : selected)
154 {
155 const std::optional<int> row = this->model->find(id);
156 if (row.has_value())
157 {
158 const QModelIndex qindex = this->model->index(*row);
159 itemSelection.select(qindex, qindex);
160 }
161 }
162 selectionModel->select(itemSelection, QItemSelectionModel::ClearAndSelect);
163 }
164 */
165 const QSet<ModelId> EditTools::selectedObjects() const
166 {
167 return this->canvas->selectedObjects();
168 }
169 #endif
170 EditingMode EditTools::currentEditingMode() const
171 {
172 return this->drawState.mode;
173 }
174 #if 0
175 void EditTools::closeShape() 250 void EditTools::closeShape()
176 { 251 {
177 if (this->drawState.polygon.size() >= 2 and this->drawState.polygon.size() <= 4) 252 if (this->polygon.size() >= 2 and this->polygon.size() <= 4) {
178 { 253 switch (this->polygon.size()) {
179 switch (this->drawState.polygon.size())
180 {
181 case 2: 254 case 2:
182 Q_EMIT this->modelAction(AppendToModel{ 255 Q_EMIT this->modelAction(AppendToModel{
183 .newElement = Colored<LineSegment>{ 256 .newElement = Colored<LineSegment>{
184 LineSegment{ 257 LineSegment{
185 .p1 = this->drawState.polygon[0], 258 .p1 = this->polygon[0],
186 .p2 = this->drawState.polygon[1], 259 .p2 = this->polygon[1],
187 }, 260 },
188 EDGE_COLOR, 261 EDGE_COLOR,
189 } 262 }
190 }); 263 });
191 break; 264 break;
192 case 3: 265 case 3:
193 Q_EMIT this->modelAction(AppendToModel{ 266 Q_EMIT this->modelAction(AppendToModel{
194 .newElement = Colored<Triangle>{ 267 .newElement = Colored<Triangle>{
195 Triangle{ 268 Triangle{
196 .p1 = this->drawState.polygon[0], 269 .p1 = this->polygon[0],
197 .p2 = this->drawState.polygon[1], 270 .p2 = this->polygon[1],
198 .p3 = this->drawState.polygon[2], 271 .p3 = this->polygon[2],
199 }, 272 },
200 MAIN_COLOR, 273 MAIN_COLOR,
201 } 274 }
202 }); 275 });
203 break; 276 break;
204 case 4: 277 case 4:
205 Q_EMIT this->modelAction(AppendToModel{ 278 Q_EMIT this->modelAction(AppendToModel{
206 .newElement = Colored<Quadrilateral>{ 279 .newElement = Colored<Quadrilateral>{
207 Quadrilateral{ 280 Quadrilateral{
208 .p1 = this->drawState.polygon[0], 281 .p1 = this->polygon[0],
209 .p2 = this->drawState.polygon[1], 282 .p2 = this->polygon[1],
210 .p3 = this->drawState.polygon[2], 283 .p3 = this->polygon[2],
211 .p4 = this->drawState.polygon[3], 284 .p4 = this->polygon[3],
212 }, 285 },
213 MAIN_COLOR, 286 MAIN_COLOR,
214 } 287 }
215 }); 288 });
216 break; 289 break;
217 } 290 }
218 } 291 }
219 this->drawState.polygon.clear(); 292 this->polygon.clear();
220 updatePreviewPolygon(&this->drawState); 293 this->updatePreviewPolygon();
221 } 294 }
222
223 #endif

mercurial