diff --git a/CMakeLists.txt b/CMakeLists.txt
index d9883bd9527..e84a95fd693 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -946,6 +946,7 @@ set(GUI_SOURCES
src/gui/LaunchingScreen.cc
src/gui/LibraryInfoDialog.cc
src/gui/MainWindow.cc
+ src/gui/Measurement.cc
src/gui/Animate.cc
src/gui/MouseSelector.cc
src/gui/OctoPrint.cc
diff --git a/resources/common.qrc.in b/resources/common.qrc.in
index 13af2ceb82d..9477cee75be 100644
--- a/resources/common.qrc.in
+++ b/resources/common.qrc.in
@@ -130,5 +130,9 @@
icons/svg-default/vcr-control-step-forward-white.svg
icons/svg-default/vcr-control-end.svg
icons/svg-default/vcr-control-end-white.svg
+ icons/svg-default/measure-dist.svg
+ icons/svg-default/measure-dist-white.svg
+ icons/svg-default/measure-ang.svg
+ icons/svg-default/measure-ang-white.svg
diff --git a/resources/icons/svg-default/icons.scad b/resources/icons/svg-default/icons.scad
index 7f6e068184e..8a4465b858d 100644
--- a/resources/icons/svg-default/icons.scad
+++ b/resources/icons/svg-default/icons.scad
@@ -67,6 +67,8 @@ icons = [
["vcr-control-pause"],
["vcr-control-step-forward"],
["vcr-control-end"],
+ ["measure-dist"],
+ ["measure-ang"]
];
icon(selected_icon) {
@@ -121,6 +123,8 @@ icon(selected_icon) {
vcr_control_pause();
vcr_control_step_forward();
vcr_control_end();
+ measure_dist();
+ measure_ang();
}
if (list_icons) {
@@ -733,3 +737,36 @@ module vcr_control_end(){
translate([x, 0]) square([thick, 0.4 * width], center = true);
}
}
+
+module measure_dist(){
+ x = 0.75 * width /2;
+ a = width*0.2;
+ t = thin*0.1;
+ offset(rounding)
+ translate([width/2,height/2]){
+ for(mirr=[1,0,0]) mirror([mirr,0,0]) {
+ translate([x, 0]) square([t, 0.8 * width], center = true);
+ polygon([[x-t,-15],[x-a-t,a-15],[x-a-t,-a-15]]);
+ }
+ translate([0,-15]) square([2*x, thin], center = true);
+ }
+ translate([25,50])
+ resize([40, 40], true)
+ text("10", 40, font = export_font);
+
+}
+
+module measure_ang() {
+ x = 0.75 * width /2;
+ a = width*0.2;
+ offset(rounding)
+ translate([width/2,height/2]){
+ for(mirr=[1,0,0]) mirror([mirr,0,0]) {
+ translate([0, -x]) rotate([0,0,45]) square([thin, 0.8 * width/sqrt(2)]);
+ }
+ }
+ translate([width*0.5,width*0.6]) scale(0.7) curved_arrow();
+ translate([30,30])
+ resize([40, 40], true)
+ text("45", 40, font = export_font);
+}
diff --git a/resources/icons/svg-default/measure-ang-white.svg b/resources/icons/svg-default/measure-ang-white.svg
new file mode 100644
index 00000000000..8d5040b523e
--- /dev/null
+++ b/resources/icons/svg-default/measure-ang-white.svg
@@ -0,0 +1,278 @@
+
+
+
diff --git a/resources/icons/svg-default/measure-ang.svg b/resources/icons/svg-default/measure-ang.svg
new file mode 100644
index 00000000000..29ab166499d
--- /dev/null
+++ b/resources/icons/svg-default/measure-ang.svg
@@ -0,0 +1,278 @@
+
+
+
diff --git a/resources/icons/svg-default/measure-dist-white.svg b/resources/icons/svg-default/measure-dist-white.svg
new file mode 100644
index 00000000000..84c2a500d5a
--- /dev/null
+++ b/resources/icons/svg-default/measure-dist-white.svg
@@ -0,0 +1,49 @@
+
+
+
diff --git a/resources/icons/svg-default/measure-dist.svg b/resources/icons/svg-default/measure-dist.svg
new file mode 100644
index 00000000000..c1085d69298
--- /dev/null
+++ b/resources/icons/svg-default/measure-dist.svg
@@ -0,0 +1,266 @@
+
+
+
diff --git a/src/core/Selection.h b/src/core/Selection.h
new file mode 100644
index 00000000000..df554793bca
--- /dev/null
+++ b/src/core/Selection.h
@@ -0,0 +1,38 @@
+/*
+ * OpenSCAD (www.openscad.org)
+ * Copyright (C) 2009-2011 Clifford Wolf and
+ * Marius Kintel
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * As a special exception, you have permission to link this program
+ * with the CGAL library and distribute executables, as long as you
+ * follow the requirements of the GNU GPL in regard to all of the
+ * software in the executable aside from CGAL.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#pragma once
+#include
+#include
+
+enum { SELECTION_POINT, SELECTION_LINE};
+
+struct SelectedObject {
+ int type;
+ Vector3d p1;
+ Vector3d p2;
+};
+
diff --git a/src/glview/GLView.cc b/src/glview/GLView.cc
index 645d2e955a3..bcde89fe22a 100644
--- a/src/glview/GLView.cc
+++ b/src/glview/GLView.cc
@@ -81,7 +81,7 @@ void GLView::setCamera(const Camera& cam)
this->cam = cam;
}
-void GLView::setupCamera() const
+void GLView::setupCamera()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
@@ -109,6 +109,10 @@ void GLView::setupCamera() const
glRotated(cam.object_rot.x(), 1.0, 0.0, 0.0);
glRotated(cam.object_rot.y(), 0.0, 1.0, 0.0);
glRotated(cam.object_rot.z(), 0.0, 0.0, 1.0);
+ glTranslated(cam.object_trans[0],cam.object_trans[1],cam.object_trans[2]); // translation be part of modelview matrix!
+ glGetDoublev(GL_MODELVIEW_MATRIX,this->modelview);
+ glTranslated(-cam.object_trans[0],-cam.object_trans[1],-cam.object_trans[2]);
+ glGetDoublev(GL_PROJECTION_MATRIX,this->projection);
}
void GLView::paintGL()
@@ -169,7 +173,15 @@ void GLView::paintGL()
this->renderer->prepare(showfaces, showedges);
this->renderer->draw(showfaces, showedges);
}
-
+ Vector3d eyedir(this->modelview[2],this->modelview[6],this->modelview[10]);
+ glColor3f(1,0,0);
+ for (const SelectedObject &obj:this->selected_obj) {
+ showObject(obj,eyedir);
+ }
+ glColor3f(0,1,0);
+ for (const SelectedObject obj: this->shown_obj) {
+ showObject(obj,eyedir);
+ }
glDisable(GL_LIGHTING);
if (showaxes) GLView::showSmallaxes(axescolor);
}
@@ -382,6 +394,49 @@ void GLView::showCrosshairs(const Color4f& col)
glEnd();
}
+void GLView::showObject(const SelectedObject &obj, const Vector3d &eyedir)
+{
+ auto vd = cam.zoomValue()/200.0;
+ switch(obj.type) {
+ case SELECTION_POINT:
+ {
+ double n=1/sqrt(3);
+ // create an octaeder
+ //x- x+ y- y+ z- z+
+ int sequence[]={ 2, 0, 4, 1, 2, 4, 0, 3, 4, 3, 1, 4, 0, 2, 5, 2, 1, 5, 3, 0, 5, 1, 3, 5 };
+ glBegin(GL_TRIANGLES);
+ for(int i=0;i<8;i++) {
+ glNormal3f((i&1)?-n:n,(i&2)?-n:n,(i&4)?-n:n);
+ for(int j=0;j<3;j++) {
+ int code=sequence[i*3+j];
+ switch(code) {
+ case 0: glVertex3d(obj.p1[0]-vd,obj.p1[1],obj.p1[2]); break;
+ case 1: glVertex3d(obj.p1[0]+vd,obj.p1[1],obj.p1[2]); break;
+ case 2: glVertex3d(obj.p1[0],obj.p1[1]-vd,obj.p1[2]); break;
+ case 3: glVertex3d(obj.p1[0],obj.p1[1]+vd,obj.p1[2]); break;
+ case 4: glVertex3d(obj.p1[0],obj.p1[1],obj.p1[2]-vd); break;
+ case 5: glVertex3d(obj.p1[0],obj.p1[1],obj.p1[2]+vd); break;
+ }
+ }
+ }
+ glEnd();
+ }
+ break;
+ case SELECTION_LINE:
+ {
+ Vector3d diff=obj.p2-obj.p1;
+ Vector3d wdir=eyedir.cross(diff).normalized()*vd/2.0;
+ glBegin(GL_QUADS);
+ glVertex3d(obj.p1[0]-wdir[0],obj.p1[1]-wdir[1],obj.p1[2]-wdir[2]);
+ glVertex3d(obj.p2[0]-wdir[0],obj.p2[1]-wdir[1],obj.p2[2]-wdir[2]);
+ glVertex3d(obj.p2[0]+wdir[0],obj.p2[1]+wdir[1],obj.p2[2]+wdir[2]);
+ glVertex3d(obj.p1[0]+wdir[0],obj.p1[1]+wdir[1],obj.p1[2]+wdir[2]);
+ glEnd();
+ }
+ break;
+ }
+}
+
void GLView::showScalemarkers(const Color4f& col)
{
// Add scale ticks on large axes
diff --git a/src/glview/GLView.h b/src/glview/GLView.h
index 53580a17973..7623c47a091 100644
--- a/src/glview/GLView.h
+++ b/src/glview/GLView.h
@@ -23,6 +23,8 @@
#include
#include "Camera.h"
#include "ColorMap.h"
+#include "system-gl.h"
+#include "Selection.h"
class GLView
{
@@ -36,7 +38,7 @@ class GLView
virtual void paintGL();
void setCamera(const Camera& cam);
- void setupCamera() const;
+ void setupCamera() ;
void setColorScheme(const ColorScheme& cs);
void setColorScheme(const std::string& cs);
@@ -69,6 +71,10 @@ class GLView
bool showedges;
bool showcrosshairs;
bool showscale;
+ GLdouble modelview[16];
+ GLdouble projection[16];
+ std::vector selected_obj;
+ std::vector shown_obj;
#ifdef ENABLE_OPENCSG
bool is_opencsg_capable;
@@ -77,6 +83,7 @@ class GLView
virtual void display_opencsg_warning() = 0;
int opencsg_id;
#endif
+ void showObject(const SelectedObject &pt,const Vector3d &eyedir);
private:
void showCrosshairs(const Color4f& col);
void showAxes(const Color4f& col);
diff --git a/src/glview/Renderer.cc b/src/glview/Renderer.cc
index bdc1a8ade55..da722a9e55b 100644
--- a/src/glview/Renderer.cc
+++ b/src/glview/Renderer.cc
@@ -441,6 +441,7 @@ void Renderer::render_edges(const PolySet& ps, csgmode_e csgmode) const
glEnable(GL_LIGHTING);
}
+std::vector Renderer::findModelObject(Vector3d near_pt, Vector3d far_pt, int mouse_x, int mouse_y, double tolerance) { return std::vector(); }
#else //NULLGL
Renderer::Renderer() : colorscheme(nullptr) {}
@@ -454,5 +455,6 @@ void Renderer::setColor(ColorMode colormode, const shaderinfo_t *shaderinfo) con
void Renderer::setColorScheme(const ColorScheme& cs) {}
void Renderer::render_surface(const PolySet& ps, csgmode_e csgmode, const Transform3d& m, const shaderinfo_t *shaderinfo) const {}
void Renderer::render_edges(const PolySet& ps, csgmode_e csgmode) const {}
+std::vector Renderer::findModelObject(Vector3d near_pt, Vector3d far_pt, int mouse_x, int mouse_y, double tolerance) { return std::vector(); }
#endif //NULLGL
diff --git a/src/glview/Renderer.h b/src/glview/Renderer.h
index 5d06bb42a6e..385252eeaab 100644
--- a/src/glview/Renderer.h
+++ b/src/glview/Renderer.h
@@ -4,6 +4,7 @@
#include "ColorMap.h"
#include "enums.h"
#include "PolySet.h"
+#include "Selection.h"
#ifdef _MSC_VER // NULL
#include
@@ -82,6 +83,7 @@ class Renderer
[[nodiscard]] virtual csgmode_e get_csgmode(const bool highlight_mode, const bool background_mode, const OpenSCADOperator type = OpenSCADOperator::UNION) const;
virtual void render_surface(const PolySet& geom, csgmode_e csgmode, const Transform3d& m, const shaderinfo_t *shaderinfo = nullptr) const;
virtual void render_edges(const PolySet& geom, csgmode_e csgmode) const;
+ virtual std::vector findModelObject(Vector3d near_pt, Vector3d far_pt, int mouse_x, int mouse_y, double tolerance);
protected:
std::map colormap;
diff --git a/src/glview/cgal/CGALRenderer.cc b/src/glview/cgal/CGALRenderer.cc
index c1a867c1a6e..0d5ec808bce 100644
--- a/src/glview/cgal/CGALRenderer.cc
+++ b/src/glview/cgal/CGALRenderer.cc
@@ -330,3 +330,78 @@ BoundingBox CGALRenderer::getBoundingBox() const
}
return bbox;
}
+double calculateLinePointDistance(const Vector3d &l1, const Vector3d &l2, const Vector3d &pt, double & dist_lat) {
+ Vector3d d = (l2 - l1).normalized();
+ dist_lat = (pt-l1).dot(d);
+ return (l1 + d * dist_lat-pt).norm();
+}
+
+double calculateLineLineDistance(const Vector3d &l1b, const Vector3d &l1e, const Vector3d &l2b, const Vector3d &l2e, double &dist_lat)
+{
+ double d;
+ Vector3d v1=l1e-l1b;
+ Vector3d v2=l2e-l2b;
+ Vector3d n=v1.cross(v2);
+ if(n.norm() == 0) {
+ return calculateLinePointDistance(l1b,l1e,l2b,d);
+ }
+ double t=n.norm();
+ n.normalize();
+ d=n.dot(l1b-l2b);
+ dist_lat=(v2.cross(n)).dot(l2b-l1b)/t;
+ return d;
+}
+
+std::vector CGALRenderer::findModelObject(Vector3d near_pt, Vector3d far_pt,int mouse_x, int mouse_y, double tolerance) {
+ std::vector results;
+ double dist_near;
+ double dist_nearest=NAN;
+ Vector3d pt1_nearest;
+ Vector3d pt2_nearest;
+ for (const std::shared_ptr& ps : this->polysets) {
+ for(const Vector3d &pt: ps->vertices) {
+ double dist_pt= calculateLinePointDistance(near_pt, far_pt, pt, dist_near);
+ if(dist_pt < tolerance ) {
+ if(isnan(dist_nearest) || dist_near < dist_nearest)
+ {
+ dist_nearest=dist_near;
+ pt1_nearest=pt;
+ }
+ }
+ }
+ }
+ if(!isnan(dist_nearest)) {
+ SelectedObject obj;
+ obj.type = SELECTION_POINT;
+ obj.p1=pt1_nearest;
+ results.push_back(obj);
+ return results;
+ }
+ for (const std::shared_ptr& ps : this->polysets) {
+ for(const auto &pol : ps->indices) {
+ int n = pol.size();
+ for(int i=0;i < n;i++ )
+ {
+ int ind1=pol[i];
+ int ind2=pol[(i+1)%n];
+ double dist_lat;
+ double dist_norm= fabs(calculateLineLineDistance(ps->vertices[ind1], ps->vertices[ind2], near_pt, far_pt,dist_lat));
+ if(dist_lat >= 0 && dist_lat <= 1 && dist_norm < tolerance ) {
+ dist_nearest=dist_lat;
+ pt1_nearest=ps->vertices[ind1];
+ pt2_nearest=ps->vertices[ind2];
+ }
+ }
+ }
+ }
+
+ if(!isnan(dist_nearest)) {
+ SelectedObject obj;
+ obj.type = SELECTION_LINE;
+ obj.p1=pt1_nearest;
+ obj.p2=pt2_nearest;
+ results.push_back(obj);
+ return results;
+ }
+ return results;
+}
diff --git a/src/glview/cgal/CGALRenderer.h b/src/glview/cgal/CGALRenderer.h
index 14234aece1a..2de56382f07 100644
--- a/src/glview/cgal/CGALRenderer.h
+++ b/src/glview/cgal/CGALRenderer.h
@@ -6,6 +6,7 @@
#ifdef ENABLE_CGAL
#include "CGAL_OGL_Polyhedron.h"
#include "CGAL_Nef_polyhedron.h"
+#include "Selection.h"
#endif
class CGALRenderer : public VBORenderer
@@ -17,6 +18,7 @@ class CGALRenderer : public VBORenderer
void draw(bool showfaces, bool showedges, const shaderinfo_t *shaderinfo = nullptr) const override;
void setColorScheme(const ColorScheme& cs) override;
BoundingBox getBoundingBox() const override;
+ std::vector findModelObject(Vector3d near_pt, Vector3d far_pt, int mouse_x, int mouse_y, double tolerance) override;
private:
void addGeometry(const std::shared_ptr& geom);
diff --git a/src/glview/system-gl.h b/src/glview/system-gl.h
index 443f2577c27..e729212a0ed 100644
--- a/src/glview/system-gl.h
+++ b/src/glview/system-gl.h
@@ -103,6 +103,7 @@ bool glCheckd(const char *stmt, const char *file, int line)
#define GLint int
#define GLuint unsigned int
+#define GLdouble unsigned int
inline void glColor4fv(float *c) {}
#endif // NULLGL
diff --git a/src/gui/MainWindow.cc b/src/gui/MainWindow.cc
index fdab8057de6..fde298e46d5 100644
--- a/src/gui/MainWindow.cc
+++ b/src/gui/MainWindow.cc
@@ -384,7 +384,9 @@ MainWindow::MainWindow(const QStringList& filenames)
QSettingsCached settings;
this->qglview->setMouseCentricZoom(Settings::Settings::mouseCentricZoom.value());
this->qglview->setMouseSwapButtons(Settings::Settings::mouseSwapButtons.value());
-
+ this->meas.setView(qglview);
+ this->designActionMeasureDist->setEnabled(false);
+ this->designActionMeasureAngle->setEnabled(false);
autoReloadTimer = new QTimer(this);
autoReloadTimer->setSingleShot(false);
@@ -459,6 +461,8 @@ MainWindow::MainWindow(const QStringList& filenames)
connect(this->designActionReloadAndPreview, SIGNAL(triggered()), this, SLOT(actionReloadRenderPreview()));
connect(this->designActionPreview, SIGNAL(triggered()), this, SLOT(actionRenderPreview()));
connect(this->designActionRender, SIGNAL(triggered()), this, SLOT(actionRender()));
+ connect(this->designActionMeasureDist, SIGNAL(triggered()), this, SLOT(actionMeasureDistance()));
+ connect(this->designActionMeasureAngle, SIGNAL(triggered()), this, SLOT(actionMeasureAngle()));
connect(this->designAction3DPrint, SIGNAL(triggered()), this, SLOT(action3DPrint()));
connect(this->designCheckValidity, SIGNAL(triggered()), this, SLOT(actionCheckValidity()));
connect(this->designActionDisplayAST, SIGNAL(triggered()), this, SLOT(actionDisplayAST()));
@@ -552,7 +556,8 @@ MainWindow::MainWindow(const QStringList& filenames)
connect(this->qglview, SIGNAL(cameraChanged()), animateWidget, SLOT(cameraChanged()));
connect(this->qglview, SIGNAL(cameraChanged()), viewportControlWidget, SLOT(cameraChanged()));
connect(this->qglview, SIGNAL(resized()), viewportControlWidget, SLOT(viewResized()));
- connect(this->qglview, SIGNAL(doSelectObject(QPoint)), this, SLOT(selectObject(QPoint)));
+ connect(this->qglview, SIGNAL(doRightClick(QPoint)), this, SLOT(rightClick(QPoint)));
+ connect(this->qglview, SIGNAL(doLeftClick(QPoint)), this, SLOT(leftClick(QPoint)));
connect(Preferences::inst(), SIGNAL(requestRedraw()), this->qglview, SLOT(update()));
connect(Preferences::inst(), SIGNAL(updateMouseCentricZoom(bool)), this->qglview, SLOT(setMouseCentricZoom(bool)));
@@ -616,6 +621,8 @@ MainWindow::MainWindow(const QStringList& filenames)
initActionIcon(viewActionPerspective, ":/icons/svg-default/perspective.svg", ":/icons/svg-default/perspective-white.svg");
initActionIcon(viewActionOrthogonal, ":/icons/svg-default/orthogonal.svg", ":/icons/svg-default/orthogonal-white.svg");
initActionIcon(designActionPreview, ":/icons/svg-default/preview.svg", ":/icons/svg-default/preview-white.svg");
+ initActionIcon(designActionMeasureDist, ":/icons/svg-default/measure-dist.svg", ":/icons/svg-default/measure-dist-white.svg");
+ initActionIcon(designActionMeasureAngle, ":/icons/svg-default/measure-ang.svg", ":/icons/svg-default/measure-ang-white.svg");
initActionIcon(fileActionExportSTL, ":/icons/svg-default/export-stl.svg", ":/icons/svg-default/export-stl-white.svg");
initActionIcon(fileActionExportAMF, ":/icons/svg-default/export-amf.svg", ":/icons/svg-default/export-amf-white.svg");
initActionIcon(fileActionExport3MF, ":/icons/svg-default/export-3mf.svg", ":/icons/svg-default/export-3mf-white.svg");
@@ -697,6 +704,7 @@ MainWindow::MainWindow(const QStringList& filenames)
connect(this->animateDock, SIGNAL(topLevelChanged(bool)), this, SLOT(animateTopLevelChanged(bool)));
connect(this->viewportControlDock, SIGNAL(topLevelChanged(bool)), this, SLOT(viewportControlTopLevelChanged(bool)));
+ connect(this->activeEditor, SIGNAL(escapePressed()), this, SLOT(measureFinished()));
// display this window and check for OpenGL 2.0 (OpenCSG) support
viewModeThrownTogether();
show();
@@ -2007,6 +2015,9 @@ void MainWindow::actionRenderPreview()
GuiLocker::lock();
preview_requested = false;
+ this->designActionMeasureDist->setEnabled(false);
+ this->designActionMeasureAngle->setEnabled(false);
+
prepareCompile("csgRender", windowActionHideAnimate->isChecked(), true);
compile(false, false);
if (preview_requested) {
@@ -2230,6 +2241,9 @@ void MainWindow::actionRender()
if (GuiLocker::isLocked()) return;
GuiLocker::lock();
+ this->designActionMeasureDist->setEnabled(true);
+ this->designActionMeasureAngle->setEnabled(true);
+
prepareCompile("cgalRender", true, false);
compile(false);
}
@@ -2297,12 +2311,34 @@ void MainWindow::actionRenderDone(const std::shared_ptr& root_ge
compileEnded();
}
+void MainWindow::actionMeasureDistance()
+{
+ meas.startMeasureDist();
+}
+
+void MainWindow::actionMeasureAngle()
+{
+ meas.startMeasureAngle();
+}
+
+void MainWindow::leftClick(QPoint mouse)
+{
+ QString str = meas.statemachine(mouse);
+ if(str.size() > 0) {
+ this->qglview->measure_state = MEASURE_IDLE;
+ QMenu resultmenu(this);
+ auto action = resultmenu.addAction(str);
+ connect(action, SIGNAL(triggered()), this, SLOT(measureFinished()));
+ resultmenu.exec(qglview->mapToGlobal(mouse));
+ }
+}
+
/**
* Call the mouseselection to determine the id of the clicked-on object.
* Use the generated ID and try to find it within the list of products
* And finally move the cursor to the beginning of the selected object in the editor
*/
-void MainWindow::selectObject(QPoint mouse)
+void MainWindow::rightClick(QPoint mouse)
{
// selecting without a renderer?!
if (!this->qglview->renderer) {
@@ -2373,6 +2409,13 @@ void MainWindow::selectObject(QPoint mouse)
tracemenu.exec(this->qglview->mapToGlobal(mouse));
}
}
+void MainWindow::measureFinished(void)
+{
+ this->qglview->selected_obj.clear();
+ this->qglview->shown_obj.clear();
+ this->qglview->update();
+ this->qglview->measure_state = MEASURE_IDLE;
+}
/**
* Expects the sender to have properties "file", "line" and "column" defined
diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h
index 29c6e7ae71d..30ab1f7976f 100644
--- a/src/gui/MainWindow.h
+++ b/src/gui/MainWindow.h
@@ -4,6 +4,7 @@
#include "Geometry.h"
#include "export.h"
#include "ExportPdfDialog.h"
+#include "Measurement.h"
#include "RenderStatistic.h"
#include "TabManager.h"
#include "Tree.h"
@@ -86,6 +87,8 @@ class MainWindow : public QMainWindow, public Ui::MainWindow, public InputEventH
QWidget *animateDockTitleWidget;
QWidget *viewportControlTitleWidget;
+ Measurement meas;
+
int compileErrors;
int compileWarnings;
@@ -109,6 +112,7 @@ private slots:
void openCSGSettingsChanged();
void consoleOutput(const Message& msgObj);
void setCursor();
+ void measureFinished();
void errorLogOutput(const Message& log_msg);
public:
@@ -240,6 +244,8 @@ private slots:
void actionRender();
void actionRenderDone(const std::shared_ptr&);
void cgalRender();
+ void actionMeasureDistance();
+ void actionMeasureAngle();
void actionCheckValidity();
void actionDisplayAST();
void actionDisplayCSGTree();
@@ -329,7 +335,8 @@ public slots:
void viewResetView();
void viewAll();
void editorContentChanged();
- void selectObject(QPoint coordinate);
+ void leftClick(QPoint coordinate);
+ void rightClick(QPoint coordinate);
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
void helpAbout();
diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui
index 28c6b3adaea..9665877d1d8 100644
--- a/src/gui/MainWindow.ui
+++ b/src/gui/MainWindow.ui
@@ -174,6 +174,8 @@
+
+
@@ -310,6 +312,8 @@
+
+
@@ -983,6 +987,22 @@
F8
+
+
+ Measure &Distance
+
+
+ d
+
+
+
+
+ Measure &Angle
+
+
+ a
+
+
&Check Validity
diff --git a/src/gui/Measurement.cc b/src/gui/Measurement.cc
new file mode 100644
index 00000000000..8abc6936db3
--- /dev/null
+++ b/src/gui/Measurement.cc
@@ -0,0 +1,148 @@
+/*
+ * OpenSCAD (www.openscad.org)
+ * Copyright (C) 2009-2011 Clifford Wolf and
+ * Marius Kintel
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * As a special exception, you have permission to link this program
+ * with the CGAL library and distribute executables, as long as you
+ * follow the requirements of the GNU GPL in regard to all of the
+ * software in the executable aside from CGAL.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "Measurement.h"
+
+Measurement::Measurement()
+{
+}
+
+void Measurement::setView(QGLView *qglview) {
+ this->qglview=qglview;
+ this->qglview->measure_state = MEASURE_IDLE;
+}
+
+void Measurement::startMeasureDist(void)
+{
+ this->qglview->selected_obj.clear();
+ this->qglview->update();
+ this->qglview->measure_state=MEASURE_DIST1;
+}
+
+void Measurement::startMeasureAngle(void)
+{
+ this->qglview->selected_obj.clear();
+ this->qglview->update();
+ this->qglview->measure_state=MEASURE_ANG1;
+}
+QString Measurement::statemachine(QPoint mouse)
+{
+ if(qglview->measure_state == MEASURE_IDLE) return "";
+ qglview->selectPoint(mouse.x(),mouse.y());
+ double ang=NAN;
+ double dist=NAN;
+ SelectedObject obj1, obj2, obj3;
+ switch(qglview->measure_state) {
+ case MEASURE_DIST1:
+ if(qglview->selected_obj.size() == 1) qglview->measure_state = MEASURE_DIST2;
+ break;
+ case MEASURE_DIST2:
+ if(qglview->selected_obj.size() == 2)
+ {
+ double lat;
+ obj1=qglview->selected_obj[0];
+ obj2=qglview->selected_obj[1];
+ if(obj1.type == SELECTION_POINT && obj2.type == SELECTION_POINT) dist =(obj2.p1-obj1.p1).norm();
+ if(obj1.type == SELECTION_POINT && obj2.type == SELECTION_LINE) dist =calculateLinePointDistance(obj2.p1, obj2.p2,obj1.p1,lat);
+ if(obj1.type == SELECTION_LINE && obj2.type == SELECTION_POINT) dist =calculateLinePointDistance(obj1.p1, obj1.p2,obj2.p1,lat);
+ if(obj1.type == SELECTION_LINE && obj2.type == SELECTION_LINE) dist =calculateLineLineDistance(obj1.p1, obj1.p2,obj2.p1,obj2.p2,lat);
+ if(!std::isnan(dist)) {
+ return QString("Distance is %1").arg(fabs(dist));
+ std::stringstream ss;
+ }
+ qglview->selected_obj.clear();
+ qglview->shown_obj.clear();
+ qglview->update();
+ qglview->measure_state = MEASURE_IDLE;
+ }
+ break;
+ case MEASURE_ANG1:
+ if(qglview->selected_obj.size() == 1) qglview->measure_state = MEASURE_ANG2;
+ break;
+ case MEASURE_ANG2:
+ if(qglview->selected_obj.size() == 2)
+ {
+ obj1=qglview->selected_obj[0];
+ obj2=qglview->selected_obj[1];
+ Vector3d side1, side2;
+ if(obj1.type == SELECTION_LINE && obj2.type == SELECTION_POINT)
+ {
+ side1=(obj1.p2-obj1.p1).normalized();
+ side2=(obj1.p2-obj2.p1).normalized();
+ ang=acos(side1.dot(side2))*180.0/3.14159265359;
+ goto display_angle;
+ }
+ else if(obj1.type == SELECTION_POINT && obj2.type == SELECTION_LINE)
+ {
+ side1=(obj2.p2-obj2.p1).normalized();
+ side2=(obj2.p2-obj1.p1).normalized();
+ ang=acos(side1.dot(side2))*180.0/3.14159265359;
+ goto display_angle;
+ }
+ else if(obj1.type == SELECTION_LINE && obj2.type == SELECTION_LINE)
+ {
+ if(obj1.p2 == obj2.p1) {
+ side1=(obj2.p1-obj1.p1).normalized();
+ side2=(obj2.p1-obj2.p2).normalized();
+ }
+ else if(obj2.p2 == obj1.p1) {
+ side1=(obj1.p1-obj2.p1).normalized();
+ side2=(obj1.p1-obj1.p2).normalized();
+ } else {
+ side1=(obj1.p2-obj1.p1).normalized();
+ side2=(obj2.p2-obj2.p1).normalized();
+ }
+ ang=acos(side1.dot(side2))*180.0/3.14159265359;
+ goto display_angle;
+ } else
+ qglview->measure_state = MEASURE_ANG3;
+ }
+ break;
+ case MEASURE_ANG3:
+ if(qglview->selected_obj.size() == 3){
+ obj1=qglview->selected_obj[0];
+ obj2=qglview->selected_obj[1];
+ obj3=qglview->selected_obj[2];
+ if(obj1.type == SELECTION_POINT && obj2.type == SELECTION_POINT && obj3.type == SELECTION_POINT)
+ {
+ Vector3d side1=(obj2.p1-obj1.p1).normalized();
+ Vector3d side2=(obj2.p1-obj3.p1).normalized();
+ ang=acos(side1.dot(side2))*180.0/3.14159265359;
+ }
+display_angle:
+ if(!std::isnan(ang))
+ {
+ return QString("Angle is %1 Degrees").arg(ang);
+ }
+ qglview->selected_obj.clear();
+ qglview->shown_obj.clear();
+ qglview->update();
+ qglview->measure_state = MEASURE_IDLE;
+ }
+ break;
+ }
+ return "";
+}
diff --git a/src/gui/Measurement.h b/src/gui/Measurement.h
new file mode 100644
index 00000000000..d30f5d9a975
--- /dev/null
+++ b/src/gui/Measurement.h
@@ -0,0 +1,21 @@
+
+#pragma once
+
+#include "QGLView.h"
+
+enum { MEASURE_IDLE, MEASURE_DIST1, MEASURE_DIST2, MEASURE_ANG1, MEASURE_ANG2, MEASURE_ANG3 };
+
+extern double calculateLinePointDistance(const Vector3d &l1, const Vector3d &l2, const Vector3d &pt, double & dist_lat) ;
+extern double calculateLineLineDistance(const Vector3d &l1b, const Vector3d &l1e, const Vector3d &l2b, const Vector3d &l2e, double &dist_lat);
+
+class Measurement
+{
+ public:
+ Measurement(void);
+ void setView(QGLView *qglview);
+ QString statemachine(QPoint mouse);
+ void startMeasureDist(void);
+ void startMeasureAngle(void);
+ private:
+ QGLView *qglview;
+};
diff --git a/src/gui/QGLView.cc b/src/gui/QGLView.cc
index 484356f1767..f0757cffc39 100644
--- a/src/gui/QGLView.cc
+++ b/src/gui/QGLView.cc
@@ -57,6 +57,7 @@
#endif
#include "qt-obsolete.h"
+#include "Measurement.h"
QGLView::QGLView(QWidget *parent) : QOpenGLWidget(parent)
{
@@ -286,6 +287,11 @@ void QGLView::normalizeAngle(GLdouble& angle)
void QGLView::mouseMoveEvent(QMouseEvent *event)
{
auto this_mouse = event->globalPos();
+ if(measure_state != MEASURE_IDLE) {
+ QPoint pt = event->pos();
+ this->shown_obj = findObject(pt.x(), pt.y());
+ update();
+ }
double dx = (this_mouse.x() - last_mouse.x()) * 0.7;
double dy = (this_mouse.y() - last_mouse.y()) * 0.7;
if (mouse_drag_active) {
@@ -337,12 +343,17 @@ void QGLView::mouseReleaseEvent(QMouseEvent *event)
mouse_drag_active = false;
releaseMouse();
- auto button_compare = this->mouseSwapButtons?Qt::LeftButton : Qt::RightButton;
- if (!mouse_drag_moved
- && (event->button() == button_compare)) {
- QPoint point = event->pos();
- //point.setY(this->height() - point.y());
- emit doSelectObject(point);
+ auto button_right = this->mouseSwapButtons?Qt::LeftButton : Qt::RightButton;
+ auto button_left = this->mouseSwapButtons?Qt::RightButton : Qt::LeftButton;
+ if (!mouse_drag_moved) {
+ if(event->button() == button_right) {
+ QPoint point = event->pos();
+ emit doRightClick(point);
+ }
+ if(event->button() == button_left) {
+ QPoint point = event->pos();
+ emit doLeftClick(point);
+ }
}
mouse_drag_moved = false;
}
@@ -510,3 +521,36 @@ void QGLView::rotate2(double x, double y, double z)
update();
emit cameraChanged();
}
+
+std::vector QGLView::findObject(int mouse_x,int mouse_y)
+{
+ int viewport[4]={0,0,0,0};
+ double posXF, posYF, posZF;
+ double posXN, posYN, posZN;
+ viewport[2]=size().rwidth();
+ viewport[3]=size().rheight();
+
+ GLdouble winX = mouse_x;
+ GLdouble winY = viewport[3] - mouse_y;
+
+ gluUnProject(winX, winY, 1, this->modelview, this->projection, viewport,&posXF, &posYF, &posZF);
+ gluUnProject(winX, winY, -1, this->modelview, this->projection, viewport,&posXN, &posYN, &posZN);
+ Vector3d far_pt(posXF, posYF, posZF);
+ Vector3d near_pt(posXN, posYN, posZN);
+ Vector3d eyedir=far_pt-near_pt;
+
+ Vector3d testpt(0,0,0);
+
+ auto renderer = this->getRenderer();
+ return renderer->findModelObject(near_pt, far_pt, mouse_x, mouse_y, cam.zoomValue()/300);
+
+}
+
+void QGLView::selectPoint(int mouse_x, int mouse_y)
+{
+ std::vector obj= findObject(mouse_x, mouse_y);
+ if(obj.size() == 1) {
+ this->selected_obj.push_back(obj[0]);
+ update();
+ }
+}
diff --git a/src/gui/QGLView.h b/src/gui/QGLView.h
index a602cd6cc19..c943130b2b0 100644
--- a/src/gui/QGLView.h
+++ b/src/gui/QGLView.h
@@ -36,6 +36,9 @@ class QGLView : public QOpenGLWidget, public GLView
bool save(const char *filename) const override;
void resetView();
void viewAll();
+ void selectPoint(int x, int y);
+ std::vector findObject(int x, int y);
+ int measure_state;
public slots:
void ZoomIn();
@@ -88,7 +91,8 @@ private slots:
signals:
void cameraChanged();
void resized();
- void doSelectObject(QPoint screen_coordinate);
+ void doRightClick(QPoint screen_coordinate);
+ void doLeftClick(QPoint screen_coordinate);
};
/* These are defined in QLGView2.cc. See the commentary there. */
diff --git a/src/gui/ScintillaEditor.cc b/src/gui/ScintillaEditor.cc
index 741a299bf22..c20956a2e41 100644
--- a/src/gui/ScintillaEditor.cc
+++ b/src/gui/ScintillaEditor.cc
@@ -911,6 +911,12 @@ QString ScintillaEditor::selectedText()
bool ScintillaEditor::eventFilter(QObject *obj, QEvent *e)
{
+ if (e->type() == QEvent::KeyPress) {
+ auto keyEvent = static_cast(e);
+ if (keyEvent->key() == Qt::Key_Escape) {
+ emit escapePressed();
+ }
+ }
if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) || QGuiApplication::keyboardModifiers().testFlag(Qt::AltModifier)) {
if (!this->indicatorsActive) {
this->indicatorsActive = true;
diff --git a/src/gui/ScintillaEditor.h b/src/gui/ScintillaEditor.h
index b65909282aa..477c87bb1bf 100644
--- a/src/gui/ScintillaEditor.h
+++ b/src/gui/ScintillaEditor.h
@@ -146,6 +146,8 @@ private slots:
void fireModificationChanged();
void onIndicatorClicked(int line, int col, Qt::KeyboardModifiers state);
void onIndicatorReleased(int line, int col, Qt::KeyboardModifiers state);
+signals:
+ void escapePressed(void);
public:
void public_applySettings();