]> git.sur5r.net Git - fstl/commitdiff
New upstream version 0.11.0
authorJakob Haufe <sur5r@debian.org>
Mon, 31 Mar 2025 10:42:16 +0000 (12:42 +0200)
committerJakob Haufe <sur5r@debian.org>
Mon, 7 Apr 2025 14:52:08 +0000 (16:52 +0200)
26 files changed:
CMakeLists.txt
gl/gl.qrc
gl/mesh_light.frag [new file with mode: 0644]
qt/icons/fstl_64x64.png [new file with mode: 0644]
qt/qt.qrc
src/app.cpp
src/canvas.cpp
src/canvas.h
src/loader.cpp
src/main.cpp
src/shaderlightprefs.cpp [new file with mode: 0644]
src/shaderlightprefs.h [new file with mode: 0644]
src/window.cpp
src/window.h
xdg/README.txt [new file with mode: 0644]
xdg/fstlapp-fstl.desktop [new file with mode: 0644]
xdg/icons/fstlapp-fstl_128x128.png [new file with mode: 0644]
xdg/icons/fstlapp-fstl_16x16.png [new file with mode: 0644]
xdg/icons/fstlapp-fstl_22x22.png [new file with mode: 0644]
xdg/icons/fstlapp-fstl_256x256.png [new file with mode: 0644]
xdg/icons/fstlapp-fstl_32x32.png [new file with mode: 0644]
xdg/icons/fstlapp-fstl_48x48.png [new file with mode: 0644]
xdg/icons/fstlapp-fstl_64x64.png [new file with mode: 0644]
xdg/xdg_install.sh [new file with mode: 0755]
xdg/xdg_package_install.sh [new file with mode: 0755]
xdg/xdg_uninstall.sh [new file with mode: 0755]

index 0628afa4479e189e46941453ce4548c1de78fa90..fe586c747b66cf59d6825f15fd834b72811651b1 100644 (file)
@@ -1,6 +1,6 @@
 #### Fast .stl viewer #####
-# Original Project Author: Matt Keeter Copyright 2014 -2017
-# Author: Paul Tsouchlos Copyright 2017
+# Original Project Author: Matt Keeter Copyright 2014-2024
+# Author: Paul Tsouchlos Copyright 2017-2024
 
 cmake_minimum_required(VERSION 3.3)
 
@@ -13,7 +13,7 @@ set(CXX_STANDARD_REQUIRED ON)
 
 # Set the version number
 set (FSTL_VERSION_MAJOR "0")
-set (FSTL_VERSION_MINOR "10")
+set (FSTL_VERSION_MINOR "11")
 set (FSTL_VERSION_PATCH "0")
 set (PROJECT_VERSION "${FSTL_VERSION_MAJOR}.${FSTL_VERSION_MINOR}.${FSTL_VERSION_PATCH}")
 
@@ -32,7 +32,8 @@ src/glmesh.cpp
 src/loader.cpp
 src/main.cpp
 src/mesh.cpp
-src/window.cpp)
+src/window.cpp
+src/shaderlightprefs.cpp)
 
 #set project headers. 
 set(Project_Headers src/app.h
@@ -42,7 +43,8 @@ src/canvas.h
 src/glmesh.h
 src/loader.h
 src/mesh.h
-src/window.h)
+src/window.h
+src/shaderlightprefs.h)
 
 #set project resources and icon resource
 set(Project_Resources qt/qt.qrc gl/gl.qrc)
index 6e8baa90cd9b64fa52a89c0f0eae0a482915767c..a80653070ed5263ae55be14c4f11f1e63012759d 100644 (file)
--- a/gl/gl.qrc
+++ b/gl/gl.qrc
@@ -4,6 +4,7 @@
         <file>mesh.vert</file>
         <file>mesh_wireframe.frag</file>
         <file>mesh_surfaceangle.frag</file>
+        <file>mesh_light.frag</file>
         <file>quad.frag</file>
         <file>quad.vert</file>
         <file>colored_lines.frag</file>
diff --git a/gl/mesh_light.frag b/gl/mesh_light.frag
new file mode 100644 (file)
index 0000000..31d43bb
--- /dev/null
@@ -0,0 +1,26 @@
+#version 120
+
+uniform float zoom;
+uniform vec4 ambient_light_color;
+uniform vec4 directive_light_color;
+uniform vec3 directive_light_direction;
+
+varying vec3 ec_pos;
+
+void main() {
+    // Normalize light direction
+    vec3 dir = normalize(directive_light_direction);
+
+    // vec3 a = vec3(0.0, 1.0, 1.0);
+    // normal vector
+    vec3 ec_normal = normalize(cross(dFdx(ec_pos), dFdy(ec_pos)));
+    ec_normal.z *= zoom;
+    ec_normal = normalize(ec_normal);
+
+
+    vec3 color =  ambient_light_color.w * ambient_light_color.xyz + directive_light_color.w * dot(ec_normal,dir) * directive_light_color.xyz;
+
+    // float coef = dot(ec_normal,dir);
+    // vec3 color = coef * lightcolor + (1.0 - coef) * objectcolor;
+    gl_FragColor = vec4(color, 1.0);
+}
diff --git a/qt/icons/fstl_64x64.png b/qt/icons/fstl_64x64.png
new file mode 100644 (file)
index 0000000..2a63e41
Binary files /dev/null and b/qt/icons/fstl_64x64.png differ
index 883ac14cb3ab42d6eb3092284335d2b6d6cc2db8..f2b1266f4b8e5ffe920a08e439cc585d12bf42fe 100644 (file)
--- a/qt/qt.qrc
+++ b/qt/qt.qrc
@@ -1,5 +1,6 @@
 <RCC>
-    <qresource prefix="qt/">
+    <qresource prefix="/qt">
         <file>style.qss</file>
+        <file>icons/fstl_64x64.png</file>
     </qresource>
 </RCC>
index a76a7dafdc9d11f25bc60d5fc4178d827ec11882..07de99e59447026fa4285752ab53d1a6261ed13c 100644 (file)
@@ -1,5 +1,6 @@
 #include <QDebug>
 #include <QFileOpenEvent>
+#include <QDir>
 
 #include "app.h"
 #include "window.h"
@@ -7,10 +8,17 @@
 App::App(int& argc, char *argv[]) :
     QApplication(argc, argv), window(new Window())
 {
-    if (argc > 1)
-        window->load_stl(argv[1]);
+    if (argc > 1) {
+        QString filename = argv[1];
+        if (filename.startsWith("~")) {
+            filename.replace(0, 1, QDir::homePath());
+        }
+        window->load_stl(filename);
+    }
     else
+    {
         window->load_stl(":gl/sphere.stl");
+    }
     window->show();
 }
 
index 4da41a7e0cc17c8e049182fdb2c287205c2a7c9d..ad4ec37b63f91c4093f1e234e476ddeb15e32ea8 100644 (file)
 const float Canvas::P_PERSPECTIVE = 0.25f;
 const float Canvas::P_ORTHOGRAPHIC = 0.0f;
 
+const QString Canvas::AMBIENT_COLOR = "ambientColor";
+const QString Canvas::AMBIENT_FACTOR = "ambientFactor";
+const QString Canvas::DIRECTIVE_COLOR = "directiveColor";
+const QString Canvas::DIRECTIVE_FACTOR = "directiveFactor";
+const QString Canvas::CURRENT_LIGHT_DIRECTION = "currentLightDirection";
+
+const QColor Canvas::defaultAmbientColor = QColor::fromRgbF(0.22,0.8,1.0);
+const QColor Canvas::defaultDirectiveColor = QColor(255,255,255);
+const float Canvas::defaultAmbientFactor = 0.67;
+const float Canvas::defaultDirectiveFactor = 0.5;
+const int Canvas::defaultCurrentLightDirection = 1;
+
 Canvas::Canvas(const QSurfaceFormat& format, QWidget *parent)
     : QOpenGLWidget(parent), mesh(nullptr),
