diff --git a/GCodeViewer.pro b/GCodeViewer.pro new file mode 100644 index 0000000..b0c5f02 --- /dev/null +++ b/GCodeViewer.pro @@ -0,0 +1,23 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2011-05-11T22:05:25 +# +#------------------------------------------------- + +QT += core gui +QT += opengl + +TARGET = GCodeViewer +TEMPLATE = app + + +SOURCES += main.cpp\ + mainwindow.cpp \ + gcode.cpp \ + gcodeview.cpp + +HEADERS += mainwindow.h \ + gcode.h \ + gcodeview.h + +FORMS += mainwindow.ui diff --git a/gcode.cpp b/gcode.cpp new file mode 100644 index 0000000..fb076a8 --- /dev/null +++ b/gcode.cpp @@ -0,0 +1,107 @@ +#include "gcode.h" + +char gcode::codes[] = "ABDEFGHIJKLMPQRSTXYZ"; + + +gcode::gcode(string command) : + command(command) { + + // cout << "parsing command: " << command << std::endl; + + // Parse (and strip) any comments out into a comment string + parseComments(); + + // Parse any codes out into the code tables + parseCodes(); +} + + +// Find any comments, store them, then remove them from the command +// TODO: Handle this correctly. For now, we just look for ( and bail. +void gcode::parseComments() { + if (command.find_first_of(")") != string::npos) { + comment = command.substr(command.find_first_of("(")+1); + command = command.substr(0,command.find_first_of("(")); + // cout << " comment=" << comment << std::endl; + } + +} + + +// Find any codes, and store them +void gcode::parseCodes() { + // For each code letter we know about, scan for it and record it's value. + int codeIndex = 0; + + while (codes[codeIndex]!=0) { + // Search the command for an occurance of said code letter + if (command.find_first_of(codes[codeIndex]) != string::npos) { + double value = atof(command.substr(command.find_first_of(codes[codeIndex])+1).c_str()); + + // cout << " code=" << codes[codeIndex] << " value=" << value << std::endl; + parameters.push_back(gCodeParameter(codes[codeIndex],value)); + } + codeIndex++; + } +} + + +string gcode::getCommand() { + // TODO: Note that this is the command minus any comments. + return string(command); +} + + +string gcode::getComment() { + return string(comment); +} + + +bool gcode::hasCode(char searchCode) { + for (int i = 0; i < parameters.size(); i++) { + if(parameters[i].code == searchCode) { + return true; + } + } + + return false; +} + + +double gcode::getCodeValue(char searchCode) { + for (int i = 0; i < parameters.size(); i++) { + if(parameters[i].code == searchCode) { + return parameters[i].value; + } + } + + return -1; // TODO: What do we return if there is no code? +} + + +gcodeModel::gcodeModel() { + +} + +void gcodeModel::loadGCode(string filename) { + points.clear(); + + ifstream file; + + // TODO: error checking! + file.open(filename.c_str()); + + while (file.good()) { + string line; + + std::getline(file, line); + gcode code = gcode(line); + + // cout << " hascodeG:" << code.hasCode('G') << std::endl; + + // If the code contains a line, let's add it to our list. + if (code.hasCode('G') && (int)code.getCodeValue('G') == 1) { + points.push_back(point(code.getCodeValue('X'), code.getCodeValue('Y'),code.getCodeValue('Z'))); + } + } +} diff --git a/gcode.h b/gcode.h new file mode 100644 index 0000000..9802cbc --- /dev/null +++ b/gcode.h @@ -0,0 +1,76 @@ +#ifndef GCODE_H +#define GCODE_H + +#include +#include +#include + +using std::string; +using std::vector; +using std::ifstream; + +// Object that represents a single parsed line of GCode. +class gcode { +private: + class gCodeParameter { + public: + char code; + double value; + + gCodeParameter(char code, double value) { + this->code = code; + this->value = value; + } + }; + + // These are the letter codes that we understand + static char codes[]; + + // The actual GCode command string + string command; + + // Parsed out comment + string comment; + + // The set of parameters in this GCode + vector parameters; + + // Find any comments, store them, then remove them from the command + void parseComments(); + + // Find any codes, and store them + void parseCodes(); + +public: + gcode(string command); + + string getCommand(); + string getComment(); + bool hasCode(char searchCode); + double getCodeValue(char searchCode); +}; + +// TODO: Use whatever the equivalent class here should be. +struct point { +public: + float x; + float y; + float z; + + point(float x, float y, float z) : x(x), y(y), z(z) {} +}; + +// Object that can open a file containing GCode and turn it into a series of lines +class gcodeModel { +public: + // For now, we have a long list of points to string together, that is public! + vector points; + +public: + gcodeModel(); + + void loadGCode(string filename); +}; + + +#endif // GCODE_H diff --git a/gcodeview.cpp b/gcodeview.cpp new file mode 100644 index 0000000..28a5770 --- /dev/null +++ b/gcodeview.cpp @@ -0,0 +1,178 @@ +#include "gcodeview.h" + +#include +#include + +#include + +#import +using namespace std; + +static void qNormalizeAngle(int &angle) +{ + while (angle < 0) + angle += 360 * 16; + while (angle > 360 * 16) + angle -= 360 * 16; +} + +GcodeView::GcodeView(QWidget *parent) + : QGLWidget(parent) +{ + resetView(); + + resizeGL(this->width(),this->height()); +} + +void GcodeView::resetView() { + xRot = 0; + yRot = 0; + zRot = 0; + scale = .1; +} + +void GcodeView::setXRotation(int angle) +{ + qNormalizeAngle(angle); + if (angle != xRot) { + xRot = angle; + emit xRotationChanged(angle); + updateGL(); + } +} + +void GcodeView::setYRotation(int angle) +{ + qNormalizeAngle(angle); + if (angle != yRot) { + yRot = angle; + emit yRotationChanged(angle); + updateGL(); + } +} + +void GcodeView::setZRotation(int angle) +{ + qNormalizeAngle(angle); + if (angle != zRot) { + zRot = angle; + emit xRotationChanged(angle); + updateGL(); + } +} + +void GcodeView::initializeGL() +{ + // Set up the rendering context, define display lists etc.: + glClearColor(0.0, 0.0, 0.0, 0.0); + glDisable(GL_DEPTH_TEST); + + glDisable(GL_LIGHTING); + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void GcodeView::setupViewport(int width, int height) +{ + int side = qMin(width, height); + glViewport((width - side) / 2, (height - side) / 2, side, side); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); +#ifdef QT_OPENGL_ES + glOrthof(-0.5, +0.5, -0.5, 0.5, 4.0, 15.0); +#else + glOrtho(-0.5, +0.5, -0.5, 0.5, 4.0, 15.0); +#endif + glMatrixMode(GL_MODELVIEW); +} + +void GcodeView::resizeGL(int width, int height) +{ + setupViewport(width, height); +} + +void GcodeView::paintGL() +{ + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + double widthRatio = (double)width()/(double)height(); + + // do something to the projection? + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-widthRatio, widthRatio, -1, 1, 10, 100); + glTranslatef(0.0f, 0.0f, -15.0f); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glViewport(0, 0, width(), height()); + + + glPushMatrix(); + + glScalef(scale, scale, scale); + +// glTranslatef(0.0, 0.0, -10.0); + glRotatef(xRot / 16.0, 1.0, 0.0, 0.0); + glRotatef(yRot / 16.0, 0.0, 1.0, 0.0); + glRotatef(zRot / 16.0, 0.0, 0.0, 1.0); + + glColor4f(1,1,1,.15); + glLineWidth(2); + glBegin(GL_LINE_STRIP); + + + for (unsigned int i = 1; i < model.points.size(); i++) { + point a = model.points[i-1]; + point b = model.points[i]; + glVertex3f(a.x, a.y, a.z); // origin of the line + glVertex3f(b.x, b.y, b.z); // ending point of the line + } + + glEnd( ); + + glPopMatrix(); +} + +void GcodeView::loadModel(QString filename) { + model.loadGCode(filename.toStdString()); + updateGL(); +} + +void GcodeView::mousePressEvent(QMouseEvent *event) +{ + lastPos = event->pos(); +} + +void GcodeView::mouseMoveEvent(QMouseEvent *event) +{ + int dx = event->x() - lastPos.x(); + int dy = event->y() - lastPos.y(); + + if (event->buttons() & Qt::LeftButton) { + setXRotation(xRot + 1 * dy); + setYRotation(yRot + 1 * dx); + } else if (event->buttons() & Qt::RightButton) { + setXRotation(xRot - 1 * dy); + setYRotation(yRot - 1 * dx); + } + lastPos = event->pos(); +} + +void GcodeView::wheelEvent(QWheelEvent *event) +{ + float newScale = scale*(1 + event->delta()/100.0); + + // if (newScale > 0.01 && newScale < 2) { + scale = newScale; + // } + + updateGL(); +} + +void GcodeView::mouseDoubleClickEvent(QMouseEvent event) { + resetView(); + updateGL(); +} + diff --git a/gcodeview.h b/gcodeview.h new file mode 100644 index 0000000..71a8481 --- /dev/null +++ b/gcodeview.h @@ -0,0 +1,51 @@ +#ifndef GCODEVIEW_H +#define GCODEVIEW_H + +#include +#include + +#include + +class GcodeView : public QGLWidget +{ + Q_OBJECT // must include this if you use Qt signals/slots + +public slots: + void setXRotation(int angle); + void setYRotation(int angle); + void setZRotation(int angle); + +signals: + void xRotationChanged(int angle); + void yRotationChanged(int angle); + void zRotationChanged(int angle); + +public: + GcodeView(QWidget *parent); + + void loadModel(QString filename); + + void setupViewport(int width, int height); + void resetView(); + +protected: + void initializeGL(); + void resizeGL(int w, int h); + void paintGL(); + + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void wheelEvent(QWheelEvent *event); + void mouseDoubleClickEvent(QMouseEvent event); + +private: + gcodeModel model; + QPoint lastPos; + + float xRot; + float yRot; + float zRot; + float scale; +}; + +#endif // GCODEVIEW_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..9ae175b --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..14637de --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,31 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "gcodeview.h" + +#include +#include +#include + + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); +// ui->graphicsView->loadModel("/Users/mattmets/Projects/repg-workspace/ReplicatorG/examples/wfu_cbi_skull.gcode"); + ui->graphicsView->loadModel("/Users/mattmets/Projects/repg-workspace/ReplicatorG/examples/Mini_Snake.gcode"); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::on_actionLoad_GCode_triggered() +{ + QString file = QFileDialog::getOpenFileName(this, tr("Open File"), + "/home", + tr("GCode (*.gcode)")); + + ui->graphicsView->loadModel(file); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..ce4937f --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,25 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +namespace Ui { + class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private slots: + void on_actionLoad_GCode_triggered(); + +private: + Ui::MainWindow *ui; +}; + +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..1cbefd9 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,64 @@ + + + MainWindow + + + + 0 + 0 + 953 + 560 + + + + MainWindow + + + + + + + + + + + + 0 + 0 + 953 + 22 + + + + + File + + + + + + + + + + + Open + + + + + Exit + + + + + + + GcodeView + QGraphicsView +
gcodeview.h
+
+
+ + +