--- /dev/null
+# see https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+---
+BasedOnStyle: LLVM
+Language: Cpp
+Standard: Cpp11
+
+ColumnLimit: 135
+
+AccessModifierOffset: -4
+IndentWidth: 4
+UseTab: Never
+
+BreakBeforeBraces: Linux
+AlignEscapedNewlines: Left
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortLambdasOnASingleLine: Empty
+AlwaysBreakTemplateDeclarations: true
+BreakConstructorInitializers: AfterColon
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+IndentPPDirectives: AfterHash
+PointerAlignment: Left
\ No newline at end of file
--- /dev/null
+name: Build
+
+on:
+ push:
+ branches: [main, master]
+ pull_request:
+ branches: [main, master]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ compiler: [gcc, clang]
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y qt5-qmake qtbase5-dev build-essential clang clang-format
+
+ - name: Set compiler
+ run: |
+ if [ "${{ matrix.compiler }}" = "clang" ]; then
+ export CC=clang
+ export CXX=clang++
+ else
+ export CC=gcc
+ export CXX=g++
+ fi
+ echo "CC=$CC" >> $GITHUB_ENV
+ echo "CXX=$CXX" >> $GITHUB_ENV
+
+ - name: Build
+ run: |
+ cmake -S . -B build -DFSTL_CHECK_FORMAT=ON
+ cmake --build build --target fstl --config Release -- -j$(nproc)
+
+ - name: Check format
+ run: |
+ # Print the version for troubleshooting purposes
+ clang-format --version
+ cmake --build build --target check-format
# Original Project Author: Matt Keeter Copyright 2014-2024
# Author: Paul Tsouchlos Copyright 2017-2024
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.10)
project(fstl)
-# Setting -std=c++11
-set(CMAKE_CXX_STANDARD 11)
-# Setting standard to required, as requisted by DeveloperPaul123 on github
+# Setting -std=c++14
+set(CMAKE_CXX_STANDARD 14)
set(CXX_STANDARD_REQUIRED ON)
+option(FSTL_CHECK_FORMAT "Check source code formatting with clang-format" OFF)
+if(FSTL_CHECK_FORMAT)
+ find_program(CLANG_FORMAT_EXE NAMES clang-format)
+ if(NOT CLANG_FORMAT_EXE)
+ message(FATAL_ERROR "Could not find clang-format executable!")
+ endif()
+
+ file(GLOB_RECURSE ALL_SOURCE_FILES CONFIGURE_DEPENDS
+ src/*.h
+ src/*.cpp
+ qt/*.h
+ qt/*.cpp
+ gl/*.h
+ gl/*.cpp
+ exe/*.h
+ exe/*.cpp
+ )
+
+ add_custom_target(check-format
+ COMMAND ${CLANG_FORMAT_EXE}
+ --Werror
+ --style=file
+ --dry-run
+ ${ALL_SOURCE_FILES}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "Checking source code formatting"
+ VERBATIM)
+ add_custom_target(fix-format
+ COMMAND ${CLANG_FORMAT_EXE}
+ --style=file
+ -i
+ ${ALL_SOURCE_FILES}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "Fixing source code formatting"
+ VERBATIM)
+endif()
+
# Set the version number
set (FSTL_VERSION_MAJOR "0")
set (FSTL_VERSION_MINOR "11")
-set (FSTL_VERSION_PATCH "0")
+set (FSTL_VERSION_PATCH "1")
set (PROJECT_VERSION "${FSTL_VERSION_MAJOR}.${FSTL_VERSION_MINOR}.${FSTL_VERSION_PATCH}")
message(STATUS "Version: ${PROJECT_VERSION}")
set(OpenGL_GL_PREFERENCE GLVND)
#find required packages.
-find_package(Qt5 5.14 REQUIRED COMPONENTS Core Gui Widgets OpenGL)
+find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets OpenGL)
find_package(OpenGL REQUIRED)
find_package(Threads REQUIRED)
#include <QDebug>
-#include <QFileOpenEvent>
#include <QDir>
+#include <QFileOpenEvent>
#include "app.h"
#include "window.h"
-App::App(int& argc, char *argv[]) :
- QApplication(argc, argv), window(new Window())
+App::App(int& argc, char* argv[]) : QApplication(argc, argv), window(new Window())
{
if (argc > 1) {
- QString filename = argv[1];
+ const auto args = QCoreApplication::arguments();
+ QString filename = args.at(1);
if (filename.startsWith("~")) {
filename.replace(0, 1, QDir::homePath());
}
window->load_stl(filename);
- }
- else
- {
+ } else {
window->load_stl(":gl/sphere.stl");
}
window->show();
bool App::event(QEvent* e)
{
- if (e->type() == QEvent::FileOpen)
- {
+ if (e->type() == QEvent::FileOpen) {
window->load_stl(static_cast<QFileOpenEvent*>(e)->file());
return true;
- }
- else
- {
+ } else {
return QApplication::event(e);
}
}
{
Q_OBJECT
public:
- explicit App(int& argc, char *argv[]);
- ~App();
+ explicit App(int& argc, char* argv[]);
+ ~App();
+
protected:
bool event(QEvent* e) override;
+
private:
Window* const window;
-
};
#endif // APP_H
#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 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};
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 int ptSize = 6 * sizeof(float);
+ for (int lIdx = 0; lIdx < 3; lIdx++) {
const float* l = axisLabels[lIdx];
- const int ptCount = axisSegCount[lIdx]*2;
+ const int ptCount = axisSegCount[lIdx] * 2;
float c[3] = {0.0};
- c[lIdx] = 1.0;//set color
+ 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.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
+ // 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
+ // 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
+ // The lines which form the model-space axes
vertices.create();
vertices.bind();
vertices.allocate(aBuf, sizeof(aBuf));
}
void Axis::setScale(QVector3D min, QVector3D max)
{
- //Max function. not worth importing <algorithm> just for max
- auto Max = [](float a, float b)
- {
+ // 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]);
+ // 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
+ // 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
+ // 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);
+ 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)
+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());
+ 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)));
+ 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);
+ glDrawArrays(GL_LINES, 0, 3 * 6);
vertices.release();
- //Next, we draw the hud axis-flower
+ // Next, we draw the hud axis-flower
flowerAxisVertices.bind();
- glClear(GL_DEPTH_BUFFER_BIT);//Ensure hud draws over everything
+ 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);
+ // 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);
}
- else
- {
- hudMat.translate(1.0-2*hudSize, -1.0/aspectRatio+2*hudSize, 0);
- }
- //Scale the hud to be small
+ // Scale the hud to be small
hudMat.scale(hudSize, hudSize, 1);
- loadMatrixUniforms(orientMat, aspectMat*hudMat);
+ loadMatrixUniforms(orientMat, aspectMat * hudMat);
loadAttribPtr();
- glDrawArrays(GL_LINES, 0, 3*6);
+ glDrawArrays(GL_LINES, 0, 3 * 6);
flowerAxisVertices.release();
- for(int aIdx = 0; aIdx < 3; aIdx++){
+ 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
+ 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
+ // 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);
+ glDrawArrays(GL_LINES, 0, axisSegCount[aIdx] * 2 * 6);
b.release();
}
shader.release();
#define AXIS_H
#include <QOpenGLBuffer>
-#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
+#include <QOpenGLShaderProgram>
class Axis : protected QOpenGLFunctions
{
public:
Axis();
void setScale(QVector3D min, QVector3D max);
- void draw(QMatrix4x4 transMat, QMatrix4x4 viewMat,
- QMatrix4x4 orientMat, QMatrix4x4 aspectMat, float aspectRatio);
+ 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
+ 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
shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/quad.frag");
shader.link();
- float vbuf[] = {
- -1, -1, 0.00, 0.10, 0.15,
- -1, 1, 0.03, 0.21, 0.26,
- 1, -1, 0.00, 0.12, 0.18,
- 1, 1, 0.06, 0.26, 0.30};
+ float vbuf[] = {-1, -1, 0.00, 0.10, 0.15, -1, 1, 0.03, 0.21, 0.26, 1, -1, 0.00, 0.12, 0.18, 1, 1, 0.06, 0.26, 0.30};
vertices.create();
vertices.bind();
glEnableVertexAttribArray(vp);
glEnableVertexAttribArray(vc);
- glVertexAttribPointer(vp, 2, GL_FLOAT, false,
- 5 * sizeof(GLfloat), 0);
- glVertexAttribPointer(vc, 3, GL_FLOAT, false,
- 5 * sizeof(GLfloat),
- (GLvoid*)(2 * sizeof(GLfloat)));
+ glVertexAttribPointer(vp, 2, GL_FLOAT, false, 5 * sizeof(GLfloat), 0);
+ glVertexAttribPointer(vc, 3, GL_FLOAT, false, 5 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat)));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 8);
#define BACKDROP_H
#include <QOpenGLBuffer>
-#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
+#include <QOpenGLShaderProgram>
class Backdrop : protected QOpenGLFunctions
{
public:
Backdrop();
void draw();
+
private:
QOpenGLShaderProgram shader;
QOpenGLBuffer vertices;
#include <cmath>
-#include "canvas.h"
-#include "backdrop.h"
#include "axis.h"
+#include "backdrop.h"
+#include "canvas.h"
#include "glmesh.h"
#include "mesh.h"
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 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),
- anim(this, "perspective"), status(" "),
- meshInfo("")
+namespace
+{
+/**
+ * Abstract differences between accessing QWheelEvent position data for different versions of Qt.
+ */
+auto position(const QWheelEvent* const event)
+{
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ return event->pos();
+#else
+ return event->position();
+#endif
+}
+} // namespace
+
+Canvas::Canvas(const QSurfaceFormat& format, QWidget* parent) :
+ QOpenGLWidget(parent), mesh(nullptr), scale(1), zoom(1), anim(this, "perspective"), status(" "), meshInfo("")
{
setFormat(format);
QFile styleFile(":/qt/style.qss");
- styleFile.open( QFile::ReadOnly );
+ 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>();
+ 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
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)) {
+ 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);
+ listDir << QVector3D((double)i, (double)j, (double)k);
}
}
}
}
- currentLightDirection = settings.value(CURRENT_LIGHT_DIRECTION,defaultCurrentLightDirection).value<int>();
+ currentLightDirection = settings.value(CURRENT_LIGHT_DIRECTION, defaultCurrentLightDirection).value<int>();
if (currentLightDirection < 0 || currentLightDirection >= nameDir.length()) {
currentLightDirection = defaultCurrentLightDirection;
}
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));
- }
+ 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:
update();
}
-void Canvas::view_perspective(float p, bool animate){
- if(animate)
- {
+void Canvas::view_perspective(float p, bool animate)
+{
+ if (animate) {
view_anim(p);
- }
- else
- {
+ } else {
set_perspective(p);
}
}
update();
}
-void Canvas::setResetTransformOnLoad(bool d) {
+void Canvas::setResetTransformOnLoad(bool d)
+{
resetTransformOnLoad = d;
}
-void Canvas::resetTransform() {
+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));
-
+ currentTransform.rotate(15.0, QVector3D(1, -sin(M_PI / 12), 0));
+
zoom = 1;
}
mesh = new GLMesh(m);
QVector3D lower(m->xmin(), m->ymin(), m->zmin());
QVector3D upper(m->xmax(), m->ymax(), m->zmax());
- if (!is_reload)
- {
+ if (!is_reload) {
default_center = center = (lower + upper) / 2;
default_scale = scale = 2 / (upper - lower).length();
}
}
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]);
+ for (int dIdx = 0; dIdx < 3; dIdx++)
+ meshInfo = meshInfo.arg(lower[dIdx]).arg(upper[dIdx]);
axis->setScale(lower, upper);
update();
delete m;
}
-void Canvas::set_status(const QString &s)
+void Canvas::set_status(const QString& s)
{
status = s;
update();
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 (drawAxes) axis->draw(transform_matrix(), view_matrix(),
- orient_matrix(), aspect_matrix(), width() / float(height()));
+ 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);
+ 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;
- if(drawMode == wireframe)
- {
+ if (drawMode == wireframe) {
selected_mesh_shader = &mesh_wireframe_shader;
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- }
- else
- {
- if(drawMode == shaded)
- {
+ } else {
+ if (drawMode == shaded) {
selected_mesh_shader = &mesh_shader;
- }
- else if (drawMode == surfaceangle)
- {
+ } else if (drawMode == surfaceangle) {
selected_mesh_shader = &mesh_surfaceangle_shader;
- }
- else if (drawMode == meshlight)
- {
+ } else if (drawMode == meshlight) {
selected_mesh_shader = &mesh_meshlight_shader;
}
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
selected_mesh_shader->bind();
// Load the transform and view matrices into the shader
- glUniformMatrix4fv(
- selected_mesh_shader->uniformLocation("transform_matrix"),
- 1, GL_FALSE, transform_matrix().data());
- glUniformMatrix4fv(
- selected_mesh_shader->uniformLocation("view_matrix"),
- 1, GL_FALSE, view_matrix().data());
+ glUniformMatrix4fv(selected_mesh_shader->uniformLocation("transform_matrix"), 1, GL_FALSE, transform_matrix().data());
+ glUniformMatrix4fv(selected_mesh_shader->uniformLocation("view_matrix"), 1, GL_FALSE, view_matrix().data());
// Compensate for z-flattening when zooming
- glUniform1f(selected_mesh_shader->uniformLocation("zoom"), 1/zoom);
+ 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);
+ // 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);
+ // 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 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());
+ // 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
QMatrix4x4 Canvas::aspect_matrix() const
{
QMatrix4x4 m;
- if (width() > height())
- {
+ if (width() > height()) {
m.scale(-height() / float(width()), 1, 0.5);
- }
- else
- {
+ } else {
m.scale(-1, width() / float(height()), 0.5);
}
return m;
void Canvas::mousePressEvent(QMouseEvent* event)
{
- if (event->button() == Qt::LeftButton ||
- event->button() == Qt::RightButton)
- {
+ if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) {
mouse_pos = event->pos();
setCursor(Qt::ClosedHandCursor);
}
void Canvas::mouseReleaseEvent(QMouseEvent* event)
{
- if (event->button() == Qt::LeftButton ||
- event->button() == Qt::RightButton)
- {
+ if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) {
unsetCursor();
}
}
-
// 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 Canvas::changeMouseCoordinates(QPoint p)
+{
QPointF pr;
// Change coordinates
double ws2 = this->width() / 2.0;
return pr;
}
-void Canvas::calcArcballTransform(QPointF p1, QPointF p2) {
+void Canvas::calcArcballTransform(QPointF p1, QPointF p2)
+{
// Calc z1 & z2
double x1 = p1.x();
double x2 = p2.x();
QVector3D v1xv2Obj = currentTransform.inverted().mapVector(v1xv2);
// calc angle
- double angle = acos(std::min(1.0f,QVector3D::dotProduct(v1, v2))) * 180.0 / M_PI;
-
+ double angle = acos(std::min(1.0f, QVector3D::dotProduct(v1, v2))) * 180.0 / M_PI;
+
// apply transform
- currentTransform.rotate(angle,v1xv2Obj);
+ currentTransform.rotate(angle, v1xv2Obj);
}
void Canvas::mouseMoveEvent(QMouseEvent* event)
{
auto p = event->pos();
auto d = p - mouse_pos;
-
- if (event->buttons() & Qt::LeftButton)
- {
+ if (event->buttons() & Qt::LeftButton) {
QPointF p1r = changeMouseCoordinates(mouse_pos);
QPointF p2r = changeMouseCoordinates(p);
- calcArcballTransform(p1r,p2r);
+ calcArcballTransform(p1r, p2r);
update();
- }
- else if (event->buttons() & Qt::RightButton)
- {
- center = transform_matrix().inverted() *
- view_matrix().inverted() *
- QVector3D(-d.x() / (0.5*width()),
- d.y() / (0.5*height()), 0);
+ } else if (event->buttons() & Qt::RightButton) {
+ center = transform_matrix().inverted() * view_matrix().inverted() *
+ QVector3D(-d.x() / (0.5 * width()), d.y() / (0.5 * height()), 0);
update();
}
mouse_pos = p;
}
-void Canvas::wheelEvent(QWheelEvent *event)
+void Canvas::wheelEvent(QWheelEvent* event)
{
// Find GL position before the zoom operation
// (to zoom about mouse cursor)
- 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->angleDelta().y() < 0)
- {
- for (int i=0; i > event->angleDelta().y(); --i)
+ auto p = position(event);
+ 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->angleDelta().y() < 0) {
+ for (int i = 0; i > event->angleDelta().y(); --i)
if (invertZoom)
zoom /= 1.001;
- else
+ else
zoom *= 1.001;
- }
- else if (event->angleDelta().y() > 0)
- {
- for (int i=0; i < event->angleDelta().y(); ++i)
- if (invertZoom)
+ } else if (event->angleDelta().y() > 0) {
+ for (int i = 0; i < event->angleDelta().y(); ++i)
+ if (invertZoom)
zoom *= 1.001;
- else
+ else
zoom /= 1.001;
}
// Then find the cursor's GL position post-zoom and adjust center.
- QVector3D b = transform_matrix().inverted() *
- view_matrix().inverted() * v;
+ QVector3D b = transform_matrix().inverted() * view_matrix().inverted() * v;
center += b - a;
update();
}
glViewport(0, 0, width, height);
}
-QColor Canvas::getAmbientColor() {
+QColor Canvas::getAmbientColor()
+{
return ambientColor;
}
-void Canvas::setAmbientColor(QColor c) {
+void Canvas::setAmbientColor(QColor c)
+{
ambientColor = c;
QSettings settings;
- settings.setValue(AMBIENT_COLOR,c);
+ settings.setValue(AMBIENT_COLOR, c);
}
-double Canvas::getAmbientFactor() {
- return (float) ambientFactor;
+double Canvas::getAmbientFactor()
+{
+ return (float)ambientFactor;
}
-void Canvas::setAmbientFactor(double f) {
- ambientFactor = (float) f;
+void Canvas::setAmbientFactor(double f)
+{
+ ambientFactor = (float)f;
QSettings settings;
- settings.setValue(AMBIENT_FACTOR,f);
+ settings.setValue(AMBIENT_FACTOR, f);
}
-void Canvas::resetAmbientColor() {
+void Canvas::resetAmbientColor()
+{
setAmbientColor(defaultAmbientColor);
setAmbientFactor(defaultAmbientFactor);
}
-QColor Canvas::getDirectiveColor() {
+QColor Canvas::getDirectiveColor()
+{
return directiveColor;
}
-void Canvas::setDirectiveColor(QColor c) {
+void Canvas::setDirectiveColor(QColor c)
+{
directiveColor = c;
QSettings settings;
- settings.setValue(DIRECTIVE_COLOR,c);
+ settings.setValue(DIRECTIVE_COLOR, c);
}
-double Canvas::getDirectiveFactor() {
- return (float) directiveFactor;
+double Canvas::getDirectiveFactor()
+{
+ return (float)directiveFactor;
}
-void Canvas::setDirectiveFactor(double f) {
- directiveFactor = (float) f;
+void Canvas::setDirectiveFactor(double f)
+{
+ directiveFactor = (float)f;
QSettings settings;
- settings.setValue(DIRECTIVE_FACTOR,f);
+ settings.setValue(DIRECTIVE_FACTOR, f);
}
-void Canvas::resetDirectiveColor() {
+void Canvas::resetDirectiveColor()
+{
setDirectiveColor(defaultDirectiveColor);
setDirectiveFactor(defaultDirectiveFactor);
}
-QList<QString> Canvas::getNameDir() {
+QList<QString> Canvas::getNameDir()
+{
return nameDir;
}
-int Canvas::getCurrentLightDirection() {
+int Canvas::getCurrentLightDirection()
+{
return currentLightDirection;
}
-void Canvas::setCurrentLightDirection(int ind) {
+void Canvas::setCurrentLightDirection(int ind)
+{
currentLightDirection = ind;
QSettings settings;
- settings.setValue(CURRENT_LIGHT_DIRECTION,currentLightDirection);
+ settings.setValue(CURRENT_LIGHT_DIRECTION, currentLightDirection);
}
-void Canvas::resetCurrentLightDirection() {
+void Canvas::resetCurrentLightDirection()
+{
setCurrentLightDirection(defaultCurrentLightDirection);
}
#ifndef CANVAS_H
#define CANVAS_H
-#include <QtOpenGL>
-#include <QSurfaceFormat>
#include <QOpenGLShaderProgram>
+#include <QSurfaceFormat>
+#include <QtOpenGL>
class GLMesh;
class Mesh;
class Backdrop;
class Axis;
-enum ViewPoint {centerview, isoview, topview, bottomview, leftview, rightview, frontview, backview};
-enum DrawMode {shaded, wireframe, surfaceangle, meshlight, DRAWMODECOUNT};
+enum ViewPoint { centerview, isoview, topview, bottomview, leftview, rightview, frontview, backview };
+enum DrawMode { shaded, wireframe, surfaceangle, meshlight, 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();
const static float P_PERSPECTIVE;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
-
+
void set_perspective(float p);
void view_anim(float v);
const static QString DIRECTIVE_FACTOR;
const static QString CURRENT_LIGHT_DIRECTION;
-
GLMesh* mesh;
Backdrop* backdrop;
Axis* axis;
#include "glmesh.h"
#include "mesh.h"
-GLMesh::GLMesh(const Mesh* const mesh)
- : vertices(QOpenGLBuffer::VertexBuffer), indices(QOpenGLBuffer::IndexBuffer)
+GLMesh::GLMesh(const Mesh* const mesh) : vertices(QOpenGLBuffer::VertexBuffer), indices(QOpenGLBuffer::IndexBuffer)
{
initializeOpenGLFunctions();
indices.setUsagePattern(QOpenGLBuffer::StaticDraw);
vertices.bind();
- vertices.allocate(mesh->vertices.data(),
- mesh->vertices.size() * sizeof(float));
+ vertices.allocate(mesh->vertices.data(), mesh->vertices.size() * sizeof(float));
vertices.release();
indices.bind();
- indices.allocate(mesh->indices.data(),
- mesh->indices.size() * sizeof(uint32_t));
+ indices.allocate(mesh->indices.data(), mesh->indices.size() * sizeof(uint32_t));
indices.release();
}
vertices.bind();
indices.bind();
- glVertexAttribPointer(vp, 3, GL_FLOAT, false, 3*sizeof(float), NULL);
- glDrawElements(GL_TRIANGLES, indices.size() / sizeof(uint32_t),
- GL_UNSIGNED_INT, NULL);
+ glVertexAttribPointer(vp, 3, GL_FLOAT, false, 3 * sizeof(float), NULL);
+ glDrawElements(GL_TRIANGLES, indices.size() / sizeof(uint32_t), GL_UNSIGNED_INT, NULL);
vertices.release();
indices.release();
public:
GLMesh(const Mesh* const mesh);
void draw(GLuint vp);
+
private:
- QOpenGLBuffer vertices;
- QOpenGLBuffer indices;
+ QOpenGLBuffer vertices;
+ QOpenGLBuffer indices;
};
#endif // GLMESH_H
#include "loader.h"
#include "vertex.h"
-Loader::Loader(QObject* parent, const QString& filename, bool is_reload)
- : QThread(parent), filename(filename), is_reload(is_reload)
+Loader::Loader(QObject* parent, const QString& filename, bool is_reload) : QThread(parent), filename(filename), is_reload(is_reload)
{
// Nothing to do here
}
void Loader::run()
{
Mesh* mesh = load_stl();
- if (mesh)
- {
- if (mesh->empty())
- {
+ if (mesh) {
+ if (mesh->empty()) {
emit error_empty_mesh();
delete mesh;
- }
- else
- {
+ } else {
emit got_mesh(mesh, is_reload);
emit loaded_file(filename);
}
void parallel_sort(Vertex* begin, Vertex* end, int threads)
{
- if (threads < 2 || end - begin < 2)
- {
+ if (threads < 2 || end - begin < 2) {
std::sort(begin, end);
- }
- else
- {
+ } else {
const auto mid = begin + (end - begin) / 2;
- if (threads == 2)
- {
+ if (threads == 2) {
auto future = std::async(parallel_sort, begin, mid, threads / 2);
std::sort(mid, end);
future.wait();
- }
- else
- {
+ } else {
auto a = std::async(std::launch::async, parallel_sort, begin, mid, threads / 2);
auto b = std::async(std::launch::async, parallel_sort, mid, end, threads / 2);
a.wait();
{
// Save indicies as the second element in the array
// (so that we can reconstruct triangle order after sorting)
- for (size_t i=0; i < tri_count*3; ++i)
- {
+ for (size_t i = 0; i < tri_count * 3; ++i) {
verts[i].i = i;
}
// Check how many threads the hardware can safely support. This may return
// 0 if the property can't be read so we shoud check for that too.
auto threads = std::thread::hardware_concurrency();
- if (threads == 0)
- {
+ if (threads == 0) {
threads = 8;
}
parallel_sort(verts.begin(), verts.end(), threads);
// This vector will store triangles as sets of 3 indices
- std::vector<GLuint> indices(tri_count*3);
+ std::vector<GLuint> indices(tri_count * 3);
// Go through the sorted vertex list, deduplicating and creating
// an indexed geometry representation for the triangles.
// Unique vertices are moved so that they occupy the first vertex_count
// positions in the verts array.
size_t vertex_count = 0;
- for (auto v : verts)
- {
- if (!vertex_count || v != verts[vertex_count-1])
- {
+ for (auto v : verts) {
+ if (!vertex_count || v != verts[vertex_count - 1]) {
verts[vertex_count++] = v;
}
indices[v.i] = vertex_count - 1;
verts.resize(vertex_count);
std::vector<GLfloat> flat_verts;
- flat_verts.reserve(vertex_count*3);
- for (auto v : verts)
- {
+ flat_verts.reserve(vertex_count * 3);
+ for (auto v : verts) {
flat_verts.push_back(v.x);
flat_verts.push_back(v.y);
flat_verts.push_back(v.z);
Mesh* Loader::load_stl()
{
QFile file(filename);
- if (!file.open(QIODevice::ReadOnly))
- {
+ if (!file.open(QIODevice::ReadOnly)) {
emit error_missing_file();
return NULL;
}
file_size_old = file_size;
QThread::usleep(100000);
file_size = file.size();
- }
- while(file_size != file_size_old);
+ } while (file_size != file_size_old);
// First, try to read the stl as an ASCII file
- if (file.read(5) == "solid")
- {
+ if (file.read(5) == "solid") {
file.readLine(); // skip solid name
const auto line = file.readLine().trimmed();
- if (line.startsWith("facet") ||
- line.startsWith("endsolid"))
- {
+ if (line.startsWith("facet") || line.startsWith("endsolid")) {
file.seek(0);
return read_stl_ascii(file);
}
data >> tri_count;
// Verify that the file is the right size
- if (file.size() != 84 + tri_count*50)
- {
+ if (file.size() != 84 + tri_count * 50) {
emit error_bad_stl();
return NULL;
}
// Extract vertices into an array of xyz, unsigned pairs
- QVector<Vertex> verts(tri_count*3);
+ 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]);
// Store vertices in the array, processing one triangle at a time.
auto b = buffer.get() + 3 * sizeof(float);
- for (auto v=verts.begin(); v != verts.end(); v += 3)
- {
+ for (auto v = verts.begin(); v != verts.end(); v += 3) {
// Load vertex data from .stl file into vertices
- for (unsigned i=0; i < 3; ++i)
- {
+ for (unsigned i = 0; i < 3; ++i) {
qFromLittleEndian<float>(b, 3, &v[i]);
b += 3 * sizeof(float);
}
{
file.readLine();
uint32_t tri_count = 0;
- QVector<Vertex> verts(tri_count*3);
+ QVector<Vertex> verts(tri_count * 3);
bool okay = true;
- while (!file.atEnd() && okay)
- {
+ while (!file.atEnd() && okay) {
const auto line = file.readLine().simplified();
- if (line.startsWith("endsolid"))
- {
+ if (line.startsWith("endsolid")) {
break;
- }
- else if (!line.startsWith("facet normal") ||
- !file.readLine().simplified().startsWith("outer loop"))
- {
+ } else if (!line.startsWith("facet normal") || !file.readLine().simplified().startsWith("outer loop")) {
okay = false;
break;
}
- for (int i=0; i < 3; ++i)
- {
+ for (int i = 0; i < 3; ++i) {
auto line = file.readLine().simplified().split(' ');
- if (line[0] != "vertex")
- {
+ if (line[0] != "vertex") {
okay = false;
break;
}
const float z = line[3].toFloat(&okay);
verts.push_back(Vertex(x, y, z));
}
- if (!file.readLine().trimmed().startsWith("endloop") ||
- !file.readLine().trimmed().startsWith("endfacet"))
- {
+ if (!file.readLine().trimmed().startsWith("endloop") || !file.readLine().trimmed().startsWith("endfacet")) {
okay = false;
break;
}
tri_count++;
}
- if (okay)
- {
+ if (okay) {
return mesh_from_verts(tri_count, verts);
- }
- else
- {
+ } else {
emit error_bad_stl();
return NULL;
}
}
-
#include "app.h"
-int main(int argc, char *argv[])
+int main(int argc, char* argv[])
{
// Force C locale to force decimal point
QLocale::setDefault(QLocale::c());
-#include <QFile>
#include <QDataStream>
+#include <QFile>
#include <QVector3D>
#include <cmath>
////////////////////////////////////////////////////////////////////////////////
-Mesh::Mesh(std::vector<GLfloat>&& v, std::vector<GLuint>&& i)
- : vertices(std::move(v)), indices(std::move(i))
+Mesh::Mesh(std::vector<GLfloat>&& v, std::vector<GLuint>&& i) : vertices(std::move(v)), indices(std::move(i))
{
// Nothing to do here
}
float Mesh::min(size_t start) const
{
- if (start >= vertices.size())
- {
+ if (start >= vertices.size()) {
return -1;
}
float v = vertices[start];
- for (size_t i=start; i < vertices.size(); i += 3)
- {
+ for (size_t i = start; i < vertices.size(); i += 3) {
v = fmin(v, vertices[i]);
}
return v;
float Mesh::max(size_t start) const
{
- if (start >= vertices.size())
- {
+ if (start >= vertices.size()) {
return 1;
}
float v = vertices[start];
- for (size_t i=start; i < vertices.size(); i += 3)
- {
+ for (size_t i = start; i < vertices.size(); i += 3) {
v = fmax(v, vertices[i]);
}
return v;
int Mesh::triCount() const
{
- return indices.size()/3;
+ return indices.size() / 3;
}
+
bool Mesh::empty() const
{
return vertices.size() == 0;
float min(size_t start) const;
float max(size_t start) const;
- float xmin() const { return min(0); }
- float ymin() const { return min(1); }
- float zmin() const { return min(2); }
- float xmax() const { return max(0); }
- float ymax() const { return max(1); }
- float zmax() const { return max(2); }
+ float xmin() const
+ {
+ return min(0);
+ }
+ float ymin() const
+ {
+ return min(1);
+ }
+ float zmin() const
+ {
+ return min(2);
+ }
+ float xmax() const
+ {
+ return max(0);
+ }
+ float ymax() const
+ {
+ return max(1);
+ }
+ float zmax() const
+ {
+ return max(2);
+ }
int triCount() const;
bool empty() const;
const QString ShaderLightPrefs::PREFS_GEOM = "shaderPrefsGeometry";
-ShaderLightPrefs::ShaderLightPrefs(QWidget *parent, Canvas *_canvas) : QDialog(parent)
+ShaderLightPrefs::ShaderLightPrefs(QWidget* parent, Canvas* _canvas) : QDialog(parent)
{
canvas = _canvas;
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);
+ 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);
+ middleLayout->addWidget(buttonAmbientColor, 0, 1);
buttonAmbientColor->setFocusPolicy(Qt::NoFocus);
- connect(buttonAmbientColor,SIGNAL(clicked(bool)),this,SLOT(buttonAmbientColorClicked()));
+ 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()));
+ middleLayout->addWidget(editAmbientFactor, 0, 2);
+ connect(editAmbientFactor, SIGNAL(editingFinished()), this, SLOT(editAmbientFactorFinished()));
QPushButton* buttonResetAmbientColor = new QPushButton("Reset");
- middleLayout->addWidget(buttonResetAmbientColor,0,3);
+ middleLayout->addWidget(buttonResetAmbientColor, 0, 3);
buttonResetAmbientColor->setFocusPolicy(Qt::NoFocus);
- connect(buttonResetAmbientColor,SIGNAL(clicked(bool)),this,SLOT(resetAmbientColorClicked()));
-
+ connect(buttonResetAmbientColor, SIGNAL(clicked(bool)), this, SLOT(resetAmbientColorClicked()));
dummy.fill(canvas->getDirectiveColor());
buttonDirectiveColor = new QPushButton;
buttonDirectiveColor->setIcon(QIcon(dummy));
- middleLayout->addWidget(buttonDirectiveColor,1,1);
+ middleLayout->addWidget(buttonDirectiveColor, 1, 1);
buttonDirectiveColor->setFocusPolicy(Qt::NoFocus);
- connect(buttonDirectiveColor,SIGNAL(clicked(bool)),this,SLOT(buttonDirectiveColorClicked()));
+ 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()));
+ middleLayout->addWidget(editDirectiveFactor, 1, 2);
+ connect(editDirectiveFactor, SIGNAL(editingFinished()), this, SLOT(editDirectiveFactorFinished()));
QPushButton* buttonResetDirectiveColor = new QPushButton("Reset");
- middleLayout->addWidget(buttonResetDirectiveColor,1,3);
+ middleLayout->addWidget(buttonResetDirectiveColor, 1, 3);
buttonResetDirectiveColor->setFocusPolicy(Qt::NoFocus);
- connect(buttonResetDirectiveColor,SIGNAL(clicked(bool)),this,SLOT(resetDirectiveColorClicked()));
+ connect(buttonResetDirectiveColor, SIGNAL(clicked(bool)), this, SLOT(resetDirectiveColorClicked()));
// Fill in directions
comboDirections = new QComboBox;
- middleLayout->addWidget(comboDirections,2,1,1,2);
+ middleLayout->addWidget(comboDirections, 2, 1, 1, 2);
comboDirections->addItems(canvas->getNameDir());
comboDirections->setCurrentIndex(canvas->getCurrentLightDirection());
- connect(comboDirections,SIGNAL(currentIndexChanged(int)),this,SLOT(comboDirectionsChanged(int)));
+ connect(comboDirections, SIGNAL(currentIndexChanged(int)), this, SLOT(comboDirectionsChanged(int)));
QPushButton* buttonResetDirection = new QPushButton("Reset");
- middleLayout->addWidget(buttonResetDirection,2,3);
+ middleLayout->addWidget(buttonResetDirection, 2, 3);
buttonResetDirection->setFocusPolicy(Qt::NoFocus);
- connect(buttonResetDirection,SIGNAL(clicked(bool)),this,SLOT(resetDirection()));
-
+ 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;
+ 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()));
+ connect(okButton, SIGNAL(clicked(bool)), this, SLOT(okButtonClicked()));
QSettings settings;
if (!settings.value(PREFS_GEOM).isNull()) {
}
}
-void ShaderLightPrefs::buttonAmbientColorClicked() {
- QColor newColor = QColorDialog::getColor(canvas->getAmbientColor(), this, QString("Choose color"),QColorDialog::DontUseNativeDialog);
- if (newColor.isValid() == true)
- {
+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());
}
}
-void ShaderLightPrefs::editAmbientFactorFinished() {
+void ShaderLightPrefs::editAmbientFactorFinished()
+{
canvas->setAmbientFactor(editAmbientFactor->text().toDouble());
canvas->update();
}
-void ShaderLightPrefs::resetAmbientColorClicked() {
+void ShaderLightPrefs::resetAmbientColorClicked()
+{
canvas->resetAmbientColor();
QPixmap dummy(20, 20);
dummy.fill(canvas->getAmbientColor());
canvas->update();
}
-void ShaderLightPrefs::buttonDirectiveColorClicked() {
- QColor newColor = QColorDialog::getColor(canvas->getDirectiveColor(), this, QString("Choose color"),QColorDialog::DontUseNativeDialog);
- if (newColor.isValid() == true)
- {
+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());
}
}
-void ShaderLightPrefs::editDirectiveFactorFinished() {
+void ShaderLightPrefs::editDirectiveFactorFinished()
+{
canvas->setDirectiveFactor(editDirectiveFactor->text().toDouble());
canvas->update();
}
-void ShaderLightPrefs::resetDirectiveColorClicked() {
+void ShaderLightPrefs::resetDirectiveColorClicked()
+{
canvas->resetDirectiveColor();
QPixmap dummy(20, 20);
dummy.fill(canvas->getDirectiveColor());
canvas->update();
}
-void ShaderLightPrefs::okButtonClicked() {
+void ShaderLightPrefs::okButtonClicked()
+{
this->close();
}
-void ShaderLightPrefs::comboDirectionsChanged(int ind) {
+void ShaderLightPrefs::comboDirectionsChanged(int ind)
+{
canvas->setCurrentLightDirection(ind);
canvas->update();
}
-void ShaderLightPrefs::resetDirection() {
+void ShaderLightPrefs::resetDirection()
+{
canvas->resetCurrentLightDirection();
comboDirections->setCurrentIndex(canvas->getCurrentLightDirection());
canvas->update();
}
-void ShaderLightPrefs::resizeEvent(QResizeEvent *event)
+void ShaderLightPrefs::resizeEvent(QResizeEvent* event)
{
QSettings().setValue(PREFS_GEOM, saveGeometry());
}
-void ShaderLightPrefs::moveEvent(QMoveEvent *event)
+void ShaderLightPrefs::moveEvent(QMoveEvent* event)
{
QSettings().setValue(PREFS_GEOM, saveGeometry());
}
-
-
ShaderLightPrefs(QWidget* parent, Canvas* _canvas);
protected:
- void resizeEvent(QResizeEvent *event) override;
- void moveEvent(QMoveEvent *event) override;
+ void resizeEvent(QResizeEvent* event) override;
+ void moveEvent(QMoveEvent* event) override;
private slots:
void buttonAmbientColorClicked();
/*
* Represents an optionally-indexed vertex in space
*/
-struct Vertex
-{
+struct Vertex {
Vertex() {}
Vertex(float x, float y, float z) : x(x), y(y), z(z) {}
GLfloat x, y, z;
- GLuint i=0;
+ GLuint i = 0;
bool operator!=(const Vertex& rhs) const
{
}
bool operator<(const Vertex& rhs) const
{
- if (x != rhs.x) return x < rhs.x;
- else if (y != rhs.y) return y < rhs.y;
- else if (z != rhs.z) return z < rhs.z;
- else return false;
+ if (x != rhs.x)
+ return x < rhs.x;
+ else if (y != rhs.y)
+ return y < rhs.y;
+ else if (z != rhs.z)
+ return z < rhs.z;
+ else
+ return false;
}
};
#include <QMenuBar>
-#include "window.h"
#include "canvas.h"
#include "loader.h"
#include "shaderlightprefs.h"
+#include "window.h"
const QString Window::OPEN_EXTERNAL_KEY = "externalCmd";
const QString Window::RECENT_FILE_KEY = "recentFiles";
const QString Window::WINDOW_GEOM_KEY = "windowGeometry";
const QString Window::RESET_TRANSFORM_ON_LOAD_KEY = "resetTransformOnLoad";
-Window::Window(QWidget *parent) :
+Window::Window(QWidget* parent) :
QMainWindow(parent),
open_action(new QAction("Open", this)),
open_external_action(new QAction("Open with", 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)),
+ 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)),
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
-
+
canvas = new Canvas(format, this);
setCentralWidget(canvas);
meshlightprefs = new ShaderLightPrefs(this, canvas);
- QObject::connect(drawModePrefs_action, &QAction::triggered,this,&Window::on_drawModePrefs);
+ QObject::connect(drawModePrefs_action, &QAction::triggered, this, &Window::on_drawModePrefs);
- QObject::connect(watcher, &QFileSystemWatcher::fileChanged,
- this, &Window::on_watched_change);
+ QObject::connect(watcher, &QFileSystemWatcher::fileChanged, this, &Window::on_watched_change);
open_action->setShortcut(QKeySequence::Open);
- QObject::connect(open_action, &QAction::triggered,
- this, &Window::on_open);
+ QObject::connect(open_action, &QAction::triggered, this, &Window::on_open);
this->addAction(open_action);
open_external_action->setShortcut(QKeySequence::Open);
this->addAction(open_external_action);
open_external_action->setShortcut(QKeySequence(Qt::ALT + Qt::Key_S));
- QList<QKeySequence> quitShortcuts = { QKeySequence::Quit, QKeySequence::Close };
+ QList<QKeySequence> quitShortcuts = {QKeySequence::Quit, QKeySequence::Close};
quit_action->setShortcuts(quitShortcuts);
- QObject::connect(quit_action, &QAction::triggered,
- this, &Window::close);
+ QObject::connect(quit_action, &QAction::triggered, this, &Window::close);
this->addAction(quit_action);
autoreload_action->setCheckable(true);
- QObject::connect(autoreload_action, &QAction::triggered,
- this, &Window::on_autoreload_triggered);
+ QObject::connect(autoreload_action, &QAction::triggered, this, &Window::on_autoreload_triggered);
reload_action->setShortcut(QKeySequence::Refresh);
reload_action->setEnabled(false);
- QObject::connect(reload_action, &QAction::triggered,
- this, &Window::on_reload);
+ QObject::connect(reload_action, &QAction::triggered, this, &Window::on_reload);
- QObject::connect(about_action, &QAction::triggered,
- this, &Window::on_about);
+ QObject::connect(about_action, &QAction::triggered, this, &Window::on_about);
- QObject::connect(recent_files_clear_action, &QAction::triggered,
- this, &Window::on_clear_recent);
- QObject::connect(recent_files_group, &QActionGroup::triggered,
- this, &Window::on_load_recent);
+ QObject::connect(recent_files_clear_action, &QAction::triggered, this, &Window::on_clear_recent);
+ QObject::connect(recent_files_group, &QActionGroup::triggered, this, &Window::on_load_recent);
save_screenshot_action->setCheckable(false);
- QObject::connect(save_screenshot_action, &QAction::triggered,
- this, &Window::on_save_screenshot);
-
+ QObject::connect(save_screenshot_action, &QAction::triggered, this, &Window::on_save_screenshot);
+
rebuild_recent_files();
const auto file_menu = menuBar()->addMenu("File");
projection_menu->addAction(perspective_action);
projection_menu->addAction(orthographic_action);
const auto projections = new QActionGroup(projection_menu);
- for (auto p : {perspective_action, orthographic_action})
- {
+ for (auto p : {perspective_action, orthographic_action}) {
projections->addAction(p);
p->setCheckable(true);
}
projections->setExclusive(true);
- QObject::connect(projections, &QActionGroup::triggered,
- this, &Window::on_projection);
+ QObject::connect(projections, &QActionGroup::triggered, this, &Window::on_projection);
const auto draw_menu = view_menu->addMenu("Draw Mode");
draw_menu->addAction(shaded_action);
draw_menu->addAction(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})
- {
+ for (auto p : {shaded_action, wireframe_action, surfaceangle_action, meshlight_action}) {
drawModes->addAction(p);
p->setCheckable(true);
}
drawModes->setExclusive(true);
- QObject::connect(drawModes, &QActionGroup::triggered,
- this, &Window::on_drawMode);
+ QObject::connect(drawModes, &QActionGroup::triggered, this, &Window::on_drawMode);
view_menu->addAction(drawModePrefs_action);
drawModePrefs_action->setDisabled(true);
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);
+ 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,
- this, &Window::on_drawAxes);
+ 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);
+ 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);
+ 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);
- QObject::connect(hide_menuBar_action, &QAction::toggled,
- this, &Window::on_hide_menuBar);
+ QObject::connect(hide_menuBar_action, &QAction::toggled, 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);
+ 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);
load_persist_settings();
}
-void Window::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);
axes_action->setChecked(draw_axes);
QString projection = settings.value(PROJECTION_KEY, "perspective").toString();
- if(projection == "perspective"){
+ if (projection == "perspective") {
canvas->view_perspective(Canvas::P_PERSPECTIVE, false);
perspective_action->setChecked(true);
- }else{
+ } else {
canvas->view_perspective(Canvas::P_ORTHOGRAPHIC, false);
orthographic_action->setChecked(true);
}
QString path = settings.value(OPEN_EXTERNAL_KEY, "").toString();
- if (!QDir::isAbsolutePath(path) && !path.isEmpty())
- {
+ if (!QDir::isAbsolutePath(path) && !path.isEmpty()) {
path = QStandardPaths::findExecutable(path);
}
QString displayName = path.mid(path.lastIndexOf(QDir::separator()) + 1);
open_external_action->setData(path);
DrawMode draw_mode = (DrawMode)settings.value(DRAW_MODE_KEY, DRAWMODECOUNT).toInt();
-
- if(draw_mode >= DRAWMODECOUNT)
- {
+
+ if (draw_mode >= DRAWMODECOUNT) {
draw_mode = shaded;
}
- QAction* (dm_acts[]) = {shaded_action, wireframe_action, surfaceangle_action, meshlight_action};
+ QAction*(dm_acts[]) = {shaded_action, wireframe_action, surfaceangle_action, meshlight_action};
dm_acts[draw_mode]->setChecked(true);
on_drawMode(dm_acts[draw_mode]);
restoreGeometry(settings.value(WINDOW_GEOM_KEY).toByteArray());
}
-void Window::on_drawModePrefs() {
+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
void Window::on_open()
{
- const QString filename = QFileDialog::getOpenFileName(
- this, "Load .stl file", QString(), "STL files (*.stl *.STL)");
- if (!filename.isNull())
- {
+ 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())
- {
+ 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());
+ program = QFileDialog::getOpenFileName((QWidget*)this, "Select program to open with", QDir::rootPath());
if (!program.isEmpty()) {
QSettings settings;
settings.setValue(OPEN_EXTERNAL_KEY, program);
void Window::on_about()
{
QMessageBox::about(this, "",
- "<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/fstl-app/fstl\""
- " style=\"color: #93a1a1;\">https://github.com/fstl-app/fstl</a></p>"
- "<p>© 2014-2024 Matthew Keeter<br>"
- "<a href=\"mailto:matt.j.keeter@gmail.com\""
- " style=\"color: #93a1a1;\">matt.j.keeter@gmail.com</a></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/fstl-app/fstl\""
+ " style=\"color: #93a1a1;\">https://github.com/fstl-app/fstl</a></p>"
+ "<p>© 2014-2025 Matthew Keeter<br>"
+ "<a href=\"mailto:matt.j.keeter@gmail.com\""
+ " style=\"color: #93a1a1;\">matt.j.keeter@gmail.com</a></p>");
}
void Window::on_bad_stl()
void Window::set_watched(const QString& filename)
{
const auto files = watcher->files();
- if (files.size())
- {
+ if (files.size()) {
watcher->removePaths(watcher->files());
}
watcher->addPath(filename);
const auto f = QFileInfo(filename).absoluteFilePath();
recent.removeAll(f);
recent.prepend(f);
- while (recent.size() > MAX_RECENT_FILES)
- {
+ while (recent.size() > MAX_RECENT_FILES) {
recent.pop_back();
}
settings.setValue(RECENT_FILE_KEY, recent);
void Window::on_projection(QAction* proj)
{
- if (proj == perspective_action)
- {
+ if (proj == perspective_action) {
canvas->view_perspective(Canvas::P_PERSPECTIVE, true);
QSettings().setValue(PROJECTION_KEY, "perspective");
- }
- else
- {
+ } else {
canvas->view_perspective(Canvas::P_ORTHOGRAPHIC, true);
QSettings().setValue(PROJECTION_KEY, "orthographic");
}
meshlightprefs->hide();
DrawMode mode;
- if (act == shaded_action)
- {
+ if (act == shaded_action) {
drawModePrefs_action->setEnabled(false);
mode = shaded;
- }
- else if (act == wireframe_action)
- {
+ } else if (act == wireframe_action) {
drawModePrefs_action->setEnabled(false);
mode = wireframe;
- }
- else if (act == surfaceangle_action)
- {
+ } else if (act == surfaceangle_action) {
drawModePrefs_action->setEnabled(false);
mode = surfaceangle;
- }
- else if (act == meshlight_action)
- {
+ } else if (act == meshlight_action) {
drawModePrefs_action->setEnabled(true);
mode = meshlight;
}
QSettings().setValue(INVERT_ZOOM_KEY, d);
}
-void Window::on_resetTransformOnLoad(bool 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())
- {
+ if (autoreload_action->isChecked()) {
load_stl(filename, true);
}
}
void Window::on_autoreload_triggered(bool b)
{
- if (b)
- {
+ if (b) {
on_reload();
}
QSettings().setValue(AUTORELOAD_KEY, b);
{
const auto image = canvas->grabFramebuffer();
auto file_name = QFileDialog::getSaveFileName(
- this,
- tr("Save Screenshot Image"),
- QStandardPaths::standardLocations(QStandardPaths::StandardLocation::PicturesLocation).first(),
- "Images (*.png *.jpg)");
+ this, tr("Save Screenshot Image"),
+ QStandardPaths::standardLocations(QStandardPaths::StandardLocation::PicturesLocation).first(), "Images (*.png *.jpg)");
- auto get_file_extension = [](const std::string& file_name) -> std::string
- {
+ auto get_file_extension = [](const std::string& file_name) -> std::string {
const auto location = std::find(file_name.rbegin(), file_name.rend(), '.');
- if (location == file_name.rend())
- {
+ if (location == file_name.rend()) {
return "";
}
};
const auto extension = get_file_extension(file_name.toStdString());
- if(extension.empty() || (extension != "png" && extension != "jpg"))
- {
+ if (extension.empty() || (extension != "png" && extension != "jpg")) {
file_name.append(".png");
}
-
+
const auto save_ok = image.save(file_name);
- if(!save_ok)
- {
+ if (!save_ok) {
QMessageBox::warning(this, tr("Error Saving Image"), tr("Unable to save screen shot image."));
}
}
QStringList files = settings.value(RECENT_FILE_KEY).toStringList();
const auto actions = recent_files_group->actions();
- for (auto a : actions)
- {
+ for (auto a : actions) {
recent_files_group->removeAction(a);
}
recent_files->clear();
- for (auto f : files)
- {
+ for (auto f : files) {
const auto a = new QAction(f, recent_files);
a->setData(f);
recent_files_group->addAction(a);
recent_files->addAction(a);
}
- if (files.size() == 0)
- {
+ if (files.size() == 0) {
auto a = new QAction("No recent files", recent_files);
recent_files->addAction(a);
a->setEnabled(false);
void Window::on_reload()
{
auto fs = watcher->files();
- if (fs.size() == 1)
- {
+ if (fs.size() == 1) {
load_stl(fs[0], true);
}
}
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);
+ 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;
+ if (!open_action->isEnabled())
+ return false;
canvas->set_status("Loading " + filename);
Loader* loader = new Loader(this, filename, is_reload);
- connect(loader, &Loader::started,
- this, &Window::disable_open);
-
- connect(loader, &Loader::got_mesh,
- canvas, &Canvas::load_mesh);
- connect(loader, &Loader::error_bad_stl,
- this, &Window::on_bad_stl);
- connect(loader, &Loader::error_empty_mesh,
- this, &Window::on_empty_mesh);
- connect(loader, &Loader::error_missing_file,
- this, &Window::on_missing_file);
-
- connect(loader, &Loader::finished,
- loader, &Loader::deleteLater);
- connect(loader, &Loader::finished,
- this, &Window::enable_open);
- connect(loader, &Loader::finished,
- canvas, &Canvas::clear_status);
-
- if (filename[0] != ':')
- {
- connect(loader, &Loader::loaded_file,
- this, &Window::setWindowTitle);
- connect(loader, &Loader::loaded_file,
- this, &Window::set_watched);
- connect(loader, &Loader::loaded_file,
- this, &Window::on_loaded);
+ connect(loader, &Loader::started, this, &Window::disable_open);
+
+ connect(loader, &Loader::got_mesh, canvas, &Canvas::load_mesh);
+ connect(loader, &Loader::error_bad_stl, this, &Window::on_bad_stl);
+ connect(loader, &Loader::error_empty_mesh, this, &Window::on_empty_mesh);
+ connect(loader, &Loader::error_missing_file, this, &Window::on_missing_file);
+
+ connect(loader, &Loader::finished, loader, &Loader::deleteLater);
+ connect(loader, &Loader::finished, this, &Window::enable_open);
+ connect(loader, &Loader::finished, canvas, &Canvas::clear_status);
+
+ if (filename[0] != ':') {
+ connect(loader, &Loader::loaded_file, this, &Window::setWindowTitle);
+ connect(loader, &Loader::loaded_file, this, &Window::set_watched);
+ connect(loader, &Loader::loaded_file, this, &Window::on_loaded);
reload_action->setEnabled(true);
}
return true;
}
-void Window::dragEnterEvent(QDragEnterEvent *event)
+void Window::dragEnterEvent(QDragEnterEvent* event)
{
- if (event->mimeData()->hasUrls())
- {
+ if (event->mimeData()->hasUrls()) {
auto urls = event->mimeData()->urls();
if (urls.size() == 1 && urls.front().path().endsWith(".stl"))
event->acceptProposedAction();
}
}
-void Window::dropEvent(QDropEvent *event)
+void Window::dropEvent(QDropEvent* event)
{
load_stl(event->mimeData()->urls().front().toLocalFile());
}
-void Window::resizeEvent(QResizeEvent *event)
+void Window::resizeEvent(QResizeEvent* event)
{
QSettings().setValue(WINDOW_GEOM_KEY, saveGeometry());
QWidget::resizeEvent(event);
}
-void Window::moveEvent(QMoveEvent *event)
+void Window::moveEvent(QMoveEvent* event)
{
QSettings().setValue(WINDOW_GEOM_KEY, saveGeometry());
QWidget::moveEvent(event);
int start = 0;
int end = list.size() - 1;
int index = 0;
- while (start <= end){
- int mid = (start+end)/2;
+ while (start <= end) {
+ int mid = (start + end) / 2;
if (list[mid] == value) {
return;
}
int compare = collator.compare(value, list[mid]);
if (compare < 0) {
- end = mid-1;
+ end = mid - 1;
index = mid;
} else {
- start = mid+1;
+ start = mid + 1;
index = start;
}
}
void Window::build_folder_file_list()
{
QString current_folder_path = QFileInfo(current_file).absoluteDir().absolutePath();
- if (!lookup_folder_files.isEmpty())
- {
+ if (!lookup_folder_files.isEmpty()) {
if (current_folder_path == lookup_folder) {
return;
}
void Window::keyPressEvent(QKeyEvent* event)
{
- if (!open_action->isEnabled())
- {
+ if (!open_action->isEnabled()) {
QMainWindow::keyPressEvent(event);
return;
}
- if (event->key() == Qt::Key_Left)
- {
+ if (event->key() == Qt::Key_Left) {
load_prev();
return;
- }
- else if (event->key() == Qt::Key_Right)
- {
+ } else if (event->key() == Qt::Key_Right) {
load_next();
return;
- }
- else if (event->key() == Qt::Key_Escape)
- {
+ } else if (event->key() == Qt::Key_Escape) {
hide_menuBar_action->setChecked(false);
return;
}
QMainWindow::keyPressEvent(event);
}
-void Window::on_fullscreen() {
+void Window::on_fullscreen()
+{
if (!this->isFullScreen()) {
this->showFullScreen();
} else {
#ifndef WINDOW_H
#define WINDOW_H
-#include <QMainWindow>
#include <QActionGroup>
-#include <QFileSystemWatcher>
#include <QCollator>
+#include <QFileSystemWatcher>
+#include <QMainWindow>
class Canvas;
class ShaderLightPrefs;
{
Q_OBJECT
public:
- explicit Window(QWidget* parent=0);
- bool load_stl(const QString& filename, bool is_reload=false);
+ explicit Window(QWidget* parent = 0);
+ bool load_stl(const QString& filename, bool is_reload = false);
bool load_prev(void);
bool load_next(void);
protected:
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
- void resizeEvent(QResizeEvent *event) override;
- void moveEvent(QMoveEvent *event) override;
+ void resizeEvent(QResizeEvent* event) override;
+ void moveEvent(QMoveEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
public slots:
QMenu* const recent_files;
QActionGroup* const recent_files_group;
QAction* const recent_files_clear_action;
- const static int MAX_RECENT_FILES=8;
+ 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;