-      scale(1), zoom(1), tilt(90), yaw(0),
+      scale(1), zoom(1),
       anim(this, "perspective"), status(" "),
       meshInfo("")
 {
@@ -21,6 +33,38 @@ Canvas::Canvas(const QSurfaceFormat& format, QWidget *parent)
     QFile styleFile(":/qt/style.qss");
     styleFile.open( QFile::ReadOnly );
     setStyleSheet(styleFile.readAll());
+    currentTransform = QMatrix4x4();
+    resetTransform();
+
+    QSettings settings;
+    ambientColor = settings.value(AMBIENT_COLOR,defaultAmbientColor).value<QColor>();
+    directiveColor = settings.value(DIRECTIVE_COLOR,defaultDirectiveColor).value<QColor>();
+    ambientFactor = settings.value(AMBIENT_FACTOR,defaultAmbientFactor).value<float>();
+    directiveFactor = settings.value(DIRECTIVE_FACTOR,defaultDirectiveFactor).value<float>();
+
+    // Fill direction list
+    // Fill in directions
+    nameDir.clear();
+    listDir.clear();
+    QList<QString> xname, yname, zname;
+    xname << "right " << " " << "left ";
+    yname << "top " << " " << "bottom ";
+    zname << "rear " << " " << "front ";
+    for (int i=-1; i<2 ; i++) {
+        for (int j=-1; j<2; j++) {
+            for (int k=-1; k<2; k++) {
+                QString current = xname.at(i+1) + yname.at(j+1) + zname.at(k+1);
+                if (!(i==0 && j==0 && k==0)) {
+                    nameDir << current.simplified();
+                    listDir << QVector3D((double)i,(double)j,(double)k);
+                }
+            }
+        }
+    }
+    currentLightDirection = settings.value(CURRENT_LIGHT_DIRECTION,defaultCurrentLightDirection).value<int>();
+    if (currentLightDirection < 0 || currentLightDirection >= nameDir.length()) {
+        currentLightDirection = defaultCurrentLightDirection;
+    }
 
     anim.setDuration(100);
 }
@@ -42,6 +86,65 @@ void Canvas::view_anim(float v)
     anim.start();
 }
 
+void Canvas::common_view_change(enum ViewPoint c)
+{
+    if (c == centerview) {
+        scale = default_scale;
+        center = default_center;
+        zoom = 1;
+        update();
+        return;
+    }
+
+    currentTransform.setToIdentity();
+    currentTransform.rotate(180.0, QVector3D(0, 0, 1));
+
+    switch (c)
+    {
+    case isoview:
+        {
+            currentTransform.rotate(90, QVector3D(1, 0, 0));
+            currentTransform.rotate(-45, QVector3D(0, 0, 1));
+            currentTransform.rotate(35.264, QVector3D(1, 1, 0));
+        }
+        break;
+    case topview:
+        {
+            currentTransform.rotate(180, QVector3D(1, 0, 0));
+        }
+        break;
+    case leftview:
+        {
+            currentTransform.rotate(180, QVector3D(1, 0, 0));
+            currentTransform.rotate(90, QVector3D(0, 0, 1));
+            currentTransform.rotate(90, QVector3D(0, 1, 0));
+        }
+        break;
+    case rightview:
+        {
+            currentTransform.rotate(180, QVector3D(1, 0, 0));
+            currentTransform.rotate(-90.0, QVector3D(0, 1, 0));
+            currentTransform.rotate(-90, QVector3D(1, 0, 0));
+        }
+        break;
+    case frontview:
+        {
+            currentTransform.rotate(90, QVector3D(1, 0, 0));
+        }
+        break;
+    case backview:
+        {
+            currentTransform.rotate(90, QVector3D(1, 0, 0));
+            currentTransform.rotate(180, QVector3D(0, 0, 1));
+        }
+    case bottomview:
+        [[fallthrough]];
+    default:
+        break;
+    }
+    update();
+}
+
 void Canvas::view_perspective(float p, bool animate){
     if(animate)
     {
@@ -65,6 +168,20 @@ void Canvas::invert_zoom(bool d)
     update();
 }
 
+void Canvas::setResetTransformOnLoad(bool d) {
+    resetTransformOnLoad = d;
+}
+
+void Canvas::resetTransform() {
+    currentTransform.setToIdentity();
+    // apply some rotations to define initial orientation
+    currentTransform.rotate(-90.0, QVector3D(1, 0, 0));
+    currentTransform.rotate(180.0 + 15.0, QVector3D(0, 0, 1));
+    currentTransform.rotate(15.0, QVector3D(1, -sin(M_PI/12), 0));
+    
+    zoom = 1;
+}
+
 void Canvas::load_mesh(Mesh* m, bool is_reload)
 {
     delete mesh;
@@ -73,13 +190,14 @@ void Canvas::load_mesh(Mesh* m, bool is_reload)
     QVector3D upper(m->xmax(), m->ymax(), m->zmax());
     if (!is_reload)
     {
-        center = (lower + upper) / 2;
-        scale = 2 / (upper - lower).length();
+        default_center = center = (lower + upper) / 2;
+        default_scale = scale = 2 / (upper - lower).length();
 
         // Reset other camera parameters
         zoom = 1;
-        yaw = 0;
-        tilt = 90;
+        if (resetTransformOnLoad) {
+            resetTransform();
+        }
     }
     meshInfo = QStringLiteral("Triangles: %1\nX: [%2, %3]\nY: [%4, %5]\nZ: [%6, %7]").arg(m->triCount());
     for(int dIdx = 0; dIdx < 3; dIdx++) meshInfo = meshInfo.arg(lower[dIdx]).arg(upper[dIdx]);
@@ -128,6 +246,9 @@ void Canvas::initializeGL()
     mesh_surfaceangle_shader.addShader(mesh_vertshader);
     mesh_surfaceangle_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh_surfaceangle.frag");
     mesh_surfaceangle_shader.link();
+    mesh_meshlight_shader.addShader(mesh_vertshader);
+    mesh_meshlight_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh_light.frag");
+    mesh_meshlight_shader.link();
 
     backdrop = new Backdrop();
     axis = new Axis();
@@ -165,10 +286,14 @@ void Canvas::draw_mesh()
         {
             selected_mesh_shader = &mesh_shader;
         }
-        else
+        else if (drawMode == surfaceangle)
         {
             selected_mesh_shader = &mesh_surfaceangle_shader;
         }
+        else if (drawMode == meshlight)
+        {
+            selected_mesh_shader = &mesh_meshlight_shader;
+        }
         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
     }
 
@@ -185,6 +310,28 @@ void Canvas::draw_mesh()
     // Compensate for z-flattening when zooming
     glUniform1f(selected_mesh_shader->uniformLocation("zoom"), 1/zoom);
 
+    // specific meshlight arguments
+    if (drawMode == meshlight) {
+        // Ambient Light Color, followed by the ambient light coefficient to use
+        //glUniform4f(selected_mesh_shader->uniformLocation("ambient_light_color"),0.22f, 0.8f, 1.0f, 0.67f);
+        glUniform4f(selected_mesh_shader->uniformLocation("ambient_light_color"),ambientColor.redF(), ambientColor.greenF(), ambientColor.blueF(), ambientFactor);
+        // Directive Light Color, followed by the directive light coefficient to use
+        //glUniform4f(selected_mesh_shader->uniformLocation("directive_light_color"),1.0f,1.0f,1.0f,0.5f);
+        glUniform4f(selected_mesh_shader->uniformLocation("directive_light_color"),directiveColor.redF(),directiveColor.greenF(),directiveColor.blueF(),directiveFactor);
+
+        // Directive Light Direction
+        // dir 1,0,0  Light from the left
+        // dir -1,0,0 Light from the right
+        // dir 0,1,0  Light from bottom
+        // dir 0,-1,0 Light from top
+        // dir 0,0,1  Light from viewer (front)
+        // dir 0,0,-1 Light from behind
+        //
+        // -1,-1,0 Light from top right
+        //glUniform3f(selected_mesh_shader->uniformLocation("directive_light_direction"),-1.0f,-1.0f,0.0f);
+        glUniform3f(selected_mesh_shader->uniformLocation("directive_light_direction"),listDir.at(currentLightDirection).x(), listDir.at(currentLightDirection).y(), listDir.at(currentLightDirection).z());
+    }
+
     // Find and enable the attribute location for vertex position
     const GLuint vp = selected_mesh_shader->attributeLocation("vertex_position");
     glEnableVertexAttribArray(vp);
@@ -201,11 +348,7 @@ void Canvas::draw_mesh()
 }
 QMatrix4x4 Canvas::orient_matrix() const
 {
-    QMatrix4x4 m;
-    m.rotate(tilt, QVector3D(1, 0, 0));
-    m.rotate(yaw,  QVector3D(0, 0, 1));
-    //We want the x axis to the right, and the z axis up
-    m.scale(-1, 1, -1);
+    QMatrix4x4 m = currentTransform;
     return m;
 }
 QMatrix4x4 Canvas::transform_matrix() const
