work on circle tool

Mon, 16 May 2022 01:40:49 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Mon, 16 May 2022 01:40:49 +0300
changeset 187
30204975694a
parent 186
922662adb72a
child 188
64ea7282611e

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);

mercurial