|      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 } | 
        |