@@ -255,16 +398,72 @@ void Canvas::mouseReleaseEvent(QMouseEvent* event)
     }
 }
 
+
+// This method change the referential of the mouse point coordinates
+// into a referential x=[-1.0,1.0], y=[-1.0,1.0], with 0,0 being the
+// center of the widget.
+QPointF Canvas::changeMouseCoordinates(QPoint p) {
+    QPointF pr;
+    // Change coordinates
+    double ws2 = this->width() / 2.0;
+    double hs2 = this->height() / 2.0;
+    pr.setX(p.x() / ws2 - 1.0);
+    pr.setY(p.y() / hs2 - 1.0);
+    return pr;
+}
+
+void Canvas::calcArcballTransform(QPointF p1, QPointF p2) {
+    // Calc z1 & z2
+    double x1 = p1.x();
+    double x2 = p2.x();
+    double y1 = p1.y();
+    double y2 = p2.y();
+    double p1sq = x1 * x1 + y1 * y1;
+    double z1;
+    if (p1sq <= 1) {
+        z1 = sqrt(1.0 - p1sq);
+    } else {
+        x1 = x1 / sqrt(p1sq);
+        y1 = y1 / sqrt(p1sq);
+        z1 = 0.0;
+    }
+    double p2sq = x2 * x2 + y2 * y2;
+    double z2;
+    if (p2sq <= 1) {
+        z2 = sqrt(1.0 - p2sq);
+    } else {
+        x2 = x2 / sqrt(p2sq);
+        y2 = y2 / sqrt(p2sq);
+        z2 = 0.0;
+    }
+
+    // set v1 and v2
+    QVector3D v1(x1, y1, z1);
+    QVector3D v2(x2, y2, z2);
+
+    // calc v1 cross v2
+    QVector3D v1xv2 = QVector3D::crossProduct(v1, v2);
+    QVector3D v1xv2Obj = currentTransform.inverted().mapVector(v1xv2);
+
+    // calc angle
+    double angle = acos(std::min(1.0f,QVector3D::dotProduct(v1, v2))) * 180.0 / M_PI;
+    
+    // apply transform
+    currentTransform.rotate(angle,v1xv2Obj);
+}
+
 void Canvas::mouseMoveEvent(QMouseEvent* event)
 {
     auto p = event->pos();
     auto d = p - mouse_pos;
-
+    
 
     if (event->buttons() & Qt::LeftButton)
     {
-        yaw = fmod(yaw - d.x(), 360);
-        tilt = fmod(tilt - d.y(), 360);
+        QPointF p1r = changeMouseCoordinates(mouse_pos);
+        QPointF p2r = changeMouseCoordinates(p);
+        calcArcballTransform(p1r,p2r);
+
         update();
     }
     else if (event->buttons() & Qt::RightButton)
@@ -316,3 +515,71 @@ void Canvas::resizeGL(int width, int height)
 {
     glViewport(0, 0, width, height);
 }
+
+QColor Canvas::getAmbientColor() {
+    return ambientColor;
+}
+
+void Canvas::setAmbientColor(QColor c) {
+    ambientColor = c;
+    QSettings settings;
+    settings.setValue(AMBIENT_COLOR,c);
+}
+
+double Canvas::getAmbientFactor() {
+    return (float) ambientFactor;
+}
+
+void Canvas::setAmbientFactor(double f) {
+    ambientFactor = (float) f;
+    QSettings settings;
+    settings.setValue(AMBIENT_FACTOR,f);
+}
+
+void Canvas::resetAmbientColor() {
+    setAmbientColor(defaultAmbientColor);
+    setAmbientFactor(defaultAmbientFactor);
+}
+
+QColor Canvas::getDirectiveColor() {
+    return directiveColor;
+}
+
+void Canvas::setDirectiveColor(QColor c) {
+    directiveColor = c;
+    QSettings settings;
+    settings.setValue(DIRECTIVE_COLOR,c);
+}
+
+double Canvas::getDirectiveFactor() {
+    return (float) directiveFactor;
+}
+
+void Canvas::setDirectiveFactor(double f) {
+    directiveFactor = (float) f;
+    QSettings settings;
+    settings.setValue(DIRECTIVE_FACTOR,f);
+}
+
+void Canvas::resetDirectiveColor() {
+    setDirectiveColor(defaultDirectiveColor);
+    setDirectiveFactor(defaultDirectiveFactor);
+}
+
+QList<QString> Canvas::getNameDir() {
+    return nameDir;
+}
+
+int Canvas::getCurrentLightDirection() {
+    return currentLightDirection;
+}
+
+void Canvas::setCurrentLightDirection(int ind) {
+    currentLightDirection = ind;
+    QSettings settings;
+    settings.setValue(CURRENT_LIGHT_DIRECTION,currentLightDirection);
+}
+
+void Canvas::resetCurrentLightDirection() {
+    setCurrentLightDirection(defaultCurrentLightDirection);
+}
index 0fdff64cb501cc98c88e44294edc150823ced4b4..8dab30c4fed67b031dca310755cb5b260dc6f81f 100644 (file)
@@ -10,7 +10,8 @@ class Mesh;
 class Backdrop;
 class Axis;
 
-enum DrawMode {shaded, wireframe, surfaceangle, DRAWMODECOUNT};
+enum ViewPoint {centerview, isoview, topview, bottomview, leftview, rightview, frontview, backview};
+enum DrawMode {shaded, wireframe, surfaceangle, meshlight, DRAWMODECOUNT};
 
 class Canvas : public QOpenGLWidget, protected QOpenGLFunctions
 {
@@ -27,6 +28,25 @@ public:
     void draw_axes(bool d);
     void invert_zoom(bool d);
     void set_drawMode(enum DrawMode mode);
+    void common_view_change(enum ViewPoint c);
+    void setResetTransformOnLoad(bool d);
+
+    QColor getAmbientColor();
+    void setAmbientColor(QColor c);
+    double getAmbientFactor();
+    void setAmbientFactor(double f);
+    void resetAmbientColor();
+
+    QColor getDirectiveColor();
+    void setDirectiveColor(QColor c);
+    double getDirectiveFactor();
+    void setDirectiveFactor(double f);
+    void resetDirectiveColor();
+
+    QList<QString> getNameDir();
+    int getCurrentLightDirection();
+    void setCurrentLightDirection(int ind);
+    void resetCurrentLightDirection();
 
 public slots:
     void set_status(const QString& s);
@@ -53,26 +73,50 @@ private:
     QMatrix4x4 transform_matrix() const;
     QMatrix4x4 aspect_matrix() const;
     QMatrix4x4 view_matrix() const;
+    void resetTransform();
+    QPointF changeMouseCoordinates(QPoint p);
+    void calcArcballTransform(QPointF p1, QPointF p2);
 
     QOpenGLShader* mesh_vertshader;
     QOpenGLShaderProgram mesh_shader;
     QOpenGLShaderProgram mesh_wireframe_shader;
     QOpenGLShaderProgram mesh_surfaceangle_shader;
+    QOpenGLShaderProgram mesh_meshlight_shader;
+
+    QColor ambientColor;
+    QColor directiveColor;
+    float ambientFactor;
+    float directiveFactor;
+    QList<QString> nameDir;
+    QList<QVector3D> listDir;
+    int currentLightDirection;
+
+    const static QColor defaultAmbientColor;
+    const static QColor defaultDirectiveColor;
+    const static float defaultAmbientFactor;
+    const static float defaultDirectiveFactor;
+    const static int defaultCurrentLightDirection;
+    const static QString AMBIENT_COLOR;
+    const static QString AMBIENT_FACTOR;
+    const static QString DIRECTIVE_COLOR;
+    const static QString DIRECTIVE_FACTOR;
+    const static QString CURRENT_LIGHT_DIRECTION;
+
 
     GLMesh* mesh;
     Backdrop* backdrop;
     Axis* axis;
 
-    QVector3D center;
-    float scale;
+    QVector3D center, default_center;
+    float scale, default_scale;
     float zoom;
-    float tilt;
-    float yaw;
+    QMatrix4x4 currentTransform;
 
     float perspective;
     enum DrawMode drawMode;
     bool drawAxes;
     bool invertZoom;
+    bool resetTransformOnLoad;
     Q_PROPERTY(float perspective MEMBER perspective WRITE set_perspective);
     QPropertyAnimation anim;
 
index 38bd2ddc66796ba7498eb3ece007e445d2f564ce..58b81a1846e235f0873f64b9fa8fd96628618674 100644 (file)
@@ -116,6 +116,15 @@ Mesh* Loader::load_stl()
         return NULL;
     }
 
+    qint64 file_size, file_size_old;
+    file_size = file.size();
+    do {
+        file_size_old = file_size;
+        QThread::usleep(100000);
+        file_size = file.size();
+    }
+    while(file_size != file_size_old);
+
     // First, try to read the stl as an ASCII file
     if (file.read(5) == "solid")
     {
index 4ec872275b0eef795c7c68c6751392babe3be565..14561ebcb19b5237ac772a2ea267d0c6f037be34 100644 (file)
@@ -4,10 +4,14 @@
 
 int main(int argc, char *argv[])
 {
+    // Force C locale to force decimal point
+    QLocale::setDefault(QLocale::c());
+
     QCoreApplication::setOrganizationName("fstl-app");
     QCoreApplication::setOrganizationDomain("https://github.com/fstl-app/fstl");
     QCoreApplication::setApplicationName("fstl");
     QCoreApplication::setApplicationVersion(FSTL_VERSION);
     App a(argc, argv);
+
     return a.exec();
 }
diff --git a/src/shaderlightprefs.cpp b/src/shaderlightprefs.cpp
new file mode 100644 (file)
index 0000000..d00c707
--- /dev/null
@@ -0,0 +1,180 @@
+#include "shaderlightprefs.h"
+#include "canvas.h"
+#include <QColorDialog>
+
+const QString ShaderLightPrefs::PREFS_GEOM = "shaderPrefsGeometry";
+
+ShaderLightPrefs::ShaderLightPrefs(QWidget *parent, Canvas *_canvas) : QDialog(parent)
+{
+    canvas = _canvas;
+
+    QVBoxLayout* prefsLayout = new QVBoxLayout;
+    this->setLayout(prefsLayout);
+
+    QLabel* title = new QLabel("Shader preferences");
+    QFont boldFont = QApplication::font();
+    boldFont.setWeight(QFont::Bold);
+    title->setFont(boldFont);
+    title->setAlignment(Qt::AlignCenter);
+    prefsLayout->addWidget(title);
+
+    QWidget* middleWidget = new QWidget;
+    QGridLayout* middleLayout = new QGridLayout;
+    middleWidget->setLayout(middleLayout);
+    this->layout()->addWidget(middleWidget);
+
+    // labels
+    middleLayout->addWidget(new QLabel("Ambient Color"),0,0);
+    middleLayout->addWidget(new QLabel("Directive Color"),1,0);
+    middleLayout->addWidget(new QLabel("Direction"),2,0);
+
+    QPixmap dummy(20, 20);
+
+    dummy.fill(canvas->getAmbientColor());
+    buttonAmbientColor = new QPushButton;
+    buttonAmbientColor->setIcon(QIcon(dummy));
+    middleLayout->addWidget(buttonAmbientColor,0,1);
+    buttonAmbientColor->setFocusPolicy(Qt::NoFocus);
+    connect(buttonAmbientColor,SIGNAL(clicked(bool)),this,SLOT(buttonAmbientColorClicked()));
+
+    editAmbientFactor = new QLineEdit;
+    editAmbientFactor->setValidator(new QDoubleValidator);
+    editAmbientFactor->setText(QString("%1").arg(canvas->getAmbientFactor()));
+    middleLayout->addWidget(editAmbientFactor,0,2);
+    connect(editAmbientFactor,SIGNAL(editingFinished()),this,SLOT(editAmbientFactorFinished()));
+
+    QPushButton* buttonResetAmbientColor = new QPushButton("Reset");
+    middleLayout->addWidget(buttonResetAmbientColor,0,3);
+    buttonResetAmbientColor->setFocusPolicy(Qt::NoFocus);
+    connect(buttonResetAmbientColor,SIGNAL(clicked(bool)),this,SLOT(resetAmbientColorClicked()));
+
+
+    dummy.fill(canvas->getDirectiveColor());
+    buttonDirectiveColor = new QPushButton;
+    buttonDirectiveColor->setIcon(QIcon(dummy));
+    middleLayout->addWidget(buttonDirectiveColor,1,1);
+    buttonDirectiveColor->setFocusPolicy(Qt::NoFocus);
+    connect(buttonDirectiveColor,SIGNAL(clicked(bool)),this,SLOT(buttonDirectiveColorClicked()));
+
+    editDirectiveFactor = new QLineEdit;
+    editDirectiveFactor->setValidator(new QDoubleValidator);
+    editDirectiveFactor->setText(QString("%1").arg(canvas->getDirectiveFactor()));
+    middleLayout->addWidget(editDirectiveFactor,1,2);
+    connect(editDirectiveFactor,SIGNAL(editingFinished()),this,SLOT(editDirectiveFactorFinished()));
+
+    QPushButton* buttonResetDirectiveColor = new QPushButton("Reset");
+    middleLayout->addWidget(buttonResetDirectiveColor,1,3);
+    buttonResetDirectiveColor->setFocusPolicy(Qt::NoFocus);
+    connect(buttonResetDirectiveColor,SIGNAL(clicked(bool)),this,SLOT(resetDirectiveColorClicked()));
+
+    // Fill in directions
+
+    comboDirections = new QComboBox;
+    middleLayout->addWidget(comboDirections,2,1,1,2);
+    comboDirections->addItems(canvas->getNameDir());
+    comboDirections->setCurrentIndex(canvas->getCurrentLightDirection());
+    connect(comboDirections,SIGNAL(currentIndexChanged(int)),this,SLOT(comboDirectionsChanged(int)));
+
+    QPushButton* buttonResetDirection = new QPushButton("Reset");
+    middleLayout->addWidget(buttonResetDirection,2,3);
+    buttonResetDirection->setFocusPolicy(Qt::NoFocus);
+    connect(buttonResetDirection,SIGNAL(clicked(bool)),this,SLOT(resetDirection()));
+
+
+    // Ok button
+    QWidget* boxButton = new QWidget;
+    QHBoxLayout* boxButtonLayout = new QHBoxLayout;
+    boxButton->setLayout(boxButtonLayout);
+    QFrame *spacerL = new QFrame;
+    spacerL->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding));
+    QPushButton* okButton = new QPushButton("Ok");
+    boxButtonLayout->addWidget(spacerL);
+    boxButtonLayout->addWidget(okButton);
+    this->layout()->addWidget(boxButton);
+    okButton->setFocusPolicy(Qt::NoFocus);
+    connect(okButton,SIGNAL(clicked(bool)),this,SLOT(okButtonClicked()));
+
+    QSettings settings;
+    if (!settings.value(PREFS_GEOM).isNull()) {
+        restoreGeometry(settings.value(PREFS_GEOM).toByteArray());
+    }
+}
+
+void ShaderLightPrefs::buttonAmbientColorClicked() {
+    QColor newColor = QColorDialog::getColor(canvas->getAmbientColor(), this, QString("Choose color"),QColorDialog::DontUseNativeDialog);
+    if (newColor.isValid() == true)
+    {
+        canvas->setAmbientColor(newColor);
+        QPixmap dummy(20, 20);
+        dummy.fill(canvas->getAmbientColor());
+        buttonAmbientColor->setIcon(QIcon(dummy));
+        canvas->update();
+    }
+}
+
+void ShaderLightPrefs::editAmbientFactorFinished() {
+    canvas->setAmbientFactor(editAmbientFactor->text().toDouble());
+    canvas->update();
+}
+
+void ShaderLightPrefs::resetAmbientColorClicked() {
+    canvas->resetAmbientColor();
+    QPixmap dummy(20, 20);
+    dummy.fill(canvas->getAmbientColor());
+    buttonAmbientColor->setIcon(QIcon(dummy));
+    editAmbientFactor->setText(QString("%1").arg(canvas->getAmbientFactor()));
+    canvas->update();
+}
+
+void ShaderLightPrefs::buttonDirectiveColorClicked() {
+    QColor newColor = QColorDialog::getColor(canvas->getDirectiveColor(), this, QString("Choose color"),QColorDialog::DontUseNativeDialog);
+    if (newColor.isValid() == true)
+    {
+        canvas->setDirectiveColor(newColor);
+        QPixmap dummy(20, 20);
+        dummy.fill(canvas->getDirectiveColor());
+        buttonDirectiveColor->setIcon(QIcon(dummy));
+        canvas->update();
+    }
+}
+
+void ShaderLightPrefs::editDirectiveFactorFinished() {
+    canvas->setDirectiveFactor(editDirectiveFactor->text().toDouble());
+    canvas->update();
+}
+
+void ShaderLightPrefs::resetDirectiveColorClicked() {
+    canvas->resetDirectiveColor();
+    QPixmap dummy(20, 20);
+    dummy.fill(canvas->getDirectiveColor());
+    buttonDirectiveColor->setIcon(QIcon(dummy));
+    editDirectiveFactor->setText(QString("%1").arg(canvas->getDirectiveFactor()));
+    canvas->update();
+}
+
+void ShaderLightPrefs::okButtonClicked() {
+    this->close();
+}
+
+void ShaderLightPrefs::comboDirectionsChanged(int ind) {
+    canvas->setCurrentLightDirection(ind);
+    canvas->update();
+}
+
+void ShaderLightPrefs::resetDirection() {
+    canvas->resetCurrentLightDirection();
+    comboDirections->setCurrentIndex(canvas->getCurrentLightDirection());
+    canvas->update();
+}
+
+void ShaderLightPrefs::resizeEvent(QResizeEvent *event)
+{
+    QSettings().setValue(PREFS_GEOM, saveGeometry());
+}
+
+void ShaderLightPrefs::moveEvent(QMoveEvent *event)
+{
+    QSettings().setValue(PREFS_GEOM, saveGeometry());
+}
+
+
diff --git a/src/shaderlightprefs.h b/src/shaderlightprefs.h
new file mode 100644 (file)
index 0000000..9f57339
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef SHADERLIGHTPREFS_H
+#define SHADERLIGHTPREFS_H
+
+#include <QDialog>
+
+class Canvas;
+class QLabel;
+class QLineEdit;
+class QComboBox;
+
+class ShaderLightPrefs : public QDialog
+{
+    Q_OBJECT
+public:
+    ShaderLightPrefs(QWidget* parent, Canvas* _canvas);
+
+protected:
+    void resizeEvent(QResizeEvent *event) override;
+    void moveEvent(QMoveEvent *event) override;
+
+private slots:
+    void buttonAmbientColorClicked();
+    void editAmbientFactorFinished();
+    void resetAmbientColorClicked();
+
+    void buttonDirectiveColorClicked();
+    void editDirectiveFactorFinished();
+    void resetDirectiveColorClicked();
+
+    void comboDirectionsChanged(int ind);
+    void resetDirection();
+
+    void okButtonClicked();
+
+private:
+    Canvas* canvas;
+    QPushButton* buttonAmbientColor;
+    QLineEdit* editAmbientFactor;
+    QPushButton* buttonDirectiveColor;
+    QLineEdit* editDirectiveFactor;
+    QComboBox* comboDirections;
+
+    const static QString PREFS_GEOM;
+};
+
+#endif // SHADERLIGHTPREFS_H
index e9f3f94ebf2d980e1e088d876d8acf53ae89c699..c8aaba7658afbe6a8713bfd27a1c3dabcc3259f9 100644 (file)
@@ -3,7 +3,9 @@
 #include "window.h"
 #include "canvas.h"
 #include "loader.h"
