cmake_minimum_required(VERSION 3.3)
+project(fstl)
+
# Setting -std=c++11
set(CMAKE_CXX_STANDARD 11)
# Setting standard to required, as requisted by DeveloperPaul123 on github
# Set the version number
set (FSTL_VERSION_MAJOR "0")
-set (FSTL_VERSION_MINOR "9")
-set (FSTL_VERSION_PATCH "4")
+set (FSTL_VERSION_MINOR "10")
+set (FSTL_VERSION_PATCH "0")
set (PROJECT_VERSION "${FSTL_VERSION_MAJOR}.${FSTL_VERSION_MINOR}.${FSTL_VERSION_PATCH}")
-project(fstl)
+message(STATUS "Version: ${PROJECT_VERSION}")
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
#set project sources
set(Project_Sources src/app.cpp
src/backdrop.cpp
+src/axis.cpp
src/canvas.cpp
src/glmesh.cpp
src/loader.cpp
#set project headers.
set(Project_Headers src/app.h
src/backdrop.h
+src/axis.h
src/canvas.h
src/glmesh.h
src/loader.h
set(OpenGL_GL_PREFERENCE GLVND)
#find required packages.
-find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets OpenGL)
+find_package(Qt5 5.14 REQUIRED COMPONENTS Core Gui Widgets OpenGL)
find_package(OpenGL REQUIRED)
find_package(Threads REQUIRED)
#include opengl files.
include_directories(${QT_QTOPENGL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR} )
-add_executable(fstl WIN32 ${Project_Sources} ${Project_Headers} ${Project_Resources_RCC} ${Icon_Resource})
-target_link_libraries(fstl Qt5::Widgets Qt5::Core Qt5::Gui Qt5::OpenGL ${OPENGL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
if(WIN32)
+ add_executable(fstl WIN32 ${Project_Sources} ${Project_Headers} ${Project_Resources_RCC} ${Icon_Resource})
set(Fstl_LINK_FLAGS ${CMAKE_CURRENT_SOURCE_DIR}/${Icon_Resource})
set_target_properties(fstl PROPERTIES LINK_FLAGS ${Fstl_LINK_FLAGS})
+elseif(APPLE)
+ add_executable(fstl MACOSX_BUNDLE ${Project_Sources} ${Project_Headers} ${Project_Resources_RCC} ${Icon_Resource})
+else()
+ add_executable(fstl ${Project_Sources} ${Project_Headers} ${Project_Resources_RCC} ${Icon_Resource})
endif(WIN32)
+target_link_libraries(fstl Qt5::Widgets Qt5::Core Qt5::Gui Qt5::OpenGL ${OPENGL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
+
# Add version definitions to use within the code.
target_compile_definitions(fstl PRIVATE -DFSTL_VERSION="${PROJECT_VERSION}")
if(WIN32)
- set(QT_USE_QTMAIN true)
-
- if(MSVC)
- set_source_files_properties(fstl PROPERTIES LINKER_LANGUAGE "CXX")
- set_target_properties(fstl PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS")
-
- install(TARGETS fstl DESTINATION bin COMPONENT all)
-
- install(FILES
- $<TARGET_FILE:Qt5::Gui_EGL>
- $<TARGET_FILE:Qt5::Gui_GLESv2>
- $<TARGET_FILE:Qt5::Core>
- $<TARGET_FILE:Qt5::Gui>
- $<TARGET_FILE:Qt5::OpenGL>
- $<TARGET_FILE:Qt5::Widgets>
- DESTINATION bin COMPONENT all)
-
- #install file in the platforms directory.
- install (FILES
- ${Qt5Core_DIR}/../../../plugins/platforms/qwindows.dll
- DESTINATION bin/platforms COMPONENT all
- )
-
- #custom commands based on: https://gist.github.com/Rod-Persky/e6b93e9ee31f9516261b
- add_custom_command(TARGET fstl POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Core> $<TARGET_FILE_DIR:${PROJECT_NAME}>
- COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Gui> $<TARGET_FILE_DIR:${PROJECT_NAME}>
- COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Widgets> $<TARGET_FILE_DIR:${PROJECT_NAME}>
- COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::OpenGL> $<TARGET_FILE_DIR:${PROJECT_NAME}>
- )
- endif(MSVC)
-
- # windows specific installer generation information
- set(CPACK_GENERATOR NSIS)
- set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL OFF)
- set(CPACK_NSIS_MODIFY_PATH ON)
- set(CPACK_NSIS_MUI_FINISHPAGE_RUN ${PROJECT_NAME})
- set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}")
- set(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\fstl.exe")
- set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/mkeeter/fstl")
- set(CPACK_NSIS_DISPLAY_NAME "fstl ${FSTL_VERSION}")
- set(CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/exe/fstl.ico")
- set(CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}/exe/fstl.ico")
- set(CPACK_NSIS_CREATE_ICONS_EXTRA
- "CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\fstl.lnk' '$INSTDIR\\\\bin\\\\fstl.exe'")
- set(CPACK_COMPONENTS_ALL all)
- if (CMAKE_CL_64)
- set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
- else (CMAKE_CL_64)
- set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES")
- endif (CMAKE_CL_64)
+ set(QT_USE_QTMAIN true)
+
+ if(MSVC)
+ set_source_files_properties(fstl PROPERTIES LINKER_LANGUAGE "CXX")
+ set_target_properties(fstl PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS")
+
+ install(TARGETS fstl DESTINATION bin COMPONENT all)
+
+ install(FILES
+ $<TARGET_FILE:Qt5::Gui_EGL>
+ $<TARGET_FILE:Qt5::Gui_GLESv2>
+ $<TARGET_FILE:Qt5::Core>
+ $<TARGET_FILE:Qt5::Gui>
+ $<TARGET_FILE:Qt5::OpenGL>
+ $<TARGET_FILE:Qt5::Widgets>
+ DESTINATION bin COMPONENT all)
+
+ #install file in the platforms directory.
+ install (FILES
+ ${Qt5Core_DIR}/../../../plugins/platforms/qwindows.dll
+ DESTINATION bin/platforms COMPONENT all
+ )
+
+ #custom commands based on: https://gist.github.com/Rod-Persky/e6b93e9ee31f9516261b
+ add_custom_command(TARGET fstl POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Core> $<TARGET_FILE_DIR:${PROJECT_NAME}>
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Gui> $<TARGET_FILE_DIR:${PROJECT_NAME}>
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Widgets> $<TARGET_FILE_DIR:${PROJECT_NAME}>
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::OpenGL> $<TARGET_FILE_DIR:${PROJECT_NAME}>
+ )
+ endif(MSVC)
+
+ # windows specific installer generation information
+ set(CPACK_GENERATOR NSIS)
+ set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL OFF)
+ set(CPACK_NSIS_MODIFY_PATH ON)
+ set(CPACK_NSIS_MUI_FINISHPAGE_RUN ${PROJECT_NAME})
+ set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}")
+ set(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\fstl.exe")
+ set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/fstl-app/fstl")
+ set(CPACK_NSIS_DISPLAY_NAME "fstl ${FSTL_VERSION}")
+ set(CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/exe/fstl.ico")
+ set(CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}/exe/fstl.ico")
+ set(CPACK_NSIS_CREATE_ICONS_EXTRA
+ "CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\fstl.lnk' '$INSTDIR\\\\bin\\\\fstl.exe'")
+ set(CPACK_COMPONENTS_ALL all)
+ if (CMAKE_CL_64)
+ set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
+ else (CMAKE_CL_64)
+ set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES")
+ endif (CMAKE_CL_64)
elseif(APPLE)
- set(CPACK_GENERATOR "DragNDrop")
- set(CPACK_DMG_FORMAT "UDBZ")
- set(CPACK_DMG_VOLUME_NAME "${PROJECT_NAME}")
- set(CPACK_SYSTEM_NAME "OSX")
- set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
+ set(CPACK_GENERATOR "DragNDrop")
+ set(CPACK_DMG_FORMAT "UDBZ")
+ set(CPACK_DMG_VOLUME_NAME "${PROJECT_NAME}")
+ set(CPACK_SYSTEM_NAME "OSX")
+ set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/app/fstl.icns")
else()
- install(TARGETS fstl RUNTIME DESTINATION bin)
+ install(TARGETS fstl RUNTIME DESTINATION bin)
- set(CPACK_GENERATOR "DEB;RPM")
- set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
+ set(CPACK_GENERATOR "DEB;RPM")
+ set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
endif(WIN32)
include(CPack)
# fstl
-`fstl` is the fastest [.stl file](http://en.wikipedia.org/wiki/STL_\(file_format\)) viewer.
+`fstl` is a very fast [.stl file](http://en.wikipedia.org/wiki/STL_\(file_format\)) viewer.
+
+It was originally written by [Matt Keeter](https://mattkeeter.com),
+and is now primarily maintained by [@DeveloperPaul123](https://github.com/DeveloperPaul123).
It is designed to quickly load and render very high-polygon models;
showing 2 million triangles at 60+ FPS on a mid-range laptop.
the project is under 1K lines of code and should be fairly approachable.
## Screenshot
+
![Eiffel tower](http://mattkeeter.com/projects/fstl/eiffel.png)
(credit to [Pranav Panchal](https://grabcad.com/pranav.panchal))
+## Setting `fstl` as the Default STL Viewer
+
+### Windows
+
+1. Right-click an STL file
+2. Select `Open With` >>> `Choose another app`
+3. Select `More Apps` and `Look for another app on this PC`
+4. Enter the path to the `fstl` EXE file
+
+### MacOS
+
+1. Ctrl+click an STL file
+2. Select `Get Info`
+3. Navigate to the `Open with` section
+4. Select `fstl` in the dropdown
+5. Click `Change All`
+
+### Linux
+
+If `mimeopen` is available on your system, it can be used to set `fstl` as the default viewer for STL files.
+Run the following in your terminal:
+
+```bash
+# replace example.stl with an actual file
+mimeopen -d example.stl
+```
+
+The following output will result:
+
+```
+Please choose a default application for files of type model/stl
+
+ 1) Other...
+
+use application #
+```
+
+Select the `Other` option and type `fstl` as the desired command to open STL files.
+This will now become the system default, even when opening files from the file manager.
+
## Building
-The only dependency for `fstl` is [Qt 5](https://www.qt.io).
+The only dependency for `fstl` is [Qt 5](https://www.qt.io),
+plus [`cmake`](https://cmake.org/) for building.
### macOS
-Install Qt from their website or [Homebrew](brew.sh),
-making sure `qmake` is on your shell's path.
+Install Qt from their website or [Homebrew](brew.sh).
+
+Install `cmake` through Homebrew or equivalent.
Then, run through the following set of commands in a shell:
+
```
-git clone https://github.com/mkeeter/fstl
+git clone https://github.com/fstl-app/fstl
cd fstl
mkdir build
cd build
-qmake ../qt/fstl.pro
+cmake -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/5.15.0/ ..
make -j8
./fstl.app/Contents/MacOS/fstl
```
+You may need to edit the Qt path depending on your installation.
+
To package a standalone app, go to the app directory and run `package.sh`
```
Install Qt with your distro's package manager (required libraries are Core, Gui,
Widgets and OpenGL, e.g. `qt5-default` and `libqt5opengl5-dev` on Debian).
-You can build fstl with qmake (in some distros qmake-qt5) or with CMake:
+You can build fstl with CMake:
```
-git clone https://github.com/mkeeter/fstl
+git clone https://github.com/fstl-app/fstl
cd fstl
mkdir build
cd build
-
-qmake ../qt/fstl.pro # For qmake build
-cmake .. # For CMake build
-
+cmake ..
make -j8
./fstl
```
MACDEPLOYQT=`otool -L $APP.app/Contents/MacOS/fstl | sed -n -e "s:\(.*\)lib/QtCore.*:\1/bin/macdeployqt:gp"`
$MACDEPLOYQT $APP.app
+cp ../app/Info.plist $APP.app/Contents/
# Delete unused Qt plugins
cd fstl.app/Contents/PlugIns
done
cd ../Resources
-rm empty.lproj
# Create a disk image
cd ../../..
--- /dev/null
+#version 120
+
+varying vec3 frag_color;
+
+void main() {
+ gl_FragColor = vec4(frag_color, 1.0);
+}
--- /dev/null
+#version 120
+attribute vec3 vertex_position;
+attribute vec3 vertex_color;
+
+uniform mat4 transform_matrix;
+uniform mat4 view_matrix;
+
+varying vec3 frag_color;
+
+void main() {
+ gl_Position = view_matrix*transform_matrix*
+ vec4(vertex_position, 1.0);
+ frag_color = vertex_color;
+}
<file>mesh.frag</file>
<file>mesh.vert</file>
<file>mesh_wireframe.frag</file>
+ <file>mesh_surfaceangle.frag</file>
<file>quad.frag</file>
<file>quad.vert</file>
+ <file>colored_lines.frag</file>
+ <file>colored_lines.vert</file>
<file>sphere.stl</file>
</qresource>
</RCC>
--- /dev/null
+#version 120
+
+uniform float zoom;
+
+varying vec3 ec_pos;
+
+void main() {
+ vec3 ec_normal = normalize(cross(dFdx(ec_pos), dFdy(ec_pos)));
+ ec_normal.z *= zoom;
+ ec_normal = normalize(ec_normal);
+ //rotated 10deg around the red axis for better color match
+ float x = dot(ec_normal, vec3(1.0, 0.0, 0.0));
+ float y = dot(ec_normal, vec3(0.0, 0.985, 0.174));
+ float z = dot(ec_normal, vec3(0.0, -0.174, 0.985));
+
+ gl_FragColor = vec4(0.5-0.5*x, 0.5-0.5*y, 0.5+0.5*z, 1.0);
+}
+++ /dev/null
-QT += core gui opengl widgets
-
-TARGET = fstl
-TEMPLATE = app
-
-# Bump optimization up to -O3 in release builds
-QMAKE_CXXFLAGS_RELEASE -= -O2
-QMAKE_CXXFLAGS_RELEASE += -O3
-
-SOURCES += \
- ../src/app.cpp\
- ../src/main.cpp\
- ../src/canvas.cpp \
- ../src/mesh.cpp \
- ../src/glmesh.cpp \
- ../src/loader.cpp \
- ../src/window.cpp \
- ../src/backdrop.cpp
-
-HEADERS += \
- ../src/app.h\
- ../src/canvas.h \
- ../src/mesh.h \
- ../src/glmesh.h \
- ../src/loader.h \
- ../src/window.h \
- ../src/backdrop.h
-
-CONFIG += c++11
-
-RESOURCES += \
- qt.qrc \
- ../gl/gl.qrc
-
-macx {
- QMAKE_INFO_PLIST = ../app/Info.plist
- ICON = ../app/fstl.icns
-}
-
-win32 {
- RC_FILE = ../exe/fstl.rc
-}
-
-linux {
- target.path = /usr/bin
- INSTALLS += target
-}
-
-static {
- CONFIG += static
-}
App::App(int& argc, char *argv[]) :
QApplication(argc, argv), window(new Window())
{
- QCoreApplication::setOrganizationName("mkeeter");
- QCoreApplication::setOrganizationDomain("https://github.com/mkeeter/fstl");
- QCoreApplication::setApplicationName("fstl");
-
if (argc > 1)
window->load_stl(argv[1]);
else
App::~App()
{
- delete window;
+ delete window;
}
bool App::event(QEvent* e)
--- /dev/null
+#include "axis.h"
+
+const float xLet[] = {
+ -0.1, -0.2, 0,
+ 0.1, 0.2, 0,
+ 0.1, -0.2, 0,
+ -0.1, 0.2, 0
+};
+const float yLet[] = {
+ 0, -0.2, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0.1, 0.2, 0,
+ 0, 0, 0,
+ -0.1, 0.2, 0
+};
+const float zLet[] = {
+ -0.1, -0.2, 0,
+ 0.1, -0.2, 0,
+ 0.1, -0.2, 0,
+ -0.1, 0.2, 0,
+ -0.1, 0.2, 0,
+ 0.1, 0.2, 0
+};
+const int axisSegCount[] = {2, 3, 3};
+const float* axisLabels[] = {xLet, yLet, zLet};
+
+Axis::Axis()
+{
+ initializeOpenGLFunctions();
+
+ shader.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/gl/colored_lines.vert");
+ shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/colored_lines.frag");
+ shader.link();
+ const int ptSize = 6*sizeof(float);
+ for(int lIdx = 0; lIdx < 3; lIdx++)
+ {
+ const float* l = axisLabels[lIdx];
+ const int ptCount = axisSegCount[lIdx]*2;
+ float c[3] = {0.0};
+ c[lIdx] = 1.0;//set color
+ QOpenGLBuffer b = flowerLabelVertices[lIdx];
+ b.create();
+ b.bind();
+ b.allocate(ptCount*ptSize);
+ for(int pIdx = 0; pIdx < ptCount; pIdx++)
+ {
+ b.write(pIdx*ptSize, &(l[pIdx*3]), ptSize/2);//write coords
+ b.write(pIdx*ptSize + ptSize/2, c, ptSize/2);//write color
+ }
+ b.release();
+ }
+ //Axis buffer: 6 floats per vertex, 2 vert per line, 3 lines
+ float aBuf[6*2*3] = {0.0};
+ for(int aIdx = 0; aIdx < 3; aIdx++)
+ {
+ aBuf[(2*aIdx)*6+3+aIdx] = 1.0;//Set color (last 3 floats)
+ aBuf[(2*aIdx+1)*6+3+aIdx] = 1.0;//Set color (last 3 floats)
+ aBuf[(2*aIdx+1)*6+aIdx] = 1.0;//Extend line in axis
+ }
+ //The lines which form the 'axis-flower' in the corner
+ flowerAxisVertices.create();
+ flowerAxisVertices.bind();
+ flowerAxisVertices.allocate(aBuf, sizeof(aBuf));
+ flowerAxisVertices.release();
+ //The lines which form the model-space axes
+ vertices.create();
+ vertices.bind();
+ vertices.allocate(aBuf, sizeof(aBuf));
+ vertices.release();
+}
+void Axis::setScale(QVector3D min, QVector3D max)
+{
+ //Max function. not worth importing <algorithm> just for max
+ auto Max = [](float a, float b)
+ {
+ return (a > b) ? a : b;
+ };
+ //This is how much the axes extend beyond the model
+ //We want it to be dependent on the model's size, but uniform on all axes.
+ const float axismargin = 0.25*Max(Max(max[0]-min[0], max[1]-min[1]), max[2]-min[2]);
+ vertices.bind();
+ //Manually rewrite coordinates to control axis draw lengths
+ float s = sizeof(float);
+ //aIdx*12+aIdx gets us to the set of 2 points of the axis line, plus the offset for that dimension
+ //+6 gets us to the other end of the line in that dimension
+ for(int aIdx = 0; aIdx < 3; aIdx++)
+ {
+ float t = min[aIdx]-axismargin;
+ vertices.write(s*(aIdx*12+aIdx), &t, s);
+ t = max[aIdx]+axismargin;
+ vertices.write(s*(aIdx*12+aIdx+6), &t, s);
+ }
+ vertices.release();
+}
+void Axis::draw(QMatrix4x4 transMat, QMatrix4x4 viewMat,
+ QMatrix4x4 orientMat, QMatrix4x4 aspectMat, float aspectRatio)
+{
+ shader.bind();
+ vertices.bind();
+ // Load the transform and view matrices into the shader
+ auto loadMatrixUniforms = [&](QMatrix4x4 transform, QMatrix4x4 view)
+ {
+ glUniformMatrix4fv(
+ shader.uniformLocation("transform_matrix"),
+ 1, GL_FALSE, transform.data());
+ glUniformMatrix4fv(
+ shader.uniformLocation("view_matrix"),
+ 1, GL_FALSE, view.data());
+ };
+ const GLuint vp = shader.attributeLocation("vertex_position");
+ const GLuint vc = shader.attributeLocation("vertex_color");
+ glEnableVertexAttribArray(vp);
+ glEnableVertexAttribArray(vc);
+ auto loadAttribPtr = [&]()
+ {
+ glVertexAttribPointer(vp, 3, GL_FLOAT, false,
+ 6 * sizeof(GLfloat), 0);
+ glVertexAttribPointer(vc, 3, GL_FLOAT, false,
+ 6 * sizeof(GLfloat),
+ (GLvoid*)(3 * sizeof(GLfloat)));
+ };
+ loadMatrixUniforms(transMat, viewMat);
+ loadAttribPtr();
+
+ glDrawArrays(GL_LINES, 0, 3*6);
+
+ vertices.release();
+ //Next, we draw the hud axis-flower
+ flowerAxisVertices.bind();
+ glClear(GL_DEPTH_BUFFER_BIT);//Ensure hud draws over everything
+ const float hudSize = 0.2;
+ QMatrix4x4 hudMat;
+ //Move the hud to the bottom left corner with margin
+ if (aspectRatio > 1.0)
+ {
+ hudMat.translate(aspectRatio-2*hudSize, -1.0+2*hudSize, 0);
+ }
+ else
+ {
+ hudMat.translate(1.0-2*hudSize, -1.0/aspectRatio+2*hudSize, 0);
+ }
+ //Scale the hud to be small
+ hudMat.scale(hudSize, hudSize, 1);
+ loadMatrixUniforms(orientMat, aspectMat*hudMat);
+ loadAttribPtr();
+ glDrawArrays(GL_LINES, 0, 3*6);
+ flowerAxisVertices.release();
+ for(int aIdx = 0; aIdx < 3; aIdx++){
+ QVector3D transVec = QVector3D();
+ transVec[aIdx] = 1.25;//This is how far we want the letters to be extended out
+ QOpenGLBuffer b = flowerLabelVertices[aIdx];
+ //The only transform we want is to translate the letters to the ends of the axis lines
+ QMatrix4x4 labelTransMat = QMatrix4x4();
+ labelTransMat.translate(orientMat * transVec);
+ b.bind();
+ loadMatrixUniforms(labelTransMat, aspectMat * hudMat);
+ loadAttribPtr();
+ glDrawArrays(GL_LINES, 0, axisSegCount[aIdx]*2*6);
+ b.release();
+ }
+ shader.release();
+}
--- /dev/null
+#ifndef AXIS_H
+#define AXIS_H
+
+#include <QOpenGLBuffer>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLFunctions>
+
+class Axis : protected QOpenGLFunctions
+{
+public:
+ Axis();
+ void setScale(QVector3D min, QVector3D max);
+ void draw(QMatrix4x4 transMat, QMatrix4x4 viewMat,
+ QMatrix4x4 orientMat, QMatrix4x4 aspectMat, float aspectRatio);
+private:
+ QOpenGLShaderProgram shader;
+ QOpenGLBuffer vertices, //GL Buffer for model-space coords
+ flowerAxisVertices; //GL Buffer for hud-space axis lines
+ QOpenGLBuffer flowerLabelVertices[3];//Buffer for hud-space label lines
+};
+
+#endif // AXIS_H
#include "canvas.h"
#include "backdrop.h"
+#include "axis.h"
#include "glmesh.h"
#include "mesh.h"
+const float Canvas::P_PERSPECTIVE = 0.25f;
+const float Canvas::P_ORTHOGRAPHIC = 0.0f;
+
Canvas::Canvas(const QSurfaceFormat& format, QWidget *parent)
: QOpenGLWidget(parent), mesh(nullptr),
scale(1), zoom(1), tilt(90), yaw(0),
- perspective(0.25), anim(this, "perspective"), status(" ")
+ anim(this, "perspective"), status(" "),
+ meshInfo("")
{
- setFormat(format);
+ setFormat(format);
QFile styleFile(":/qt/style.qss");
styleFile.open( QFile::ReadOnly );
setStyleSheet(styleFile.readAll());
Canvas::~Canvas()
{
- makeCurrent();
- delete mesh;
- doneCurrent();
+ makeCurrent();
+ delete mesh;
+ delete mesh_vertshader;
+ delete backdrop;
+ delete axis;
+ doneCurrent();
}
void Canvas::view_anim(float v)
anim.start();
}
-void Canvas::view_orthographic()
-{
- view_anim(0);
-}
-
-void Canvas::view_perspective()
-{
- view_anim(0.25);
+void Canvas::view_perspective(float p, bool animate){
+ if(animate)
+ {
+ view_anim(p);
+ }
+ else
+ {
+ set_perspective(p);
+ }
}
-void Canvas::draw_shaded()
+void Canvas::draw_axes(bool d)
{
- set_drawMode(0);
+ drawAxes = d;
+ update();
}
-void Canvas::draw_wireframe()
+void Canvas::invert_zoom(bool d)
{
- set_drawMode(1);
+ invertZoom = d;
+ update();
}
void Canvas::load_mesh(Mesh* m, bool is_reload)
{
+ delete mesh;
mesh = new GLMesh(m);
-
+ QVector3D lower(m->xmin(), m->ymin(), m->zmin());
+ QVector3D upper(m->xmax(), m->ymax(), m->zmax());
if (!is_reload)
{
- QVector3D lower(m->xmin(), m->ymin(), m->zmin());
- QVector3D upper(m->xmax(), m->ymax(), m->zmax());
center = (lower + upper) / 2;
scale = 2 / (upper - lower).length();
yaw = 0;
tilt = 90;
}
-
+ 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]);
+ axis->setScale(lower, upper);
update();
delete m;
update();
}
-void Canvas::set_drawMode(int mode)
+void Canvas::set_drawMode(enum DrawMode mode)
{
drawMode = mode;
update();
{
initializeOpenGLFunctions();
- mesh_shader.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/gl/mesh.vert");
+ mesh_vertshader = new QOpenGLShader(QOpenGLShader::Vertex);
+ mesh_vertshader->compileSourceFile(":/gl/mesh.vert");
+ mesh_shader.addShader(mesh_vertshader);
mesh_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh.frag");
mesh_shader.link();
- mesh_wireframe_shader.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/gl/mesh.vert");
+ mesh_wireframe_shader.addShader(mesh_vertshader);
mesh_wireframe_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh_wireframe.frag");
mesh_wireframe_shader.link();
+ mesh_surfaceangle_shader.addShader(mesh_vertshader);
+ mesh_surfaceangle_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh_surfaceangle.frag");
+ mesh_surfaceangle_shader.link();
backdrop = new Backdrop();
+ axis = new Axis();
}
void Canvas::paintGL()
{
- glClearColor(0.0, 0.0, 0.0, 0.0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glEnable(GL_DEPTH_TEST);
-
- backdrop->draw();
- if (mesh) draw_mesh();
-
- if (status.isNull()) return;
-
- QPainter painter(this);
- painter.setRenderHint(QPainter::Antialiasing);
- painter.drawText(10, height() - 10, status);
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glEnable(GL_DEPTH_TEST);
+ backdrop->draw();
+ if (mesh) draw_mesh();
+ if (drawAxes) axis->draw(transform_matrix(), view_matrix(),
+ orient_matrix(), aspect_matrix(), width() / float(height()));
+
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+ float textHeight = painter.fontInfo().pointSize();
+ if (drawAxes) painter.drawText(QRect(10, textHeight, width(), height()), meshInfo);
+ painter.drawText(10, height() - textHeight, status);
}
void Canvas::draw_mesh()
{
QOpenGLShaderProgram* selected_mesh_shader = NULL;
- // Set gl draw mode
- if(drawMode == 1)
+ if(drawMode == wireframe)
{
selected_mesh_shader = &mesh_wireframe_shader;
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
else
{
- selected_mesh_shader = &mesh_shader;
+ if(drawMode == shaded)
+ {
+ selected_mesh_shader = &mesh_shader;
+ }
+ else
+ {
+ selected_mesh_shader = &mesh_surfaceangle_shader;
+ }
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
glDisableVertexAttribArray(vp);
selected_mesh_shader->release();
}
-
-QMatrix4x4 Canvas::transform_matrix() const
+QMatrix4x4 Canvas::orient_matrix() const
{
QMatrix4x4 m;
m.rotate(tilt, QVector3D(1, 0, 0));
m.rotate(yaw, QVector3D(0, 0, 1));
- m.scale(-scale, scale, -scale);
+ //We want the x axis to the right, and the z axis up
+ m.scale(-1, 1, -1);
+ return m;
+}
+QMatrix4x4 Canvas::transform_matrix() const
+{
+ QMatrix4x4 m = orient_matrix();
+ m.scale(scale);
m.translate(-center);
return m;
}
-
-QMatrix4x4 Canvas::view_matrix() const
+QMatrix4x4 Canvas::aspect_matrix() const
{
QMatrix4x4 m;
if (width() > height())
{
m.scale(-1, width() / float(height()), 0.5);
}
+ return m;
+}
+QMatrix4x4 Canvas::view_matrix() const
+{
+ QMatrix4x4 m = aspect_matrix();
m.scale(zoom, zoom, 1);
m(3, 2) = perspective;
return m;
{
// Find GL position before the zoom operation
// (to zoom about mouse cursor)
- auto p = event->pos();
+ auto p = event->position();
QVector3D v(1 - p.x() / (0.5*width()),
p.y() / (0.5*height()) - 1, 0);
QVector3D a = transform_matrix().inverted() *
view_matrix().inverted() * v;
- if (event->delta() < 0)
+ if (event->angleDelta().y() < 0)
{
- for (int i=0; i > event->delta(); --i)
- zoom *= 1.001;
+ for (int i=0; i > event->angleDelta().y(); --i)
+ if (invertZoom)
+ zoom /= 1.001;
+ else
+ zoom *= 1.001;
}
- else if (event->delta() > 0)
+ else if (event->angleDelta().y() > 0)
{
- for (int i=0; i < event->delta(); ++i)
- zoom /= 1.001;
+ for (int i=0; i < event->angleDelta().y(); ++i)
+ if (invertZoom)
+ zoom *= 1.001;
+ else
+ zoom /= 1.001;
}
// Then find the cursor's GL position post-zoom and adjust center.
class GLMesh;
class Mesh;
class Backdrop;
+class Axis;
+
+enum DrawMode {shaded, wireframe, surfaceangle, DRAWMODECOUNT};
class Canvas : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
- explicit Canvas(const QSurfaceFormat& format, QWidget* parent=0);
+ explicit Canvas(const QSurfaceFormat& format, QWidget* parent=0);
~Canvas();
- void view_orthographic();
- void view_perspective();
- void draw_shaded();
- void draw_wireframe();
+ const static float P_PERSPECTIVE;
+ const static float P_ORTHOGRAPHIC;
+
+ void view_perspective(float p, bool animate);
+ void draw_axes(bool d);
+ void invert_zoom(bool d);
+ void set_drawMode(enum DrawMode mode);
public slots:
void set_status(const QString& s);
void load_mesh(Mesh* m, bool is_reload);
protected:
- void paintGL() override;
- void initializeGL() override;
- void resizeGL(int width, int height) override;
+ void paintGL() override;
+ void initializeGL() override;
+ void resizeGL(int width, int height) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
- void set_perspective(float p);
- void set_drawMode(int mode);
+ void set_perspective(float p);
void view_anim(float v);
private:
void draw_mesh();
+ QMatrix4x4 orient_matrix() const;
QMatrix4x4 transform_matrix() const;
+ QMatrix4x4 aspect_matrix() const;
QMatrix4x4 view_matrix() const;
+ QOpenGLShader* mesh_vertshader;
QOpenGLShaderProgram mesh_shader;
QOpenGLShaderProgram mesh_wireframe_shader;
- QOpenGLShaderProgram quad_shader;
+ QOpenGLShaderProgram mesh_surfaceangle_shader;
GLMesh* mesh;
Backdrop* backdrop;
+ Axis* axis;
QVector3D center;
float scale;
float yaw;
float perspective;
- int drawMode;
+ enum DrawMode drawMode;
+ bool drawAxes;
+ bool invertZoom;
Q_PROPERTY(float perspective MEMBER perspective WRITE set_perspective);
QPropertyAnimation anim;
QPoint mouse_pos;
QString status;
+ QString meshInfo;
};
#endif // CANVAS_H
file.seek(0);
return read_stl_ascii(file);
}
- confusing_stl = true;
- }
- else
- {
- confusing_stl = false;
+ // Otherwise, this STL is a binary stl but contains 'solid' as
+ // the first five characters. This is a bad life choice, but
+ // we can gracefully handle it by falling through to the binary
+ // STL reader below.
}
- // Otherwise, skip the rest of the header material and read as binary
file.seek(0);
return read_stl_binary(file);
}
QVector<Vertex> verts(tri_count*3);
// Dummy array, because readRawData is faster than skipRawData
- std::unique_ptr<uint8_t> buffer(new uint8_t[tri_count * 50]);
+ std::unique_ptr<uint8_t[]> buffer(new uint8_t[tri_count * 50]);
data.readRawData((char*)buffer.get(), tri_count * 50);
// Store vertices in the array, processing one triangle at a time.
// Load vertex data from .stl file into vertices
for (unsigned i=0; i < 3; ++i)
{
- memcpy(&v[i], b, 3*sizeof(float));
+ qFromLittleEndian<float>(b, 3, &v[i]);
b += 3 * sizeof(float);
}
b += 3 * sizeof(float) + sizeof(uint16_t);
}
- if (confusing_stl)
- {
- emit warning_confusing_stl();
- }
-
return mesh_from_verts(tri_count, verts);
}
void error_bad_stl();
void error_empty_mesh();
- void warning_confusing_stl();
void error_missing_file();
private:
const QString filename;
bool is_reload;
-
- /* Used to warn on binary STLs that begin with the word 'solid'" */
- bool confusing_stl;
-
};
#endif // LOADER_H
int main(int argc, char *argv[])
{
+ 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();
}
return v;
}
+int Mesh::triCount() const
+{
+ return indices.size()/3;
+}
bool Mesh::empty() const
{
return vertices.size() == 0;
float ymax() const { return max(1); }
float zmax() const { return max(2); }
+ int triCount() const;
bool empty() const;
private:
#include "loader.h"
const QString Window::RECENT_FILE_KEY = "recentFiles";
+const QString Window::INVERT_ZOOM_KEY = "invertZoom";
+const QString Window::AUTORELOAD_KEY = "autoreload";
+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";
Window::Window(QWidget *parent) :
QMainWindow(parent),
about_action(new QAction("About", this)),
quit_action(new QAction("Quit", this)),
perspective_action(new QAction("Perspective", this)),
- orthogonal_action(new QAction("Orthographic", 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)),
+ 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)),
recent_files(new QMenu("Open recent", this)),
recent_files_group(new QActionGroup(this)),
recent_files_clear_action(new QAction("Clear recent files", this)),
open_action->setShortcut(QKeySequence::Open);
QObject::connect(open_action, &QAction::triggered,
this, &Window::on_open);
+ this->addAction(open_action);
quit_action->setShortcut(QKeySequence::Quit);
QObject::connect(quit_action, &QAction::triggered,
this, &Window::close);
+ this->addAction(quit_action);
autoreload_action->setCheckable(true);
- autoreload_action->setChecked(true);
- autoreload_action->setEnabled(false);
QObject::connect(autoreload_action, &QAction::triggered,
this, &Window::on_autoreload_triggered);
auto view_menu = menuBar()->addMenu("View");
auto projection_menu = view_menu->addMenu("Projection");
projection_menu->addAction(perspective_action);
- projection_menu->addAction(orthogonal_action);
+ projection_menu->addAction(orthographic_action);
auto projections = new QActionGroup(projection_menu);
- for (auto p : {perspective_action, orthogonal_action})
+ for (auto p : {perspective_action, orthographic_action})
{
projections->addAction(p);
p->setCheckable(true);
}
- perspective_action->setChecked(true);
projections->setExclusive(true);
QObject::connect(projections, &QActionGroup::triggered,
this, &Window::on_projection);
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})
+ for (auto p : {shaded_action, wireframe_action, surfaceangle_action})
{
drawModes->addAction(p);
p->setCheckable(true);
}
- shaded_action->setChecked(true);
drawModes->setExclusive(true);
QObject::connect(drawModes, &QActionGroup::triggered,
this, &Window::on_drawMode);
+ view_menu->addAction(axes_action);
+ axes_action->setCheckable(true);
+ QObject::connect(axes_action, &QAction::triggered,
+ this, &Window::on_drawAxes);
+
+ view_menu->addAction(invert_zoom_action);
+ invert_zoom_action->setCheckable(true);
+ QObject::connect(invert_zoom_action, &QAction::triggered,
+ this, &Window::on_invertZoom);
+
+ view_menu->addAction(hide_menuBar_action);
+ hide_menuBar_action->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C);
+ hide_menuBar_action->setCheckable(true);
+ QObject::connect(hide_menuBar_action, &QAction::toggled,
+ this, &Window::on_hide_menuBar);
+ this->addAction(hide_menuBar_action);
auto help_menu = menuBar()->addMenu("Help");
help_menu->addAction(about_action);
+ load_persist_settings();
+}
+
+void Window::load_persist_settings(){
+ QSettings settings;
+ bool invert_zoom = settings.value(INVERT_ZOOM_KEY, false).toBool();
+ canvas->invert_zoom(invert_zoom);
+ invert_zoom_action->setChecked(invert_zoom);
+
+ autoreload_action->setChecked(settings.value(AUTORELOAD_KEY, true).toBool());
+
+ bool draw_axes = settings.value(DRAW_AXES_KEY, false).toBool();
+ canvas->draw_axes(draw_axes);
+ axes_action->setChecked(draw_axes);
+
+ QString projection = settings.value(PROJECTION_KEY, "perspective").toString();
+ if(projection == "perspective"){
+ canvas->view_perspective(Canvas::P_PERSPECTIVE, false);
+ perspective_action->setChecked(true);
+ }else{
+ canvas->view_perspective(Canvas::P_ORTHOGRAPHIC, false);
+ orthographic_action->setChecked(true);
+ }
+
+ 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};
+ dm_acts[draw_mode]->setChecked(true);
+
resize(600, 400);
+ restoreGeometry(settings.value(WINDOW_GEOM_KEY).toByteArray());
}
void Window::on_open()
{
QString filename = QFileDialog::getOpenFileName(
- this, "Load .stl file", QString(), "*.stl");
+ this, "Load .stl file", QString(), "STL files (*.stl, *.STL)");
if (!filename.isNull())
{
load_stl(filename);
void Window::on_about()
{
QMessageBox::about(this, "",
- "<p align=\"center\"><b>fstl</b></p>"
+ "<p align=\"center\"><b>fstl</b><br>" FSTL_VERSION "</p>"
"<p>A fast viewer for <code>.stl</code> files.<br>"
- "<a href=\"https://github.com/mkeeter/fstl\""
- " style=\"color: #93a1a1;\">https://github.com/mkeeter/fstl</a></p>"
- "<p>© 2014-2017 Matthew Keeter<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>"
"<a href=\"mailto:matt.j.keeter@gmail.com\""
" style=\"color: #93a1a1;\">matt.j.keeter@gmail.com</a></p>");
}
"This file is syntactically correct<br>but contains no triangles.");
}
-void Window::on_confusing_stl()
-{
- QMessageBox::warning(this, "Warning",
- "<b>Warning:</b><br>"
- "This <code>.stl</code> file begins with <code>solid </code>but appears to be a binary file.<br>"
- "<code>fstl</code> loaded it, but other programs may be confused by this file.");
-}
-
void Window::on_missing_file()
{
QMessageBox::critical(this, "Error",
{
if (proj == perspective_action)
{
- canvas->view_perspective();
+ canvas->view_perspective(Canvas::P_PERSPECTIVE, true);
+ QSettings().setValue(PROJECTION_KEY, "perspective");
}
else
{
- canvas->view_orthographic();
+ canvas->view_perspective(Canvas::P_ORTHOGRAPHIC, true);
+ QSettings().setValue(PROJECTION_KEY, "orthographic");
}
}
-void Window::on_drawMode(QAction* mode)
+void Window::on_drawMode(QAction* act)
{
- if (mode == shaded_action)
+ DrawMode mode;
+ if (act == shaded_action)
{
- canvas->draw_shaded();
+ mode = shaded;
+ }
+ else if (act == wireframe_action)
+ {
+ mode = wireframe;
}
else
{
- canvas->draw_wireframe();
+ mode = surfaceangle;
}
+ canvas->set_drawMode(mode);
+ QSettings().setValue(DRAW_MODE_KEY, mode);
+}
+
+void Window::on_drawAxes(bool d)
+{
+ canvas->draw_axes(d);
+ QSettings().setValue(DRAW_AXES_KEY, d);
+}
+
+void Window::on_invertZoom(bool d)
+{
+ canvas->invert_zoom(d);
+ QSettings().setValue(INVERT_ZOOM_KEY, d);
}
void Window::on_watched_change(const QString& filename)
{
on_reload();
}
+ QSettings().setValue(AUTORELOAD_KEY, b);
}
void Window::on_clear_recent()
}
}
+void Window::on_hide_menuBar()
+{
+ menuBar()->setVisible(!hide_menuBar_action->isChecked());
+}
+
void Window::rebuild_recent_files()
{
QSettings settings;
this, &Window::on_bad_stl);
connect(loader, &Loader::error_empty_mesh,
this, &Window::on_empty_mesh);
- connect(loader, &Loader::warning_confusing_stl,
- this, &Window::on_confusing_stl);
connect(loader, &Loader::error_missing_file,
this, &Window::on_missing_file);
this, &Window::set_watched);
connect(loader, &Loader::loaded_file,
this, &Window::on_loaded);
- autoreload_action->setEnabled(true);
reload_action->setEnabled(true);
}
load_stl(event->mimeData()->urls().front().toLocalFile());
}
+void Window::resizeEvent(QResizeEvent *event)
+{
+ QSettings().setValue(WINDOW_GEOM_KEY, saveGeometry());
+ QWidget::resizeEvent(event);
+}
+
+void Window::moveEvent(QMoveEvent *event)
+{
+ QSettings().setValue(WINDOW_GEOM_KEY, saveGeometry());
+ QWidget::moveEvent(event);
+}
+
void Window::sorted_insert(QStringList& list, const QCollator& collator, const QString& value)
{
int start = 0;
QPair<QString, QString> Window::get_file_neighbors()
{
if (current_file.isEmpty()) {
- return QPair<QString, QString>(QString::null, QString::null);
+ return QPair<QString, QString>(QString(), QString());
}
build_folder_file_list();
QString current_dir = fileInfo.absoluteDir().absolutePath();
QString current_name = fileInfo.fileName();
- QString prev = QString::null;
- QString next = QString::null;
+ QString prev = QString();
+ QString next = QString();
QListIterator<QString> fileIterator(lookup_folder_files);
while (fileIterator.hasNext()) {
load_next();
return;
}
+ else if (event->key() == Qt::Key_Escape)
+ {
+ hide_menuBar_action->setChecked(false);
+ return;
+ }
QMainWindow::keyPressEvent(event);
}
protected:
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void moveEvent(QMoveEvent *event) override;
void keyPressEvent(QKeyEvent* event) override;
public slots:
void on_bad_stl();
void on_empty_mesh();
void on_missing_file();
- void on_confusing_stl();
void enable_open();
void disable_open();
private slots:
void on_projection(QAction* proj);
void on_drawMode(QAction* mode);
+ void on_drawAxes(bool d);
+ void on_invertZoom(bool d);
void on_watched_change(const QString& filename);
void on_reload();
void on_autoreload_triggered(bool r);
void on_load_recent(QAction* a);
void on_loaded(const QString& filename);
void on_save_screenshot();
-
+ void on_hide_menuBar();
+
private:
void rebuild_recent_files();
+ void load_persist_settings();
void sorted_insert(QStringList& list, const QCollator& collator, const QString& value);
void build_folder_file_list();
QPair<QString, QString> get_file_neighbors();
QAction* const about_action;
QAction* const quit_action;
QAction* const perspective_action;
- QAction* const orthogonal_action;
+ QAction* const orthographic_action;
QAction* const shaded_action;
QAction* const wireframe_action;
+ QAction* const surfaceangle_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;
QMenu* const recent_files;
QActionGroup* const recent_files_group;
QAction* const recent_files_clear_action;
const static int MAX_RECENT_FILES=8;
const static QString RECENT_FILE_KEY;
+ const static QString INVERT_ZOOM_KEY;
+ const static QString AUTORELOAD_KEY;
+ const static QString DRAW_AXES_KEY;
+ const static QString PROJECTION_KEY;
+ const static QString DRAW_MODE_KEY;
+ const static QString WINDOW_GEOM_KEY;
+
QString current_file;
QString lookup_folder;
QStringList lookup_folder_files;