From 51e25316e8dbb9383f8d296a649abdaf4bc249ce Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Sat, 22 Mar 2014 12:54:06 -0400
Subject: [PATCH 01/16] Adding hash-based mesh loading (better big-O)
---
src/loader.cpp | 8 ++++++++
src/mesh.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/mesh.h | 1 +
3 files changed, 62 insertions(+)
diff --git a/src/loader.cpp b/src/loader.cpp
index 4131d02..f0f4268 100644
--- a/src/loader.cpp
+++ b/src/loader.cpp
@@ -8,6 +8,14 @@ Loader::Loader(QObject* parent, const QString& filename)
void Loader::run()
{
+ QTime timer;
+ timer.start();
emit got_mesh(Mesh::load_stl(filename));
+ qDebug() << "Sorted:" << timer.elapsed();
+
+ timer.start();
+ emit got_mesh(Mesh::load_stl_hash(filename));
+ qDebug() << "Hash:" << timer.elapsed();
+
emit loaded_file(filename);
}
diff --git a/src/mesh.cpp b/src/mesh.cpp
index 36ad10e..6520741 100644
--- a/src/mesh.cpp
+++ b/src/mesh.cpp
@@ -56,6 +56,59 @@ typedef std::pair Vec3i;
////////////////////////////////////////////////////////////////////////////////
+Mesh* Mesh::load_stl_hash(const QString& filename)
+{
+ QFile file(filename);
+ file.open(QIODevice::ReadOnly);
+
+ QDataStream data(&file);
+ data.setByteOrder(QDataStream::LittleEndian);
+ data.setFloatingPointPrecision(QDataStream::SinglePrecision);
+
+ // Skip .stl file header
+ data.skipRawData(80);
+
+ // Load the triangle count from the .stl file
+ uint32_t tri_count;
+ data >> tri_count;
+
+ // This vector will store triangles as sets of 3 indices
+ std::vector indices(tri_count * 3);
+
+ std::vector verts;
+ verts.reserve(tri_count * 9);
+
+ QHash map;
+ map.reserve(tri_count * 3);
+
+ float xyz[3];
+ QByteArray v(sizeof(xyz), 0);
+ for (unsigned i=0; i < tri_count; ++i)
+ {
+ // Skip face's normal vector
+ data.skipRawData(3*sizeof(float));
+
+ for (int j=0; j < 3; ++j)
+ {
+ data >> xyz[0] >> xyz[1] >> xyz[2];
+ memcpy(v.data(), xyz, sizeof(xyz));
+ if (!map.contains(v))
+ {
+ map[v] = verts.size() / 3;
+ verts.push_back(xyz[0]);
+ verts.push_back(xyz[1]);
+ verts.push_back(xyz[2]);
+ }
+ indices[i*3 + j] = map[v];
+ }
+
+ // Skip face attribute
+ data.skipRawData(sizeof(uint16_t));
+ }
+
+ return new Mesh(verts, indices);
+}
+
Mesh* Mesh::load_stl(const QString& filename)
{
QFile file(filename);
diff --git a/src/mesh.h b/src/mesh.h
index f83a477..80f2a1e 100644
--- a/src/mesh.h
+++ b/src/mesh.h
@@ -11,6 +11,7 @@ class Mesh
public:
Mesh(std::vector vertices, std::vector indices);
static Mesh* load_stl(const QString& filename);
+ static Mesh* load_stl_hash(const QString& filename);
float min(size_t start) const;
float max(size_t start) const;
From 44f171a823fb8afbc9699c58ef5297ebb1dd9430 Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Sat, 22 Mar 2014 12:56:37 -0400
Subject: [PATCH 02/16] readRawData is much faster than skipRawData
---
src/mesh.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/mesh.cpp b/src/mesh.cpp
index 6520741..be36d72 100644
--- a/src/mesh.cpp
+++ b/src/mesh.cpp
@@ -86,7 +86,7 @@ Mesh* Mesh::load_stl_hash(const QString& filename)
for (unsigned i=0; i < tri_count; ++i)
{
// Skip face's normal vector
- data.skipRawData(3*sizeof(float));
+ data.readRawData(reinterpret_cast(xyz), 3*sizeof(float));
for (int j=0; j < 3; ++j)
{
@@ -103,7 +103,7 @@ Mesh* Mesh::load_stl_hash(const QString& filename)
}
// Skip face attribute
- data.skipRawData(sizeof(uint16_t));
+ data.readRawData(reinterpret_cast(xyz), sizeof(uint16_t));
}
return new Mesh(verts, indices);
@@ -127,12 +127,12 @@ Mesh* Mesh::load_stl(const QString& filename)
// Extract vertices into an array of xyz, unsigned pairs
QVector verts(tri_count*3);
-
+ float xyz[3];
// Store vertices in the array, processing one triangle at a time.
for (auto v=verts.begin(); v != verts.end(); v += 3)
{
// Skip face's normal vector
- data.skipRawData(3*sizeof(float));
+ data.readRawData(reinterpret_cast(xyz), 3*sizeof(float));
// Load vertex data from .stl file into vertices
data >> v[0].first.x >> v[0].first.y >> v[0].first.z;
@@ -140,7 +140,7 @@ Mesh* Mesh::load_stl(const QString& filename)
data >> v[2].first.x >> v[2].first.y >> v[2].first.z;
// Skip face attribute
- data.skipRawData(sizeof(uint16_t));
+ data.readRawData(reinterpret_cast(xyz), sizeof(uint16_t));
}
// Save indicies as the second element in the array
From b6368a26636027836b82f3950dc02de49260decc Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Sat, 22 Mar 2014 13:03:54 -0400
Subject: [PATCH 03/16] Hash is slower; back to sorted list
---
src/loader.cpp | 6 +----
src/mesh.cpp | 64 ++++++--------------------------------------------
src/mesh.h | 1 -
3 files changed, 8 insertions(+), 63 deletions(-)
diff --git a/src/loader.cpp b/src/loader.cpp
index f0f4268..107d90f 100644
--- a/src/loader.cpp
+++ b/src/loader.cpp
@@ -11,11 +11,7 @@ void Loader::run()
QTime timer;
timer.start();
emit got_mesh(Mesh::load_stl(filename));
- qDebug() << "Sorted:" << timer.elapsed();
-
- timer.start();
- emit got_mesh(Mesh::load_stl_hash(filename));
- qDebug() << "Hash:" << timer.elapsed();
+ qDebug() << "Time taken:" << timer.elapsed();
emit loaded_file(filename);
}
diff --git a/src/mesh.cpp b/src/mesh.cpp
index be36d72..3e092d6 100644
--- a/src/mesh.cpp
+++ b/src/mesh.cpp
@@ -56,59 +56,6 @@ typedef std::pair Vec3i;
////////////////////////////////////////////////////////////////////////////////
-Mesh* Mesh::load_stl_hash(const QString& filename)
-{
- QFile file(filename);
- file.open(QIODevice::ReadOnly);
-
- QDataStream data(&file);
- data.setByteOrder(QDataStream::LittleEndian);
- data.setFloatingPointPrecision(QDataStream::SinglePrecision);
-
- // Skip .stl file header
- data.skipRawData(80);
-
- // Load the triangle count from the .stl file
- uint32_t tri_count;
- data >> tri_count;
-
- // This vector will store triangles as sets of 3 indices
- std::vector indices(tri_count * 3);
-
- std::vector verts;
- verts.reserve(tri_count * 9);
-
- QHash map;
- map.reserve(tri_count * 3);
-
- float xyz[3];
- QByteArray v(sizeof(xyz), 0);
- for (unsigned i=0; i < tri_count; ++i)
- {
- // Skip face's normal vector
- data.readRawData(reinterpret_cast(xyz), 3*sizeof(float));
-
- for (int j=0; j < 3; ++j)
- {
- data >> xyz[0] >> xyz[1] >> xyz[2];
- memcpy(v.data(), xyz, sizeof(xyz));
- if (!map.contains(v))
- {
- map[v] = verts.size() / 3;
- verts.push_back(xyz[0]);
- verts.push_back(xyz[1]);
- verts.push_back(xyz[2]);
- }
- indices[i*3 + j] = map[v];
- }
-
- // Skip face attribute
- data.readRawData(reinterpret_cast(xyz), sizeof(uint16_t));
- }
-
- return new Mesh(verts, indices);
-}
-
Mesh* Mesh::load_stl(const QString& filename)
{
QFile file(filename);
@@ -127,12 +74,15 @@ Mesh* Mesh::load_stl(const QString& filename)
// Extract vertices into an array of xyz, unsigned pairs
QVector verts(tri_count*3);
- float xyz[3];
+
+ // Dummy array, because readRawData is faster than skipRawData
+ char buffer[sizeof(float)*3];
+
// Store vertices in the array, processing one triangle at a time.
for (auto v=verts.begin(); v != verts.end(); v += 3)
{
// Skip face's normal vector
- data.readRawData(reinterpret_cast(xyz), 3*sizeof(float));
+ data.readRawData(buffer, 3*sizeof(float));
// Load vertex data from .stl file into vertices
data >> v[0].first.x >> v[0].first.y >> v[0].first.z;
@@ -140,7 +90,7 @@ Mesh* Mesh::load_stl(const QString& filename)
data >> v[2].first.x >> v[2].first.y >> v[2].first.z;
// Skip face attribute
- data.readRawData(reinterpret_cast(xyz), sizeof(uint16_t));
+ data.readRawData(buffer, sizeof(uint16_t));
}
// Save indicies as the second element in the array
@@ -171,7 +121,7 @@ Mesh* Mesh::load_stl(const QString& filename)
}
verts.resize(vertex_count);
- std::vector flat_verts;
+ std::vector flat_verts;
flat_verts.reserve(vertex_count*3);
for (auto v : verts)
{
diff --git a/src/mesh.h b/src/mesh.h
index 80f2a1e..f83a477 100644
--- a/src/mesh.h
+++ b/src/mesh.h
@@ -11,7 +11,6 @@ class Mesh
public:
Mesh(std::vector vertices, std::vector indices);
static Mesh* load_stl(const QString& filename);
- static Mesh* load_stl_hash(const QString& filename);
float min(size_t start) const;
float max(size_t start) const;
From e36ff6ae9c3002ff915fb0dfcb8b3462430cc67b Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Mon, 24 Mar 2014 19:11:17 -0400
Subject: [PATCH 04/16] Adding zoom and pan
---
src/canvas.cpp | 40 ++++++++++++++++++++++++++++++++++------
src/canvas.h | 2 ++
2 files changed, 36 insertions(+), 6 deletions(-)
diff --git a/src/canvas.cpp b/src/canvas.cpp
index 03f9376..1539469 100644
--- a/src/canvas.cpp
+++ b/src/canvas.cpp
@@ -10,7 +10,7 @@
Canvas::Canvas(const QGLFormat& format, QWidget *parent)
: QGLWidget(format, parent), mesh(NULL),
- scale(1), tilt(90), yaw(0), status(" ")
+ scale(1), zoom(1), tilt(90), yaw(0), status(" ")
{
// Nothing to do here
}
@@ -122,12 +122,14 @@ QMatrix4x4 Canvas::view_matrix() const
{
m.scale(-1, width() / float(height()), 0.5);
}
+ m.scale(zoom, zoom, 1);
return m;
}
void Canvas::mousePressEvent(QMouseEvent* event)
{
- if (event->button() == Qt::LeftButton)
+ if (event->button() == Qt::LeftButton ||
+ event->button() == Qt::RightButton)
{
mouse_pos = event->pos();
setCursor(Qt::ClosedHandCursor);
@@ -136,7 +138,8 @@ void Canvas::mousePressEvent(QMouseEvent* event)
void Canvas::mouseReleaseEvent(QMouseEvent* event)
{
- if (event->button() == Qt::LeftButton)
+ if (event->button() == Qt::LeftButton ||
+ event->button() == Qt::RightButton)
{
unsetCursor();
}
@@ -144,13 +147,38 @@ void Canvas::mouseReleaseEvent(QMouseEvent* event)
void Canvas::mouseMoveEvent(QMouseEvent* event)
{
+ auto p = event->pos();
+ auto d = p - mouse_pos;
+
if (event->buttons() & Qt::LeftButton)
{
- auto p = event->pos();
- auto d = p - mouse_pos;
yaw = fmod(yaw - d.x(), 360);
tilt = fmax(0, fmin(180, tilt - d.y()));
- mouse_pos = p;
update();
}
+ else if (event->buttons() & Qt::RightButton)
+ {
+ qDebug() << d;
+ 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)
+{
+ if (event->delta() < 0)
+ {
+ for (int i=0; i > event->delta(); --i)
+ zoom *= 1.001;
+ }
+ else if (event->delta() > 0)
+ {
+ for (int i=0; i < event->delta(); ++i)
+ zoom /= 1.001;
+ }
+ update();
}
diff --git a/src/canvas.h b/src/canvas.h
index 64a8dd5..33313cf 100644
--- a/src/canvas.h
+++ b/src/canvas.h
@@ -32,6 +32,7 @@ protected:
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
+ void wheelEvent(QWheelEvent* event);
private:
@@ -48,6 +49,7 @@ private:
QVector3D center;
float scale;
+ float zoom;
float tilt;
float yaw;
From 9283aa4752f20951dbb82975d209ee147eea0ec1 Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Mon, 24 Mar 2014 19:14:17 -0400
Subject: [PATCH 05/16] Compensate for z-flattening when zooming
---
gl/mesh.frag | 6 ++++++
src/canvas.cpp | 3 +++
2 files changed, 9 insertions(+)
diff --git a/gl/mesh.frag b/gl/mesh.frag
index 5949025..d7a54d5 100644
--- a/gl/mesh.frag
+++ b/gl/mesh.frag
@@ -1,12 +1,18 @@
#version 120
+uniform float zoom;
+
varying vec3 ec_pos;
void main() {
vec3 base3 = vec3(0.99, 0.96, 0.89);
vec3 base2 = vec3(0.92, 0.91, 0.83);
vec3 base00 = vec3(0.40, 0.48, 0.51);
+
vec3 ec_normal = normalize(cross(dFdx(ec_pos), dFdy(ec_pos)));
+ ec_normal.z *= zoom;
+ ec_normal = normalize(ec_normal);
+
float a = dot(ec_normal, vec3(0.0, 0.0, 1.0));
float b = dot(ec_normal, vec3(-0.57, -0.57, 0.57));
diff --git a/src/canvas.cpp b/src/canvas.cpp
index 1539469..1a1e060 100644
--- a/src/canvas.cpp
+++ b/src/canvas.cpp
@@ -89,6 +89,9 @@ void Canvas::draw_mesh()
mesh_shader.uniformLocation("view_matrix"),
1, GL_FALSE, view_matrix().data());
+ // Compensate for z-flattening when zooming
+ glUniform1f(mesh_shader.uniformLocation("zoom"), 1/zoom);
+
// Find and enable the attribute location for vertex position
const GLuint vp = mesh_shader.attributeLocation("vertex_position");
glEnableVertexAttribArray(vp);
From 54206d3f9caa44f91c51547cd1edbfc4091cc908 Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Mon, 24 Mar 2014 19:32:12 -0400
Subject: [PATCH 06/16] Zoom about mouse cursor
---
src/canvas.cpp | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/canvas.cpp b/src/canvas.cpp
index 1a1e060..15624da 100644
--- a/src/canvas.cpp
+++ b/src/canvas.cpp
@@ -161,7 +161,6 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
}
else if (event->buttons() & Qt::RightButton)
{
- qDebug() << d;
center = transform_matrix().inverted() *
view_matrix().inverted() *
QVector3D(-d.x() / (0.5*width()),
@@ -173,6 +172,14 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
void Canvas::wheelEvent(QWheelEvent *event)
{
+ // Find GL position before the zoom operation
+ // (to zoom about mouse cursor)
+ auto p = event->pos();
+ 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)
{
for (int i=0; i > event->delta(); --i)
@@ -183,5 +190,10 @@ void Canvas::wheelEvent(QWheelEvent *event)
for (int i=0; i < event->delta(); ++i)
zoom /= 1.001;
}
+
+ // Then find the cursor's GL position post-zoom and adjust center.
+ QVector3D b = transform_matrix().inverted() *
+ view_matrix().inverted() * v;
+ center += b - a;
update();
}
From 25a7138328a2b54bc1476621bbc97ef37a65d858 Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Mon, 24 Mar 2014 19:49:11 -0400
Subject: [PATCH 07/16] Reset camera parameters after loading model
---
src/canvas.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/canvas.cpp b/src/canvas.cpp
index 15624da..dd645a5 100644
--- a/src/canvas.cpp
+++ b/src/canvas.cpp
@@ -30,6 +30,12 @@ void Canvas::load_mesh(Mesh* m)
pow(m->xmax() - m->xmin(), 2) +
pow(m->ymax() - m->ymin(), 2) +
pow(m->zmax() - m->zmin(), 2));
+
+ // Reset other camera parameters
+ zoom = 1;
+ yaw = 0;
+ tilt = 90;
+
update();
delete m;
From 236a2033208b7c207e54be6199dde48434982abb Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Mon, 24 Mar 2014 21:00:50 -0400
Subject: [PATCH 08/16] Add similar script for windows
---
exe/package.sh | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 exe/package.sh
diff --git a/exe/package.sh b/exe/package.sh
new file mode 100644
index 0000000..5faecf1
--- /dev/null
+++ b/exe/package.sh
@@ -0,0 +1,3 @@
+cd ..
+cp build/release/fstl.exe .
+/c/Program\ Files/7-Zip/7z.exe a fstl.zip fstl.exe README.md
From 428aff52a5780af24436a896fec1b6fcd65eea5e Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Mon, 24 Mar 2014 20:14:13 -0400
Subject: [PATCH 09/16] Raise an error message box on ascii stl
---
src/loader.cpp | 14 ++++++++++----
src/loader.h | 1 +
src/window.cpp | 10 ++++++++++
src/window.h | 1 +
4 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/src/loader.cpp b/src/loader.cpp
index 107d90f..d7ce46a 100644
--- a/src/loader.cpp
+++ b/src/loader.cpp
@@ -8,10 +8,16 @@ Loader::Loader(QObject* parent, const QString& filename)
void Loader::run()
{
- QTime timer;
- timer.start();
- emit got_mesh(Mesh::load_stl(filename));
- qDebug() << "Time taken:" << timer.elapsed();
+ { // Verify that this isn't an ascii stl file
+ QFile file(filename);
+ file.open(QIODevice::ReadOnly);
+ if (file.read(5) == "solid")
+ {
+ emit error_ascii_stl();
+ return;
+ }
+ }
+ emit got_mesh(Mesh::load_stl(filename));
emit loaded_file(filename);
}
diff --git a/src/loader.h b/src/loader.h
index 350985c..85e0c53 100644
--- a/src/loader.h
+++ b/src/loader.h
@@ -15,6 +15,7 @@ public:
signals:
void loaded_file(QString filename);
void got_mesh(Mesh* m);
+ void error_ascii_stl();
private:
const QString filename;
diff --git a/src/window.cpp b/src/window.cpp
index 15ffb03..5f8ac9e 100644
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -69,6 +69,14 @@ void Window::on_about()
" style=\"color: #93a1a1;\">matt.j.keeter@gmail.com
");
}
+void Window::on_ascii_stl()
+{
+ QMessageBox::critical(this, "Error",
+ "Error:
"
+ "Cannot open ASCII .stl
file
"
+ "Please convert to binary .stl
and retry");
+}
+
void Window::enable_open()
{
open_action->setEnabled(true);
@@ -91,6 +99,8 @@ bool Window::load_stl(const QString& filename)
connect(loader, &Loader::got_mesh,
canvas, &Canvas::load_mesh);
+ connect(loader, &Loader::error_ascii_stl,
+ this, &Window::on_ascii_stl);
connect(loader, &Loader::finished,
loader, &Loader::deleteLater);
diff --git a/src/window.h b/src/window.h
index 22c35cb..8484822 100644
--- a/src/window.h
+++ b/src/window.h
@@ -15,6 +15,7 @@ public:
public slots:
void on_open();
void on_about();
+ void on_ascii_stl();
void enable_open();
void disable_open();
From 70f0297d209e04f3de354a3684759eecc6077719 Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Mon, 24 Mar 2014 20:50:06 -0400
Subject: [PATCH 10/16] Small bash script to run macdeployqt then clean up
unused frameworks
---
app/package.sh | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100755 app/package.sh
diff --git a/app/package.sh b/app/package.sh
new file mode 100755
index 0000000..d012bfb
--- /dev/null
+++ b/app/package.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+cd ../build
+macdeployqt fstl.app
+cd fstl.app/Contents/PlugIns
+rm -rf accessible audio imageformats mediaservice playlistformats position printsupport qml1tooling sensorgestures sensors
+cd ../Frameworks
+rm -rf QtDeclarative.framework QtMultimedia.framework QtMultimediaWidgets.framework QtNetwork.framework QtPositioning.framework QtQml.framework QtQuick.framework QtScript.framework QtSensors.framework QtSql.framework QtXmlPatterns.framework
+cd ../Resources
+rm empty.lproj
+
From 030f0c5161fc2da378ff5af8aa20610555f90188 Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Mon, 24 Mar 2014 20:53:12 -0400
Subject: [PATCH 11/16] and compress the app and the README into a zip file
---
app/package.sh | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/package.sh b/app/package.sh
index d012bfb..da97313 100755
--- a/app/package.sh
+++ b/app/package.sh
@@ -7,4 +7,8 @@ cd ../Frameworks
rm -rf QtDeclarative.framework QtMultimedia.framework QtMultimediaWidgets.framework QtNetwork.framework QtPositioning.framework QtQml.framework QtQuick.framework QtScript.framework QtSensors.framework QtSql.framework QtXmlPatterns.framework
cd ../Resources
rm empty.lproj
+cd ../../..
+cp -r fstl.app ..
+cd ..
+zip -r fstl.zip fstl.app README.md
From de5c4b440a941e20420262a0b152f435e4cc5bb8 Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Mon, 24 Mar 2014 21:11:25 -0400
Subject: [PATCH 12/16] Add mac/win suffixes to zip files
---
app/package.sh | 2 +-
exe/package.sh | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/package.sh b/app/package.sh
index da97313..d51c470 100755
--- a/app/package.sh
+++ b/app/package.sh
@@ -10,5 +10,5 @@ rm empty.lproj
cd ../../..
cp -r fstl.app ..
cd ..
-zip -r fstl.zip fstl.app README.md
+zip -r fstl_mac.zip fstl.app README.md
diff --git a/exe/package.sh b/exe/package.sh
index 5faecf1..ef81b62 100644
--- a/exe/package.sh
+++ b/exe/package.sh
@@ -1,3 +1,3 @@
cd ..
cp build/release/fstl.exe .
-/c/Program\ Files/7-Zip/7z.exe a fstl.zip fstl.exe README.md
+/c/Program\ Files/7-Zip/7z.exe a fstl_win.zip fstl.exe README.md
From 9cc3bd82e832f5d49911048d6dc053e177922abf Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Tue, 25 Mar 2014 18:53:07 -0400
Subject: [PATCH 13/16] Added check for stl corruption
---
src/loader.cpp | 14 ++++++++++++++
src/loader.h | 1 +
src/window.cpp | 10 ++++++++++
src/window.h | 1 +
4 files changed, 26 insertions(+)
diff --git a/src/loader.cpp b/src/loader.cpp
index d7ce46a..355f40c 100644
--- a/src/loader.cpp
+++ b/src/loader.cpp
@@ -4,6 +4,7 @@
Loader::Loader(QObject* parent, const QString& filename)
: QThread(parent), filename(filename)
{
+ // Nothing to do here
}
void Loader::run()
@@ -16,6 +17,19 @@ void Loader::run()
emit error_ascii_stl();
return;
}
+
+ // Skip the rest of the buffer
+ file.read(75);
+
+ // Assume we're on a little-endian system for simplicity
+ uint32_t tri_count;
+ file.read(reinterpret_cast(&tri_count), sizeof(tri_count));
+
+ if (file.size() != 84 + tri_count*50)
+ {
+ emit error_bad_stl();
+ return;
+ }
}
emit got_mesh(Mesh::load_stl(filename));
diff --git a/src/loader.h b/src/loader.h
index 85e0c53..1de1157 100644
--- a/src/loader.h
+++ b/src/loader.h
@@ -16,6 +16,7 @@ signals:
void loaded_file(QString filename);
void got_mesh(Mesh* m);
void error_ascii_stl();
+ void error_bad_stl();
private:
const QString filename;
diff --git a/src/window.cpp b/src/window.cpp
index 5f8ac9e..2d809bf 100644
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -77,6 +77,14 @@ void Window::on_ascii_stl()
"Please convert to binary .stl
and retry");
}
+void Window::on_bad_stl()
+{
+ QMessageBox::critical(this, "Error",
+ "Error:
"
+ "This .stl
file is invalid or corrupted.
"
+ "Please export it from the original source, verify, and retry.");
+}
+
void Window::enable_open()
{
open_action->setEnabled(true);
@@ -101,6 +109,8 @@ bool Window::load_stl(const QString& filename)
canvas, &Canvas::load_mesh);
connect(loader, &Loader::error_ascii_stl,
this, &Window::on_ascii_stl);
+ connect(loader, &Loader::error_bad_stl,
+ this, &Window::on_bad_stl);
connect(loader, &Loader::finished,
loader, &Loader::deleteLater);
diff --git a/src/window.h b/src/window.h
index 8484822..3ac5e95 100644
--- a/src/window.h
+++ b/src/window.h
@@ -16,6 +16,7 @@ public slots:
void on_open();
void on_about();
void on_ascii_stl();
+ void on_bad_stl();
void enable_open();
void disable_open();
From 61c930444d093b2d1bb43b72a2f34b4a7d748eaf Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Tue, 25 Mar 2014 20:01:46 -0400
Subject: [PATCH 14/16] Move load_stl from Mesh to Loader
---
src/loader.cpp | 134 +++++++++++++++++++++++++++++++++++++++++--------
src/loader.h | 4 ++
src/mesh.cpp | 99 ------------------------------------
src/mesh.h | 1 -
4 files changed, 117 insertions(+), 121 deletions(-)
diff --git a/src/loader.cpp b/src/loader.cpp
index 355f40c..0d6b165 100644
--- a/src/loader.cpp
+++ b/src/loader.cpp
@@ -1,5 +1,4 @@
#include "loader.h"
-#include "mesh.h"
Loader::Loader(QObject* parent, const QString& filename)
: QThread(parent), filename(filename)
@@ -9,29 +8,122 @@ Loader::Loader(QObject* parent, const QString& filename)
void Loader::run()
{
- { // Verify that this isn't an ascii stl file
- QFile file(filename);
- file.open(QIODevice::ReadOnly);
- if (file.read(5) == "solid")
- {
- emit error_ascii_stl();
- return;
- }
+ Mesh* mesh = load_stl();
+ if (mesh)
+ {
+ emit got_mesh(mesh);
+ emit loaded_file(filename);
+ }
+}
- // Skip the rest of the buffer
- file.read(75);
- // Assume we're on a little-endian system for simplicity
- uint32_t tri_count;
- file.read(reinterpret_cast(&tri_count), sizeof(tri_count));
+////////////////////////////////////////////////////////////////////////////////
- if (file.size() != 84 + tri_count*50)
- {
- emit error_bad_stl();
- return;
- }
+struct Vec3
+{
+ GLfloat x, y, z;
+ bool operator!=(const Vec3& rhs) const
+ {
+ return x != rhs.x || y != rhs.y || z != rhs.z;
+ }
+ bool operator<(const Vec3& 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;
+ }
+};
+
+typedef std::pair Vec3i;
+
+////////////////////////////////////////////////////////////////////////////////
+
+Mesh* Loader::load_stl()
+{
+ QFile file(filename);
+ file.open(QIODevice::ReadOnly);
+ if (file.read(5) == "solid")
+ {
+ emit error_ascii_stl();
+ return NULL;
+ }
+ // Skip the rest of the header material
+ file.read(75);
+
+ QDataStream data(&file);
+ data.setByteOrder(QDataStream::LittleEndian);
+ data.setFloatingPointPrecision(QDataStream::SinglePrecision);
+
+ // Load the triangle count from the .stl file
+ uint32_t tri_count;
+ data >> tri_count;
+
+ // Verify that the file is the right size
+ if (file.size() != 84 + tri_count*50)
+ {
+ emit error_bad_stl();
+ return NULL;
}
- emit got_mesh(Mesh::load_stl(filename));
- emit loaded_file(filename);
+ // Extract vertices into an array of xyz, unsigned pairs
+ QVector verts(tri_count*3);
+
+ // Dummy array, because readRawData is faster than skipRawData
+ char buffer[sizeof(float)*3];
+
+ // Store vertices in the array, processing one triangle at a time.
+ for (auto v=verts.begin(); v != verts.end(); v += 3)
+ {
+ // Skip face's normal vector
+ data.readRawData(buffer, 3*sizeof(float));
+
+ // Load vertex data from .stl file into vertices
+ data >> v[0].first.x >> v[0].first.y >> v[0].first.z;
+ data >> v[1].first.x >> v[1].first.y >> v[1].first.z;
+ data >> v[2].first.x >> v[2].first.y >> v[2].first.z;
+
+ // Skip face attribute
+ data.readRawData(buffer, sizeof(uint16_t));
+ }
+
+ // 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)
+ {
+ verts[i].second = i;
+ }
+
+ // Sort the set of vertices (to deduplicate)
+ std::sort(verts.begin(), verts.end());
+
+ // This vector will store triangles as sets of 3 indices
+ std::vector 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.first != verts[vertex_count-1].first)
+ {
+ verts[vertex_count++] = v;
+ }
+ indices[v.second] = vertex_count - 1;
+ }
+ verts.resize(vertex_count);
+
+ std::vector flat_verts;
+ flat_verts.reserve(vertex_count*3);
+ for (auto v : verts)
+ {
+ flat_verts.push_back(v.first.x);
+ flat_verts.push_back(v.first.y);
+ flat_verts.push_back(v.first.z);
+ }
+
+ return new Mesh(flat_verts, indices);
}
+
diff --git a/src/loader.h b/src/loader.h
index 1de1157..fb0c8d8 100644
--- a/src/loader.h
+++ b/src/loader.h
@@ -12,9 +12,13 @@ public:
explicit Loader(QObject* parent, const QString& filename);
void run();
+protected:
+ Mesh* load_stl();
+
signals:
void loaded_file(QString filename);
void got_mesh(Mesh* m);
+
void error_ascii_stl();
void error_bad_stl();
diff --git a/src/mesh.cpp b/src/mesh.cpp
index 3e092d6..cfb4ca4 100644
--- a/src/mesh.cpp
+++ b/src/mesh.cpp
@@ -2,7 +2,6 @@
#include
#include
-#include
#include
#include "mesh.h"
@@ -34,101 +33,3 @@ float Mesh::max(size_t start) const
}
return v;
}
-////////////////////////////////////////////////////////////////////////////////
-
-struct Vec3
-{
- GLfloat x, y, z;
- bool operator!=(const Vec3& rhs) const
- {
- return x != rhs.x || y != rhs.y || z != rhs.z;
- }
- bool operator<(const Vec3& 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;
- }
-};
-
-typedef std::pair Vec3i;
-
-////////////////////////////////////////////////////////////////////////////////
-
-Mesh* Mesh::load_stl(const QString& filename)
-{
- QFile file(filename);
- file.open(QIODevice::ReadOnly);
-
- QDataStream data(&file);
- data.setByteOrder(QDataStream::LittleEndian);
- data.setFloatingPointPrecision(QDataStream::SinglePrecision);
-
- // Skip .stl file header
- data.skipRawData(80);
-
- // Load the triangle count from the .stl file
- uint32_t tri_count;
- data >> tri_count;
-
- // Extract vertices into an array of xyz, unsigned pairs
- QVector verts(tri_count*3);
-
- // Dummy array, because readRawData is faster than skipRawData
- char buffer[sizeof(float)*3];
-
- // Store vertices in the array, processing one triangle at a time.
- for (auto v=verts.begin(); v != verts.end(); v += 3)
- {
- // Skip face's normal vector
- data.readRawData(buffer, 3*sizeof(float));
-
- // Load vertex data from .stl file into vertices
- data >> v[0].first.x >> v[0].first.y >> v[0].first.z;
- data >> v[1].first.x >> v[1].first.y >> v[1].first.z;
- data >> v[2].first.x >> v[2].first.y >> v[2].first.z;
-
- // Skip face attribute
- data.readRawData(buffer, sizeof(uint16_t));
- }
-
- // 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)
- {
- verts[i].second = i;
- }
-
- // Sort the set of vertices (to deduplicate)
- std::sort(verts.begin(), verts.end());
-
- // This vector will store triangles as sets of 3 indices
- std::vector 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.first != verts[vertex_count-1].first)
- {
- verts[vertex_count++] = v;
- }
- indices[v.second] = vertex_count - 1;
- }
- verts.resize(vertex_count);
-
- std::vector flat_verts;
- flat_verts.reserve(vertex_count*3);
- for (auto v : verts)
- {
- flat_verts.push_back(v.first.x);
- flat_verts.push_back(v.first.y);
- flat_verts.push_back(v.first.z);
- }
-
- return new Mesh(flat_verts, indices);
-}
diff --git a/src/mesh.h b/src/mesh.h
index f83a477..e8a02f0 100644
--- a/src/mesh.h
+++ b/src/mesh.h
@@ -10,7 +10,6 @@ class Mesh
{
public:
Mesh(std::vector vertices, std::vector indices);
- static Mesh* load_stl(const QString& filename);
float min(size_t start) const;
float max(size_t start) const;
From d9301bbba097438546344405bd8899b0311cdc85 Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Wed, 26 Mar 2014 21:40:59 -0400
Subject: [PATCH 15/16] Fix double-click to open .stl on windows
---
src/app.cpp | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/app.cpp b/src/app.cpp
index afa4ea7..b6eb3af 100644
--- a/src/app.cpp
+++ b/src/app.cpp
@@ -8,7 +8,10 @@ App::App(int argc, char *argv[]) :
QApplication(argc, argv), window(new Window())
{
window->show();
- window->load_stl(":gl/sphere.stl");
+ if (argc > 1)
+ window->load_stl(argv[1]);
+ else
+ window->load_stl(":gl/sphere.stl");
}
bool App::event(QEvent* e)
From 5a38a2db976cf17640808317694c2aaa7091fe35 Mon Sep 17 00:00:00 2001
From: Matt Keeter
Date: Wed, 26 Mar 2014 21:51:58 -0400
Subject: [PATCH 16/16] Allow dropping of files onto window
---
src/window.cpp | 16 ++++++++++++++++
src/window.h | 4 ++++
2 files changed, 20 insertions(+)
diff --git a/src/window.cpp b/src/window.cpp
index 2d809bf..3002ecc 100644
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -14,6 +14,7 @@ Window::Window(QWidget *parent) :
{
setWindowTitle("fstl");
+ setAcceptDrops(true);
QFile styleFile(":/qt/style.qss");
styleFile.open( QFile::ReadOnly );
@@ -128,3 +129,18 @@ bool Window::load_stl(const QString& filename)
loader->start();
return true;
}
+
+void Window::dragEnterEvent(QDragEnterEvent *event)
+{
+ 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)
+{
+ load_stl(event->mimeData()->urls().front().toLocalFile());
+}
diff --git a/src/window.h b/src/window.h
index 3ac5e95..cb92ebb 100644
--- a/src/window.h
+++ b/src/window.h
@@ -12,6 +12,10 @@ public:
explicit Window(QWidget* parent=0);
bool load_stl(const QString& filename);
+protected:
+ void dragEnterEvent(QDragEnterEvent* event);
+ void dropEvent(QDropEvent* event);
+
public slots:
void on_open();
void on_about();