+#include "shaderlightprefs.h"
 
+const QString Window::OPEN_EXTERNAL_KEY = "externalCmd";
 const QString Window::RECENT_FILE_KEY = "recentFiles";
 const QString Window::INVERT_ZOOM_KEY = "invertZoom";
 const QString Window::AUTORELOAD_KEY = "autoreload";
@@ -11,23 +13,37 @@ const QString Window::DRAW_AXES_KEY = "drawAxes";
 const QString Window::PROJECTION_KEY = "projection";
 const QString Window::DRAW_MODE_KEY = "drawMode";
 const QString Window::WINDOW_GEOM_KEY = "windowGeometry";
+const QString Window::RESET_TRANSFORM_ON_LOAD_KEY = "resetTransformOnLoad";
 
 Window::Window(QWidget *parent) :
     QMainWindow(parent),
     open_action(new QAction("Open", this)),
+    open_external_action(new QAction("Open with", this)),
     about_action(new QAction("About", this)),
     quit_action(new QAction("Quit", this)),
     perspective_action(new QAction("Perspective", this)),
+    common_view_center_action(new QAction("Center the model", this)),
+    common_view_iso_action(new QAction("Isometric", this)),
+    common_view_top_action(new QAction("Top", this)),
+    common_view_bottom_action(new QAction("Bottom", this)),
+    common_view_left_action(new QAction("Left", this)),
+    common_view_right_action(new QAction("Right", this)),
+    common_view_front_action(new QAction("Front", this)),
+    common_view_back_action(new QAction("Back", this)),
     orthographic_action(new QAction("Orthographic", this)),
     shaded_action(new QAction("Shaded", this)),
     wireframe_action(new QAction("Wireframe", this)),
     surfaceangle_action(new QAction("Surface Angle", this)),
