Mon, 16 May 2022 01:40:49 +0300
work on circle tool
src/document.cpp | file | annotate | diff | comparison | revisions | |
src/document.ui | file | annotate | diff | comparison | revisions | |
src/geometry.cpp | file | annotate | diff | comparison | revisions | |
src/geometry.h | file | annotate | diff | comparison | revisions | |
src/tools/circletool.cpp | file | annotate | diff | comparison | revisions | |
src/ui/canvas.cpp | file | annotate | diff | comparison | revisions | |
src/ui/canvas.h | file | annotate | diff | comparison | revisions |
--- a/src/document.cpp Thu Apr 14 11:08:20 2022 +0300 +++ b/src/document.cpp Mon May 16 01:40:49 2022 +0300 @@ -27,6 +27,7 @@ #include "tools/pathtool.h" #include "tools/selecttool.h" #include "tools/transformtool.h" +#include "tools/circletool.h" Document::Document( Model* model, @@ -43,6 +44,8 @@ toolsBar{new QToolBar{this}} { this->ui.setupUi(this); + const int listWidth = static_cast<int>(this->width() / 3); + this->ui.viewportListSplitter->setSizes({listWidth * 2, listWidth}); this->ui.toolsBarContainer->setLayout(new QVBoxLayout{this->ui.toolsBarContainer}); this->ui.toolsBarContainer->layout()->addWidget(this->toolsBar); this->ui.listView->setModel(model); @@ -50,7 +53,7 @@ this->ui.viewportFrame->layout()->addWidget(this->renderer); this->toolsBar->setOrientation(Qt::Vertical); this->setMouseTracking(true); - connect(this->ui.splitter, &QSplitter::splitterMoved, this, &Document::splitterChanged); + connect(this->ui.viewportListSplitter, &QSplitter::splitterMoved, this, &Document::splitterChanged); connect(this->renderer, &Canvas::newStatusText, this, &Document::newStatusText); connect(this->renderer, &Canvas::selectionChanged, [&](const QSet<ldraw::id_t>& newSelection) { @@ -115,12 +118,12 @@ QByteArray Document::saveSplitterState() const { - return this->ui.splitter->saveState(); + return this->ui.viewportListSplitter->saveState(); } void Document::restoreSplitterState(const QByteArray& state) { - this->ui.splitter->restoreState(state); + this->ui.viewportListSplitter->restoreState(state); } void Document::setRenderPreferences(const gl::RenderPreferences& newPreferences) @@ -161,6 +164,7 @@ this->tools.reserve(3); this->tools.push_back(new SelectTool{this}); this->tools.push_back(new DrawTool{this}); + this->tools.push_back(new CircleTool{this}); this->tools.push_back(new PathTool{this}); this->tools.push_back(new TransformTool{this}); for (BaseTool* const toolInstance : this->tools)
--- a/src/document.ui Thu Apr 14 11:08:20 2022 +0300 +++ b/src/document.ui Mon May 16 01:40:49 2022 +0300 @@ -22,7 +22,7 @@ <property name="orientation"> <enum>Qt::Vertical</enum> </property> - <widget class="QSplitter" name="splitter"> + <widget class="QSplitter" name="viewportListSplitter"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> @@ -46,13 +46,32 @@ </property> </widget> </widget> - <widget class="QStackedWidget" name="toolWidgetStack"> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> + <widget class="QScrollArea" name="scrollArea"> + <property name="widgetResizable"> + <bool>true</bool> </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> + <widget class="QWidget" name="scrollAreaWidgetContents"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>921</width> + <height>73</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QStackedWidget" name="toolWidgetStack"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + </widget> + </item> + </layout> + </widget> </widget> </widget> </item>
--- a/src/geometry.cpp Thu Apr 14 11:08:20 2022 +0300 +++ b/src/geometry.cpp Mon May 16 01:40:49 2022 +0300 @@ -235,3 +235,43 @@ } return (sum < 0) ? Winding::Anticlockwise : Winding::Clockwise; } + +/** + * @brief computes the point on a Bezier curve + * @param curve + * @param t scalar between 0 and 1, with t=0 being P0 and t=1 being P3 + * @return point on curve + */ +glm::vec3 geom::pointOnCurve(const BezierCurve &curve, float t) +{ + // clamp t as rounding errors might make it slightly out of bounds + t = std::clamp(t, 0.0f, 1.0f); + const float t_2 = t * t; + const float t_3 = t * t * t; + const float coeffs[3] = { + -1*t_3 +3*t_2 -3*t +1, + +3*t_3 -6*t_2 +3*t, + -3*t_3 +3*t_2, + }; + return coeffs[0] * curve[0] + coeffs[1] * curve[1] + coeffs[2] * curve[2] + t_3 * curve[3]; +} + +/** + * @brief computes the derivative of a point on a Bezier curve + * @param curve + * @param t scalar between 0 and 1, with t=0 being P0 and t=1 being P3 + * @return point on curve + */ +glm::vec3 geom::derivativeOnCurve(const BezierCurve &curve, float t) +{ + // clamp t as rounding errors might make it slightly out of bounds + t = std::clamp(t, 0.0f, 1.0f); + const float t_2 = t * t; + const float coeffs[4] = { + -3*t_2 + 6*t -3, + +9*t_2 -12*t +3, + -9*t_2 + 6*t, + +3*t_2 + }; + return coeffs[0] * curve[0] + coeffs[1] * curve[1] + coeffs[2] * curve[2] + coeffs[3] * curve[3]; +}
--- a/src/geometry.h Thu Apr 14 11:08:20 2022 +0300 +++ b/src/geometry.h Mon May 16 01:40:49 2022 +0300 @@ -135,4 +135,22 @@ circle.radius * 2 }; } + + struct BezierCurve + { + glm::vec3 points[4]; + const glm::vec3& operator[](int x) const + { + Q_ASSERT(x >= 0 and x < 4); + return this->points[x]; + } + glm::vec3& operator[](int x) + { + Q_ASSERT(x >= 0 and x < 4); + return this->points[x]; + } + }; + + glm::vec3 pointOnCurve(const BezierCurve& curve, float t); + glm::vec3 derivativeOnCurve(const BezierCurve& curve, float t); }
--- a/src/tools/circletool.cpp Thu Apr 14 11:08:20 2022 +0300 +++ b/src/tools/circletool.cpp Mon May 16 01:40:49 2022 +0300 @@ -15,11 +15,35 @@ return tr("Draw circular primitives like circles or discs"); } +std::vector<glm::vec3> circle(int divisions) +{ + std::vector<glm::vec3> points; + points.reserve(divisions + 1); + for (int i = 0; i <= divisions; ++i) { + float ang = i * 2.0f * glm::pi<float>() / divisions; + points.push_back({std::sin(ang), std::cos(ang), 0.0f}); + } + return points; +} + void CircleTool::overpaint(Canvas *canvas, QPainter *painter) const { if (this->previewPolygon.size() >= 2) { - canvas->drawWorldPolyline() + for (int i : {0, 1}) { + canvas->drawWorldPoint(painter, this->previewPolygon[i]); + } + painter->setPen(QPen{Qt::green, 2, Qt::DashLine, Qt::RoundCap, Qt::MiterJoin}); + canvas->drawWorldPolyline(painter, {this->previewPolygon[0], this->previewPolygon[1]}); + const float size = glm::distance(this->previewPolygon[1], this->previewPolygon[0]); + glm::mat4 matrix = size * canvas->getGridMatrix(); + matrix[3] = {this->previewPolygon[0], 1}; + std::vector<glm::vec3> points = circle(16); + for (std::size_t i = 0; i < points.size(); ++i) { + points[i] = matrix * glm::vec4{points[i], 1.0f}; + } + painter->setPen(QPen{Qt::black, 2, Qt::DashLine, Qt::RoundCap, Qt::MiterJoin}); + canvas->drawWorldPolyline(painter, points); } }
--- a/src/ui/canvas.cpp Thu Apr 14 11:08:20 2022 +0300 +++ b/src/ui/canvas.cpp Mon May 16 01:40:49 2022 +0300 @@ -290,6 +290,11 @@ return this->selection; } +const glm::mat4 &Canvas::getGridMatrix() const +{ + return this->gridMatrix; +} + /** * @brief Paints a circle at where @c worldPoint is located on the screen. * @param painter Painter to use to render
--- a/src/ui/canvas.h Thu Apr 14 11:08:20 2022 +0300 +++ b/src/ui/canvas.h Mon May 16 01:40:49 2022 +0300 @@ -27,6 +27,7 @@ const std::optional<glm::vec3>& getWorldPosition() const; void adjustGridToView(); const QSet<ldraw::id_t> selectedObjects() const; + const glm::mat4& getGridMatrix() const; public Q_SLOTS: void handleSelectionChange(const QSet<ldraw::id_t>& selectedIds, const QSet<ldraw::id_t>& deselectedIds); void rebuildVertices(Document *document);