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 @@ + + + +OpenSCAD Model + + 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 @@ + + + +OpenSCAD Model + + 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 @@ + + + +OpenSCAD Model + + 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 @@ + + + +OpenSCAD Model + + 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();