+    meshlight_action(new QAction("Shaded ambient and directive light source", this)),
+    drawModePrefs_action(new QAction("Draw Mode Settings")),
     axes_action(new QAction("Draw Axes", this)),
     invert_zoom_action(new QAction("Invert Zoom", this)),
     reload_action(new QAction("Reload", this)),
     autoreload_action(new QAction("Autoreload", this)),
     save_screenshot_action(new QAction("Save Screenshot", this)),
     hide_menuBar_action(new QAction("Hide Menu Bar", this)),
+    fullscreen_action(new QAction("Toggle Fullscreen",this)),
+    resetTransformOnLoadAction(new QAction("Reset rotation on load",this)),
     recent_files(new QMenu("Open recent", this)),
     recent_files_group(new QActionGroup(this)),
     recent_files_clear_action(new QAction("Clear recent files", this)),
@@ -35,6 +51,7 @@ Window::Window(QWidget *parent) :
 
 {
     setWindowTitle("fstl");
+    setWindowIcon(QIcon(":/qt/icons/fstl_64x64.png"));
     setAcceptDrops(true);
 
     QSurfaceFormat format;
@@ -48,6 +65,10 @@ Window::Window(QWidget *parent) :
     canvas = new Canvas(format, this);
     setCentralWidget(canvas);
 
+    meshlightprefs = new ShaderLightPrefs(this, canvas);
+
+    QObject::connect(drawModePrefs_action, &QAction::triggered,this,&Window::on_drawModePrefs);
+
     QObject::connect(watcher, &QFileSystemWatcher::fileChanged,
                      this, &Window::on_watched_change);
 
@@ -56,7 +77,13 @@ Window::Window(QWidget *parent) :
                      this, &Window::on_open);
     this->addAction(open_action);
 
-    quit_action->setShortcut(QKeySequence::Quit);
+    open_external_action->setShortcut(QKeySequence::Open);
+    QObject::connect(open_external_action, &QAction::triggered, this, &Window::on_open_external);
+    this->addAction(open_external_action);
+    open_external_action->setShortcut(QKeySequence(Qt::ALT + Qt::Key_S));
+
+    QList<QKeySequence> quitShortcuts = { QKeySequence::Quit, QKeySequence::Close };
+    quit_action->setShortcuts(quitShortcuts);
     QObject::connect(quit_action, &QAction::triggered,
                      this, &Window::close);
     this->addAction(quit_action);
@@ -84,8 +111,9 @@ Window::Window(QWidget *parent) :
     
     rebuild_recent_files();
 
-    auto file_menu = menuBar()->addMenu("File");
+    const auto file_menu = menuBar()->addMenu("File");
     file_menu->addAction(open_action);
+    file_menu->addAction(open_external_action);
     file_menu->addMenu(recent_files);
     file_menu->addSeparator();
     file_menu->addAction(reload_action);
@@ -93,11 +121,11 @@ Window::Window(QWidget *parent) :
     file_menu->addAction(save_screenshot_action);
     file_menu->addAction(quit_action);
 
-    auto view_menu = menuBar()->addMenu("View");
-    auto projection_menu = view_menu->addMenu("Projection");
+    const auto view_menu = menuBar()->addMenu("View");
+    const auto projection_menu = view_menu->addMenu("Projection");
     projection_menu->addAction(perspective_action);
     projection_menu->addAction(orthographic_action);
-    auto projections = new QActionGroup(projection_menu);
+    const auto projections = new QActionGroup(projection_menu);
     for (auto p : {perspective_action, orthographic_action})
     {
         projections->addAction(p);
@@ -107,12 +135,13 @@ Window::Window(QWidget *parent) :
     QObject::connect(projections, &QActionGroup::triggered,
                      this, &Window::on_projection);
 
-    auto draw_menu = view_menu->addMenu("Draw Mode");
+    const auto draw_menu = view_menu->addMenu("Draw Mode");
     draw_menu->addAction(shaded_action);
     draw_menu->addAction(wireframe_action);
     draw_menu->addAction(surfaceangle_action);
-    auto drawModes = new QActionGroup(draw_menu);
-    for (auto p : {shaded_action, wireframe_action, surfaceangle_action})
+    draw_menu->addAction(meshlight_action);
+    const auto drawModes = new QActionGroup(draw_menu);
+    for (auto p : {shaded_action, wireframe_action, surfaceangle_action, meshlight_action})
     {
         drawModes->addAction(p);
         p->setCheckable(true);
@@ -120,6 +149,38 @@ Window::Window(QWidget *parent) :
     drawModes->setExclusive(true);
     QObject::connect(drawModes, &QActionGroup::triggered,
                      this, &Window::on_drawMode);
+    view_menu->addAction(drawModePrefs_action);
+    drawModePrefs_action->setDisabled(true);
+
+    const auto common_menu = view_menu->addMenu("Viewpoint");
+    common_menu->addAction(common_view_iso_action);
+    common_menu->addAction(common_view_top_action);
+    common_menu->addAction(common_view_bottom_action);
+    common_menu->addAction(common_view_front_action);
+    common_menu->addAction(common_view_back_action);
+    common_menu->addAction(common_view_left_action);
+    common_menu->addAction(common_view_right_action);
+    common_menu->addAction(common_view_center_action);
+    const auto common_views = new QActionGroup(common_menu);
+    common_views->addAction(common_view_iso_action);
+    common_views->addAction(common_view_top_action);
+    common_views->addAction(common_view_bottom_action);
+    common_views->addAction(common_view_front_action);
+    common_views->addAction(common_view_back_action);
+    common_views->addAction(common_view_left_action);
+    common_views->addAction(common_view_right_action);
+    common_views->addAction(common_view_center_action);
+    common_view_iso_action->setShortcut(Qt::Key_0);
+    common_view_top_action->setShortcut(Qt::Key_1);
+    common_view_bottom_action->setShortcut(Qt::Key_2);
+    common_view_front_action->setShortcut(Qt::Key_3);
+    common_view_back_action->setShortcut(Qt::Key_4);
+    common_view_left_action->setShortcut(Qt::Key_5);
+    common_view_right_action->setShortcut(Qt::Key_6);
+    common_view_center_action->setShortcut(Qt::Key_9);
+    QObject::connect(common_views, &QActionGroup::triggered,
+        this, &Window::on_common_view_change);
+
     view_menu->addAction(axes_action);
     axes_action->setCheckable(true);
     QObject::connect(axes_action, &QAction::triggered,
@@ -130,6 +191,11 @@ Window::Window(QWidget *parent) :
     QObject::connect(invert_zoom_action, &QAction::triggered,
             this, &Window::on_invertZoom);       
 
+    view_menu->addAction(resetTransformOnLoadAction);
+    resetTransformOnLoadAction->setCheckable(true);
+    QObject::connect(resetTransformOnLoadAction, &QAction::triggered,
+            this, &Window::on_resetTransformOnLoad);
+
     view_menu->addAction(hide_menuBar_action);
     hide_menuBar_action->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C);
     hide_menuBar_action->setCheckable(true);
@@ -137,6 +203,14 @@ Window::Window(QWidget *parent) :
             this, &Window::on_hide_menuBar);
     this->addAction(hide_menuBar_action);
 
+    view_menu->addAction(fullscreen_action);
+    fullscreen_action->setShortcut(Qt::Key_F11);
+    fullscreen_action->setCheckable(true);
+    QObject::connect(fullscreen_action, &QAction::toggled,
+            this, &Window::on_fullscreen);
+    this->addAction(fullscreen_action);
+
+
     auto help_menu = menuBar()->addMenu("Help");
     help_menu->addAction(about_action);
 
@@ -149,6 +223,10 @@ void Window::load_persist_settings(){
     canvas->invert_zoom(invert_zoom);
     invert_zoom_action->setChecked(invert_zoom);
 
+    bool resetTransformOnLoad = settings.value(RESET_TRANSFORM_ON_LOAD_KEY, true).toBool();
+    canvas->setResetTransformOnLoad(resetTransformOnLoad);
+    resetTransformOnLoadAction->setChecked(resetTransformOnLoad);
+
     autoreload_action->setChecked(settings.value(AUTORELOAD_KEY, true).toBool());
 
     bool draw_axes = settings.value(DRAW_AXES_KEY, false).toBool();
@@ -164,30 +242,73 @@ void Window::load_persist_settings(){
         orthographic_action->setChecked(true);
     }
 
+    QString path = settings.value(OPEN_EXTERNAL_KEY, "").toString();
+    if (!QDir::isAbsolutePath(path) && !path.isEmpty())
+    {
+        path = QStandardPaths::findExecutable(path);
+    }
+    QString displayName = path.mid(path.lastIndexOf(QDir::separator()) + 1);
+    open_external_action->setText("Open with " + displayName);
+    open_external_action->setData(path);
+
     DrawMode draw_mode = (DrawMode)settings.value(DRAW_MODE_KEY, DRAWMODECOUNT).toInt();
     
     if(draw_mode >= DRAWMODECOUNT)
     {
         draw_mode = shaded;
     }
-    canvas->set_drawMode(draw_mode);
-    QAction* (dm_acts[]) = {shaded_action, wireframe_action, surfaceangle_action};
+    QAction* (dm_acts[]) = {shaded_action, wireframe_action, surfaceangle_action, meshlight_action};
     dm_acts[draw_mode]->setChecked(true);
+    on_drawMode(dm_acts[draw_mode]);
 
     resize(600, 400);
     restoreGeometry(settings.value(WINDOW_GEOM_KEY).toByteArray());
 }
 
+void Window::on_drawModePrefs() {
+    // For now only one draw mode has settings
+    // when settings for other draw mode will be available
+    // we will need to check the current mode
+    if (meshlightprefs->isVisible()) {
+        meshlightprefs->hide();
+    } else {
+        meshlightprefs->show();
+    }
+}
+
 void Window::on_open()
 {
-    QString filename = QFileDialog::getOpenFileName(
-                this, "Load .stl file", QString(), "STL files (*.stl, *.STL)");
+    const QString filename = QFileDialog::getOpenFileName(
+                this, "Load .stl file", QString(), "STL files (*.stl *.STL)");
     if (!filename.isNull())
     {
         load_stl(filename);
     }
 }
 
+void Window::on_open_external() const
+{
+    if (current_file.isEmpty())
+    {
+        return;
+    }
+
+
+    QString program = open_external_action->data().toString();
+    if (program.isEmpty()) {
+        program = QFileDialog::getOpenFileName((QWidget*) this, "Select program to open with", QDir::rootPath());
+        if (!program.isEmpty()) {
+            QSettings settings;
+            settings.setValue(OPEN_EXTERNAL_KEY, program);
+            QString displayName = program.mid(program.lastIndexOf(QDir::separator()) + 1);
+            open_external_action->setText("Open with " + displayName);
+            open_external_action->setData(program);
+        }
+    }
+
+    QProcess::startDetached(program, QStringList(current_file));
+}
+
 void Window::on_about()
 {
     QMessageBox::about(this, "",
@@ -195,7 +316,7 @@ void Window::on_about()
         "<p>A fast viewer for <code>.stl</code> files.<br>"
         "<a href=\"https://github.com/fstl-app/fstl\""
         "   style=\"color: #93a1a1;\">https://github.com/fstl-app/fstl</a></p>"
-        "<p>© 2014-2022 Matthew Keeter<br>"
+        "<p>© 2014-2024 Matthew Keeter<br>"
         "<a href=\"mailto:matt.j.keeter@gmail.com\""
         "   style=\"color: #93a1a1;\">matt.j.keeter@gmail.com</a></p>");
 }
@@ -270,19 +391,30 @@ void Window::on_projection(QAction* proj)
 
 void Window::on_drawMode(QAction* act)
 {
+    // On mode change hide prefs first
+    meshlightprefs->hide();
+
     DrawMode mode;
     if (act == shaded_action)
     {
+        drawModePrefs_action->setEnabled(false);
         mode = shaded;
     }
     else if (act == wireframe_action)
     {
+        drawModePrefs_action->setEnabled(false);
         mode = wireframe;
     }
-    else
+    else if (act == surfaceangle_action)
     {
+        drawModePrefs_action->setEnabled(false);
         mode = surfaceangle;
     }
+    else if (act == meshlight_action)
+    {
+        drawModePrefs_action->setEnabled(true);
+        mode = meshlight;
+    }
     canvas->set_drawMode(mode);
     QSettings().setValue(DRAW_MODE_KEY, mode);
 }
@@ -299,6 +431,11 @@ void Window::on_invertZoom(bool d)
     QSettings().setValue(INVERT_ZOOM_KEY, d);
 }
 
+void Window::on_resetTransformOnLoad(bool d) {
+    canvas->setResetTransformOnLoad(d);
+    QSettings().setValue(RESET_TRANSFORM_ON_LOAD_KEY, d);
+}
+
 void Window::on_watched_change(const QString& filename)
 {
     if (autoreload_action->isChecked())
@@ -410,6 +547,18 @@ void Window::on_reload()
     }
 }
 
+void Window::on_common_view_change(QAction* common)
+{
+  if (common == common_view_center_action) canvas->common_view_change(centerview);
+  if (common == common_view_iso_action) canvas->common_view_change(isoview);
+  if (common == common_view_top_action) canvas->common_view_change(topview);
+  if (common == common_view_bottom_action) canvas->common_view_change(bottomview);
+  if (common == common_view_left_action) canvas->common_view_change(leftview);
+  if (common == common_view_right_action) canvas->common_view_change(rightview);
+  if (common == common_view_front_action) canvas->common_view_change(frontview);
+  if (common == common_view_back_action) canvas->common_view_change(backview);
+}
+
 bool Window::load_stl(const QString& filename, bool is_reload)
 {
     if (!open_action->isEnabled())  return false;
@@ -610,3 +759,11 @@ void Window::keyPressEvent(QKeyEvent* event)
 
     QMainWindow::keyPressEvent(event);
 }
+
+void Window::on_fullscreen() {
+    if (!this->isFullScreen()) {
+        this->showFullScreen();
+    } else {
+        this->showNormal();
+    }
+}
index 81cd29f5f6a42b15f91539bc3048f448c0d49c38..f13137b8c4135a0993c19e4982b1c42734a0cd82 100644 (file)
@@ -7,6 +7,7 @@
 #include <QCollator>
 
 class Canvas;
+class ShaderLightPrefs;
 
 class Window : public QMainWindow
 {
@@ -26,6 +27,7 @@ protected:
 
 public slots:
     void on_open();
+    void on_open_external() const;
     void on_about();
     void on_bad_stl();
     void on_empty_mesh();
@@ -41,14 +43,18 @@ private slots:
     void on_drawMode(QAction* mode);
     void on_drawAxes(bool d);
     void on_invertZoom(bool d);
+    void on_resetTransformOnLoad(bool d);
     void on_watched_change(const QString& filename);
     void on_reload();
+    void on_common_view_change(QAction* common);
     void on_autoreload_triggered(bool r);
     void on_clear_recent();
     void on_load_recent(QAction* a);
     void on_loaded(const QString& filename);
     void on_save_screenshot();
+    void on_fullscreen();
     void on_hide_menuBar();
+    void on_drawModePrefs();
 
 private:
     void rebuild_recent_files();
@@ -58,24 +64,38 @@ private:
     QPair<QString, QString> get_file_neighbors();
 
     QAction* const open_action;
+    QAction* const open_external_action;
     QAction* const about_action;
     QAction* const quit_action;
     QAction* const perspective_action;
+    QAction* const common_view_center_action;
+    QAction* const common_view_iso_action;
+    QAction* const common_view_top_action;
+    QAction* const common_view_bottom_action;
+    QAction* const common_view_left_action;
+    QAction* const common_view_right_action;
+    QAction* const common_view_front_action;
+    QAction* const common_view_back_action;
     QAction* const orthographic_action;
     QAction* const shaded_action;
     QAction* const wireframe_action;
     QAction* const surfaceangle_action;
+    QAction* const meshlight_action;
+    QAction* const drawModePrefs_action;
     QAction* const axes_action;
     QAction* const invert_zoom_action;
     QAction* const reload_action;
     QAction* const autoreload_action;
     QAction* const save_screenshot_action;
     QAction* const hide_menuBar_action;
+    QAction* const fullscreen_action;
+    QAction* const resetTransformOnLoadAction;
 
     QMenu* const recent_files;
     QActionGroup* const recent_files_group;
     QAction* const recent_files_clear_action;
     const static int MAX_RECENT_FILES=8;
+    static const QString OPEN_EXTERNAL_KEY;
     const static QString RECENT_FILE_KEY;
     const static QString INVERT_ZOOM_KEY;
     const static QString AUTORELOAD_KEY;
@@ -83,6 +103,7 @@ private:
     const static QString PROJECTION_KEY;
     const static QString DRAW_MODE_KEY;
     const static QString WINDOW_GEOM_KEY;
+    const static QString RESET_TRANSFORM_ON_LOAD_KEY;
 
     QString current_file;
     QString lookup_folder;
@@ -91,6 +112,8 @@ private:
     QFileSystemWatcher* watcher;
 
     Canvas* canvas;
+
+    ShaderLightPrefs* meshlightprefs;
 };
 
 #endif // WINDOW_H
diff --git a/xdg/README.txt b/xdg/README.txt
new file mode 100644 (file)
index 0000000..7aad2b0
--- /dev/null
@@ -0,0 +1,23 @@
+Linux :
+-----------
+desktop file and application icons installation.
+This tells the system that fstl knows to open stl files and allow stl to
+be launched using windows key.
+
+Install :
+./xdg_install.sh fstl
+
+Uninstall :
+./xdg_uninstall.sh fstl
+
+if runned as regular user this will install locally in :
+   $HOME/.local/share/mime/
+   $HOME/.local/share/applications/
+   $HOME/.local/share/icons/
+
+if runned as root this will install system-wide in :
+   /usr/share/mime
+   /usr/share/applications
+   /usr/share/icons
+
+Third script xdg_package_install.sh is to be used when building deb or rpm package.
diff --git a/xdg/fstlapp-fstl.desktop b/xdg/fstlapp-fstl.desktop
new file mode 100644 (file)
index 0000000..b4e93c5
--- /dev/null
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Name=fstl
+GenericName=Fast STL Viewer
+Exec=fstl %U
+Terminal=false
+Icon=fstlapp-fstl
+Type=Application
+MimeType=model/stl;
+Categories=Utility;
diff --git a/xdg/icons/fstlapp-fstl_128x128.png b/xdg/icons/fstlapp-fstl_128x128.png
new file mode 100644 (file)
index 0000000..0c31200
Binary files /dev/null and b/xdg/icons/fstlapp-fstl_128x128.png differ
diff --git a/xdg/icons/fstlapp-fstl_16x16.png b/xdg/icons/fstlapp-fstl_16x16.png
new file mode 100644 (file)
index 0000000..d53992e
Binary files /dev/null and b/xdg/icons/fstlapp-fstl_16x16.png differ
diff --git a/xdg/icons/fstlapp-fstl_22x22.png b/xdg/icons/fstlapp-fstl_22x22.png
new file mode 100644 (file)
index 0000000..40e760e
Binary files /dev/null and b/xdg/icons/fstlapp-fstl_22x22.png differ
diff --git a/xdg/icons/fstlapp-fstl_256x256.png b/xdg/icons/fstlapp-fstl_256x256.png
new file mode 100644 (file)
index 0000000..4235dc5
Binary files /dev/null and b/xdg/icons/fstlapp-fstl_256x256.png differ
diff --git a/xdg/icons/fstlapp-fstl_32x32.png b/xdg/icons/fstlapp-fstl_32x32.png
new file mode 100644 (file)
index 0000000..8bab67e
Binary files /dev/null and b/xdg/icons/fstlapp-fstl_32x32.png differ
diff --git a/xdg/icons/fstlapp-fstl_48x48.png b/xdg/icons/fstlapp-fstl_48x48.png
new file mode 100644 (file)
index 0000000..851d8bd
Binary files /dev/null and b/xdg/icons/fstlapp-fstl_48x48.png differ
diff --git a/xdg/icons/fstlapp-fstl_64x64.png b/xdg/icons/fstlapp-fstl_64x64.png
new file mode 100644 (file)
index 0000000..0f83e6b
Binary files /dev/null and b/xdg/icons/fstlapp-fstl_64x64.png differ
diff --git a/xdg/xdg_install.sh b/xdg/xdg_install.sh
new file mode 100755 (executable)
index 0000000..b9d59b2
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+# This script will install mimetypes, icons and desktop file, 
+# it takes a name in argument
+#
+# if runned as regular user this will install locally in :
+#    $HOME/.local/share/mime/
+#    $HOME/.local/share/applications/
+#    $HOME/.local/share/icons/
+#
+# if runned as root this will install system-wide in :
+#    /usr/share/mime
+#    /usr/share/applications
+#    /usr/share/icons
+
+if [ $# != 1 ]; then
+  echo "You must provide an application name"
+  exit 1
+fi
+
+name=$1
+
+# echo "Installing mimetypes"
+# xdg-mime install fstlapp-$name-mimetypes.xml
+
+echo "Installing desktop file"
+xdg-desktop-menu install fstlapp-$name.desktop
+
+echo "Installing apps icons"
+iclist="fstlapp-$name"
+for im in $iclist
+do
+  xdg-icon-resource install --theme hicolor --context apps --size 16 icons/${im}_16x16.png $im
+  xdg-icon-resource install --theme hicolor --context apps --size 22 icons/${im}_22x22.png $im
+  xdg-icon-resource install --theme hicolor --context apps --size 32 icons/${im}_32x32.png $im
+  xdg-icon-resource install --theme hicolor --context apps --size 48 icons/${im}_48x48.png $im
+  xdg-icon-resource install --theme hicolor --context apps --size 64 icons/${im}_64x64.png $im
+  xdg-icon-resource install --theme hicolor --context apps --size 128 icons/${im}_128x128.png $im
+  xdg-icon-resource install --theme hicolor --context apps --size 256 icons/${im}_256x256.png $im
+done
+
+# echo "Installing mimetypes icons"
+# iclist="`cat fstlapp-$name-mimetypes.xml | grep "icon name" | sed 's/^.*"\(.*\)".*$/\1/'`"
+# for im in $iclist
+# do
+#   xdg-icon-resource install --theme hicolor --context mimetypes --size 16 icons/${im}_16x16.png $im
+#   xdg-icon-resource install --theme hicolor --context mimetypes --size 22 icons/${im}_22x22.png $im
+#   xdg-icon-resource install --theme hicolor --context mimetypes --size 32 icons/${im}_32x32.png $im
+#   xdg-icon-resource install --theme hicolor --context mimetypes --size 48 icons/${im}_48x48.png $im
+#   xdg-icon-resource install --theme hicolor --context mimetypes --size 64 icons/${im}_64x64.png $im
+# done
+
diff --git a/xdg/xdg_package_install.sh b/xdg/xdg_package_install.sh
new file mode 100755 (executable)
index 0000000..7e19a42
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+# For a package installation (rpm or deb), we must proceed a different way
+# This script takes two arguments, the first one is the installation
+# prefix and the second is the name
+
+if [ $# != 2 ]; then
+  echo "You must provide two arguments"
+  exit 1
+fi
+
+base=$1
+name=$2
+
+# echo "Drop mimetypes file in /usr/share/mime/packages/"
+# mkdir -p $base/usr/share/mime/packages/
+# cp fstlapp-$name-mimetypes.xml $base/usr/share/mime/packages/
+
+echo "Drop desktop file in /usr/share/applications/"
+mkdir -p $base/usr/share/applications/
+cp fstlapp-$name.desktop $base/usr/share/applications/
+
+slist="16 22 32 48 64 128 256"
+echo "Installing apps icons"
+iclist="fstlapp-$name"
+for im in $iclist
+do
+  for s in $slist
+  do
+    mkdir -p $base/usr/share/icons/hicolor/${s}x${s}/apps
+    cp icons/${im}_${s}x${s}.png $base/usr/share/icons/hicolor/${s}x${s}/apps/$im.png
+  done
+done
+
+# echo "Installing mimetypes icons"
+# iclist="`cat fstlapp-$name-mimetypes.xml | grep "icon name" | sed 's/^.*"\(.*\)".*$/\1/'`"
+# for im in $iclist
+# do
+#   for s in $slist
+#   do
+#     mkdir -p $base/usr/share/icons/hicolor/${s}x${s}/mimetypes
+#     cp icons/${im}_${s}x${s}.png $base/usr/share/icons/hicolor/${s}x${s}/mimetypes/$im.png
+#   done
+# done
+
+#
+# Put this in the post installation and post uninstallation scripts
+#
+#echo "Updating mime database"
+#update-mime-database /usr/share/mim
+#
+#echo "Updating desktop database"
+#update-desktop-database
diff --git a/xdg/xdg_uninstall.sh b/xdg/xdg_uninstall.sh
new file mode 100755 (executable)
index 0000000..1dd7ef4
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# This script will uninstall mimetypes, icons and desktop file
+#
+# if runned as regular user this will uninstall locally from :
+#    $HOME/.local/share/mime/
+#    $HOME/.local/share/applications/
+#    $HOME/.local/share/icons/
+#
+# if runned as root this will uninstall system-wide from :
+#    /usr/share/mime
+#    /usr/share/applications
+#    /usr/share/icons
+
+if [ $# != 1 ]; then
+  echo "You must provide a name"
+  exit 1
+fi
+
+name=$1
+
+# echo "Uninstalling mimetypes"
+# xdg-mime uninstall fstlapp-$name-mimetypes.xml
+
+echo "Uninstalling desktop file"
+xdg-desktop-menu uninstall fstlapp-$name.desktop
+
+echo "Uninstalling apps icons"
+iclist="fstlapp-$name"
+for im in $iclist
+do
+  xdg-icon-resource uninstall --theme hicolor --context apps --size 16 $im
+  xdg-icon-resource uninstall --theme hicolor --context apps --size 22 $im
+  xdg-icon-resource uninstall --theme hicolor --context apps --size 32 $im
+  xdg-icon-resource uninstall --theme hicolor --context apps --size 48 $im
+  xdg-icon-resource uninstall --theme hicolor --context apps --size 64 $im
+  xdg-icon-resource uninstall --theme hicolor --context apps --size 128 $im
+  xdg-icon-resource uninstall --theme hicolor --context apps --size 256 $im
+done
+
+# echo "Uninstalling mimetypes icons"
+# iclist="`cat fstlapp-$name-mimetypes.xml | grep "icon name" | sed 's/^.*"\(.*\)".*$/\1/'`"
+# for im in $iclist
+# do
+#   xdg-icon-resource uninstall --theme hicolor --context mimetypes --size 16 $im
+#   xdg-icon-resource uninstall --theme hicolor --context mimetypes --size 22 $im
+#   xdg-icon-resource uninstall --theme hicolor --context mimetypes --size 32 $im
+#   xdg-icon-resource uninstall --theme hicolor --context mimetypes --size 48 $im
+#   xdg-icon-resource uninstall --theme hicolor --context mimetypes --size 64 $im
+# done