├── g2m ├── g2m.h ├── calc_tolerance.hpp ├── CMakeLists.txt ├── nanotimer.hpp ├── point.hpp ├── canonMotion.cpp ├── linearMotion.hpp ├── canonMotionless.hpp ├── nanotimer.cpp ├── canonMotion.hpp ├── helicalMotion.hpp ├── canonLine.hpp ├── linearMotion.cpp ├── machineStatus.cpp ├── g2m.hpp ├── machineStatus.hpp ├── canonLine.cpp ├── canonMotionless.cpp ├── gplayer.hpp ├── g2m.cpp └── helicalMotion.cpp ├── debian ├── compat ├── control └── rules ├── .github ├── FUNDING.yml └── workflows │ ├── label.yml │ └── main.yml ├── icons └── qgcoder.png ├── doc ├── qgcoder-001.png └── qgcoder-002.png ├── res.qrc ├── tests └── ngc-urandom.sh ├── qgcoder.desktop ├── .gitignore ├── settings_dlg.h ├── view.h ├── README.md ├── settings_dlg.cpp ├── mainwin.h ├── main.cpp ├── qgcoder.pro ├── lex_analyzer.hpp ├── lex_analyzer.cpp ├── settings.ui ├── view.cpp ├── mainwin.cpp ├── mainwin.ui └── LICENSE /g2m/g2m.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /g2m/calc_tolerance.hpp: -------------------------------------------------------------------------------- 1 | #define CALC_TOLERANCE (1e-6) 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: koppi 2 | custom: 'https://koppi.github.io' 3 | -------------------------------------------------------------------------------- /icons/qgcoder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QGCoder/qgcoder/HEAD/icons/qgcoder.png -------------------------------------------------------------------------------- /doc/qgcoder-001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QGCoder/qgcoder/HEAD/doc/qgcoder-001.png -------------------------------------------------------------------------------- /doc/qgcoder-002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QGCoder/qgcoder/HEAD/doc/qgcoder-002.png -------------------------------------------------------------------------------- /res.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/qgcoder.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/ngc-urandom.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | N=$1; echo "F100"; cat /dev/urandom | hexdump -v -e '/1 "%u\n"' | paste - - - | awk '{ print "G1 X"$1" Y"$2" Z"$3 }'|head -n $N; echo "M30" 4 | -------------------------------------------------------------------------------- /qgcoder.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Encoding=UTF-8 3 | Version=1.0 4 | Name=qgcoder 5 | GenericName=qgcoder 6 | Comment=G-code editor with 3D preview 7 | Icon=/usr/share/icons/hicolor/256x256/apps/qgcoder.png 8 | 9 | Type=Application 10 | Categories=Engineering; 11 | MimeType=text/x.gcode 12 | Exec=qgcoder %f 13 | Terminal=false 14 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: qgcoder 2 | Section: misc 3 | Priority: optional 4 | Maintainer: Jakob Flierl 5 | Uploaders: Jakob Flierl 6 | Build-Depends: debhelper (>= 7.0.50~), automake, libtool, qt5-qmake, qtbase5-dev, libqglviewer-dev-qt5, freeglut3-dev, libglew-dev 7 | Standards-Version: 3.9.1 8 | Homepage: https://github.com/QGCoder 9 | Vcs-Git: git://github.com/QGCoder/qgcoder 10 | Vcs-Browser: https://github.com/QGCoder/qgcoder 11 | 12 | Package: qgcoder 13 | Architecture: any 14 | Depends: ${shlibs:Depends}, ${misc:Depends} 15 | Description: qgcoder 16 | G-code editor with 3D preview. 17 | -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | # This workflow will triage pull requests and apply a label based on the 2 | # paths that are modified in the pull request. 3 | # 4 | # To use this workflow, you will need to set up a .github/labeler.yml 5 | # file with configuration. For more information, see: 6 | # https://github.com/actions/labeler 7 | 8 | name: Labeler 9 | on: [pull_request] 10 | 11 | jobs: 12 | label: 13 | 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | pull-requests: write 18 | 19 | steps: 20 | - uses: actions/labeler@v2 21 | with: 22 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **.exe 2 | 3 | debug 4 | release 5 | 6 | Makefile.Debug 7 | Makefile.Release 8 | lib 9 | 10 | gcoder.pro.user* 11 | 12 | Makefile 13 | 14 | .moc 15 | .ui 16 | .obj 17 | .rcc 18 | 19 | **.swp 20 | 21 | *.o 22 | 23 | .qglviewer.xml 24 | 25 | bin 26 | gen 27 | target 28 | 29 | qgcoder 30 | 31 | build-stamp 32 | debian/qgcoder.debhelper.log 33 | debian/qgcoder.substvars 34 | debian/qgcoder/ 35 | debian/files 36 | 37 | .qmake.stash 38 | gcoder-build-deps_0.0.0-ubuntu1_all.deb 39 | gcoder-build-deps_0.0.0-ubuntu1_amd64.buildinfo 40 | gcoder-build-deps_0.0.0-ubuntu1_amd64.changes 41 | x 42 | 43 | qgcoder.pro.user 44 | 45 | *.deb 46 | *.buildinfo 47 | *.changes 48 | -------------------------------------------------------------------------------- /settings_dlg.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SETTINGS_DLG_H 3 | #define SETTINGS_DLG_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "ui_settings.h" 12 | 13 | 14 | class SettingsDialog : public QDialog , private Ui_Settings 15 | { 16 | Q_OBJECT 17 | public: 18 | 19 | SettingsDialog(QWidget* parent, QString& homedir); 20 | 21 | void setValues(QString& rs, QString& tbl, QString& gcode); 22 | QString rs274; 23 | QString tooltable; 24 | QString gcodefile; 25 | 26 | private: 27 | void onFileBrowse(int buttonNumber); 28 | QString home_dir; 29 | 30 | private slots: 31 | virtual void onFileBrowse1() {onFileBrowse(1);} 32 | virtual void onFileBrowse2() {onFileBrowse(2);} 33 | virtual void onFileBrowse3() {onFileBrowse(3);} 34 | 35 | virtual void onAccept(); 36 | }; 37 | 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /g2m/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project( g2m ) 2 | cmake_minimum_required( VERSION 2.6 ) 3 | set ( CMAKE_BUILD_TYPE Debug ) 4 | add_definitions ( -Wall ) 5 | 6 | find_package ( Qt4 REQUIRED ) 7 | 8 | include ( ${QT_USE_FILE} ) 9 | include_directories ( 10 | ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} 11 | ${QT_QTCORE_INCLUDE_DIR} ${QT_QTGUI_INCLUDE_DIR} 12 | ) 13 | 14 | set ( g2m_HDRS 15 | g2m.hpp 16 | canonLine.hpp 17 | canonMotionless.hpp 18 | canonMotion.hpp 19 | linearMotion.hpp 20 | helicalMotion.hpp 21 | machineStatus.hpp 22 | nanotimer.hpp 23 | point.hpp 24 | gplayer.hpp 25 | ) 26 | 27 | set ( g2m_SRCS 28 | g2m.cpp 29 | canonLine.cpp 30 | canonMotionless.cpp 31 | canonMotion.cpp 32 | linearMotion.cpp 33 | helicalMotion.cpp 34 | machineStatus.cpp 35 | nanotimer.cpp 36 | ) 37 | 38 | # run qt MOC on these 39 | set( g2m_MOCS 40 | g2m.hpp 41 | gplayer.hpp 42 | ) 43 | 44 | QT4_WRAP_CPP(MOCS ${g2m_MOCS}) 45 | 46 | add_library ( 47 | g2m 48 | SHARED 49 | ${g2m_SRCS} 50 | ${MOCS} 51 | ) 52 | target_link_libraries ( g2m ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ) 53 | 54 | # install the shared library 55 | install( 56 | TARGETS g2m 57 | DESTINATION lib/g2m 58 | ) 59 | 60 | # this installs the c++ include headers 61 | install( 62 | FILES ${g2m_HDRS} 63 | DESTINATION include/g2m 64 | PERMISSIONS OWNER_READ GROUP_READ WORLD_READ 65 | ) 66 | -------------------------------------------------------------------------------- /view.h: -------------------------------------------------------------------------------- 1 | #ifndef VIEW_H 2 | #define VIEW_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "g2m.hpp" 11 | 12 | using namespace qglviewer; 13 | using namespace g2m; 14 | 15 | class View : public QGLViewer 16 | { 17 | Q_OBJECT; 18 | Q_PROPERTY(bool autoZoom READ autoZoom WRITE setAutoZoom RESET unsetAutoZoom); 19 | 20 | public: 21 | View(QWidget *parent = NULL); 22 | ~View(); 23 | 24 | void resetCamView(); 25 | 26 | void updateGLViewer() { 27 | #if QGLVIEWER_VERSION < 0x020700 28 | this->updateGL(); 29 | #else 30 | this->update(); 31 | #endif 32 | }; 33 | 34 | bool autoZoom()const{ return _autoZoom; }; 35 | void setAutoZoom(bool autoZoom){ _autoZoom = autoZoom; update(); }; 36 | void unsetAutoZoom(){ _autoZoom = true; update(); }; 37 | 38 | public slots: 39 | void close(); 40 | 41 | void appendCanonLine(canonLine *l); 42 | void clear(); 43 | 44 | void update(); 45 | 46 | void keyPressEvent(QKeyEvent *e); 47 | 48 | protected: 49 | void init(); 50 | void initializeGL(); 51 | 52 | virtual void draw(); 53 | virtual void fastDraw(); 54 | virtual void postDraw(); 55 | 56 | void drawObjects(bool simplified); 57 | 58 | private: 59 | Vec initialCameraPosition; 60 | Quaternion initialCameraOrientation; 61 | 62 | QMutex mutex; 63 | 64 | QSettings * settings; 65 | 66 | std::vector lines; 67 | 68 | bool dirty; 69 | 70 | double aabb[6]; 71 | 72 | bool _autoZoom; 73 | }; 74 | 75 | #endif // VIEW_H 76 | 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | An interactive G-code editing GUI. 4 | 5 | ## Installation 6 | 7 | * First install [https://github.com/QGCoder/libqgcodeeditor](https://github.com/QGCoder/libqgcodeeditor), a Qt5 designer widget plugin for editing G-code. 8 | 9 | * Next: clone, build and run ```qgcoder``` as follows: 10 | ```bash 11 | gh repo clone QGCoder/qgcoder && qgcoder 12 | qmake && make -j$(nproc) 13 | ./qgcoder 14 | ``` 15 | or build and install a Ubuntu / Debian package as follows: 16 | ```bash 17 | gh repo clone QGCoder/qgcoder && cd qgcoder 18 | mk-build-deps -i -s sudo -t "apt --yes --no-install-recommends" 19 | dpkg-buildpackage -b -rfakeroot -us -uc 20 | sudo dpkg -i ../qgcoder*.deb 21 | sudo apt -f install 22 | ``` 23 | 24 | – Tested with Ubuntu 24.04. - [![CI](https://github.com/QGCoder/qgcoder/actions/workflows/main.yml/badge.svg)](https://github.com/QGCoder/qgcoder/actions/workflows/main.yml) 25 | 26 | ## Overview 27 | 28 | When started first, you have to provide ```qgcoder``` three filenames, as seen in the following screenshot: 29 | 30 | 31 | 32 | 33 | A short [YouTube video](https://www.youtube.com/watch?v=9D3hMXP5-QM) shows, how you can interact inside ```qgcoder```. 34 | 35 | ## Author 36 | 37 | * **Jakob Flierl** - [koppi](https://github.com/koppi) 38 | 39 | ## Contributors 40 | 41 | * **ArcEye** - [ArcEye](https://github.com/ArcEye) 42 | * **Mark Pictor** 43 | * **Kazuyasu Hamada** 44 | * **Anders Wallin** - [aewallin](https://github.com/aewallin) 45 | -------------------------------------------------------------------------------- /settings_dlg.cpp: -------------------------------------------------------------------------------- 1 | #include "settings_dlg.h" 2 | #include 3 | 4 | SettingsDialog::SettingsDialog(QWidget *parent, QString& homedir) 5 | :QDialog(parent) 6 | { 7 | QString str; 8 | 9 | // build the dialog from ui 10 | setupUi(this); 11 | home_dir = homedir; 12 | 13 | } 14 | 15 | void SettingsDialog::setValues(QString& rs, QString& tbl, QString& gcode) 16 | { 17 | le_path1->setText(rs274 = rs); 18 | le_path2->setText(tooltable = tbl); 19 | le_path3->setText(gcodefile = gcode); 20 | } 21 | 22 | void SettingsDialog::onFileBrowse(int buttonNumber) 23 | { 24 | QString pathStr; 25 | QString filename; 26 | QDir dir; 27 | 28 | if( buttonNumber == 1) 29 | { 30 | if(rs274.isEmpty()) 31 | pathStr = "/usr/bin"; 32 | else 33 | pathStr = dir.absoluteFilePath(rs274); 34 | } 35 | else if(buttonNumber == 2) 36 | { 37 | if(tooltable.isEmpty()) 38 | pathStr = home_dir + "machinekit/configs"; 39 | else 40 | pathStr = dir.absoluteFilePath(tooltable); 41 | } 42 | else 43 | { 44 | if(gcodefile.isEmpty()) 45 | pathStr = "/tmp"; 46 | else 47 | pathStr = dir.absoluteFilePath(gcodefile); 48 | } 49 | 50 | filename = QFileDialog::getOpenFileName(this, tr("Settings Paths"), pathStr, tr("All files (*)")); 51 | if(filename.length()) 52 | { 53 | if( buttonNumber == 1) 54 | le_path1->setText(filename); 55 | else if(buttonNumber == 2) 56 | le_path2->setText(filename); 57 | else 58 | le_path3->setText(filename); 59 | } 60 | } 61 | 62 | void SettingsDialog::onAccept() 63 | { 64 | rs274 = le_path1->text(); 65 | tooltable = le_path2->text(); 66 | gcodefile = le_path3->text(); 67 | 68 | QDialog::accept(); 69 | } 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /mainwin.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | #include "view.h" 11 | #include "g2m.hpp" 12 | #include "settings_dlg.h" 13 | 14 | namespace Ui { 15 | class MainWindow; 16 | } 17 | 18 | class MainWindow : public QMainWindow 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit MainWindow(QWidget *parent = 0, bool fileMode = false, QString fileName = ""); 24 | ~MainWindow(); 25 | 26 | void parseGcode(); 27 | void parseCommand(); 28 | 29 | signals: 30 | void setRS274(QString s); 31 | void setToolTable(QString s); 32 | void setGcodeFile(QString f); 33 | void interpret(); 34 | 35 | public slots: 36 | // load the last command string from the settings 37 | // used during startup 38 | virtual void loadSettingsCommand(); 39 | virtual void loadGCodeFile(); 40 | 41 | virtual void changedGcode(); 42 | virtual void changedCommand(); 43 | 44 | virtual void onOpenFile(); 45 | virtual void onSaveAs(); 46 | virtual int onSettings(); 47 | 48 | virtual void appendCanonLine(g2m::canonLine*); 49 | 50 | virtual void toggleAutoZoom(); 51 | virtual void showFullScreen(); 52 | virtual void zoomIn(); 53 | virtual void zoomOut(); 54 | 55 | virtual void helpIssues(); 56 | virtual void helpChat(); 57 | 58 | protected: 59 | void loadSettings(); 60 | void saveSettings(); 61 | 62 | private: // functions 63 | int openInViewer(QString filename); 64 | void openInBrowser(QString filename); 65 | int saveInBrowser(QString& filename); 66 | 67 | void closeEvent(QCloseEvent *) Q_DECL_OVERRIDE; 68 | 69 | void setStyle(); 70 | 71 | private: // data 72 | QString home_dir, openFile; 73 | bool bFileMode = false; 74 | 75 | QString rs274; 76 | QString tooltable; 77 | QString gcodefile; 78 | 79 | Ui::MainWindow *ui; 80 | 81 | View *view; 82 | 83 | g2m::g2m *g2m; 84 | 85 | int fontSize; 86 | 87 | QSettings *settings; 88 | }; 89 | 90 | #endif // MAINWINDOW_H 91 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "mainwin.h" 4 | 5 | #define APP_VERSION QString("2016") 6 | #define APP_NAME QString("gcoder") 7 | #define APP_NAME_FULL QString("GCoder") 8 | #define APP_ORGANIZATION QString("gcoder.koppi.github.com") 9 | 10 | int main(int argv, char **args) 11 | { 12 | QSharedPointer app; 13 | 14 | // workaround for https://forum.qt.io/topic/53298/qcommandlineparser-to-select-gui-or-non-gui-mode 15 | 16 | // On Linux: enable printing of version and help without DISPLAY variable set 17 | 18 | bool runCore = false; 19 | for (int i = 0; i < argv; i++) { 20 | if (QString(args[i]) == "-h" || 21 | QString(args[i]) == "--help" || 22 | QString(args[i]) == "-v" || 23 | QString(args[i]) == "--version" ) { 24 | runCore = true; 25 | break; 26 | } 27 | } 28 | 29 | if (runCore) { 30 | app = QSharedPointer(new QCoreApplication(argv, args)); 31 | } else { 32 | app = QSharedPointer(new QApplication(argv, args)); 33 | } 34 | 35 | // end workaround 36 | 37 | setlocale(LC_NUMERIC,"C"); 38 | 39 | //XXX app->setStyleSheet("QPlainTextEdit{ selection-background-color: darkblue } QWidget { font-size: 12pt; font-family: \"Courier\"; background-color: #00003B; color: #FFA700; font: bold }"); 40 | 41 | QCoreApplication::setOrganizationName(APP_ORGANIZATION); 42 | QCoreApplication::setApplicationName(APP_NAME); 43 | QCoreApplication::setApplicationVersion(APP_VERSION); 44 | QCommandLineParser parser; 45 | 46 | parser.setApplicationDescription(QCoreApplication::applicationName()); 47 | parser.addHelpOption(); 48 | parser.addVersionOption(); 49 | parser.addPositionalArgument("file", "The G-code file to open."); 50 | 51 | parser.process(*app); 52 | 53 | MainWindow *win; 54 | 55 | if (!parser.positionalArguments().isEmpty()) { 56 | win = new MainWindow(NULL, !parser.positionalArguments().isEmpty(), parser.positionalArguments().first()); 57 | } else { 58 | win = new MainWindow(); 59 | } 60 | 61 | win->show(); 62 | 63 | return app->exec(); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /g2m/nanotimer.hpp: -------------------------------------------------------------------------------- 1 | // adapted from http://allmybrain.com/2008/06/10/timing-cc-code-on-linux/ 2 | /************************************************************************** 3 | * Copyright (C) 2010 by Mark Pictor * 4 | * mpictor@gmail.com * 5 | * * 6 | * This program is free software; you can redistribute it and/or modify * 7 | * it under the terms of the GNU General Public License as published by * 8 | * the Free Software Foundation; either version 2 of the License, or * 9 | * (at your option) any later version. * 10 | * * 11 | * This program is distributed in the hope that it will be useful, * 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | * GNU General Public License for more details. * 15 | * * 16 | * You should have received a copy of the GNU General Public License * 17 | * along with this program; if not, write to the * 18 | * Free Software Foundation, Inc., * 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 20 | **************************************************************************/ 21 | #ifndef NANOTIMER_HH 22 | #define NANOTIMER_HH 23 | 24 | #include 25 | #include 26 | 27 | namespace g2m { 28 | 29 | /// a timing class for benchmarking and debugging. 30 | class nanotimer { 31 | private: 32 | /// time-stamp when start() was called 33 | timespec begin; 34 | public: 35 | nanotimer() {} 36 | /// start the timer() 37 | void start(); 38 | /// return nanoseconds since start() 39 | long getElapsed(); 40 | /// return seconds since start() 41 | double getElapsedS(); 42 | /// return a QString with seconds, milliseconds, microseconds 43 | static QString humanreadable(double s); 44 | }; 45 | 46 | } 47 | #endif //NANOTIMER_HH 48 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | # Uncomment this to turn on verbose mode. 4 | export DH_VERBOSE=1 5 | 6 | CXXFLAGS = -Wall -g 7 | 8 | ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) 9 | CXXFLAGS += -O0 10 | else 11 | CXXFLAGS += -O2 12 | endif 13 | ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) 14 | INSTALL_PROGRAM += -s 15 | endif 16 | 17 | QMAKE_AFTER = -after \ 18 | 'QMAKE_CXXFLAGS_RELEASE = $(CXXFLAGS)' \ 19 | 'QMAKE_POST_LINK ~= s/strip/:' 20 | 21 | build: build-stamp 22 | build-stamp: $(QUILT_STAMPFN) 23 | dh_testdir 24 | 25 | qmake $(QMAKE_AFTER) qgcoder.pro && $(MAKE) 26 | 27 | touch $@ 28 | 29 | clean: 30 | dh_testdir 31 | dh_testroot 32 | rm -f build-stamp 33 | [ ! -f Makefile ] || $(MAKE) clean 34 | dh_clean 35 | 36 | install: build 37 | dh_testdir 38 | dh_testroot 39 | dh_clean -k 40 | dh_installdirs 41 | 42 | $(MAKE) INSTALL_ROOT=$(CURDIR)/debian/qgcoder install 43 | 44 | # install -d -v -m 0755 $(CURDIR)/debian/qgcoder/usr/share/applications 45 | # chmod 664 $(CURDIR)/debian/qgcoder/usr/share/applications/qgcoder.desktop 46 | 47 | # install -d -v -m 0755 $(CURDIR)/debian/qgcoder/usr/share/icons/hicolor/scalable/apps 48 | # chmod 664 $(CURDIR)/debian/qgcoder/usr/share/icons/hicolor/scalable/apps/qgcoder.svg 49 | 50 | install -d -v -m 0755 $(CURDIR)/debian/qgcoder/usr/bin 51 | install -m 755 -p qgcoder $(CURDIR)/debian/qgcoder/usr/bin/qgcoder 52 | 53 | # Build architecture-independent files here. 54 | binary-indep: build install 55 | dh_testdir 56 | dh_testroot 57 | # dh_installchangelogs ChangeLog 58 | dh_installdocs -i 59 | dh_install -i 60 | dh_installman -i 61 | dh_compress -i --exclude=.cpp --exclude=.pri 62 | dh_fixperms 63 | dh_installdeb 64 | dh_gencontrol 65 | dh_md5sums 66 | dh_builddeb 67 | 68 | # Build architecture-dependent files here. 69 | binary-arch: build install 70 | dh_testdir 71 | dh_testroot 72 | # dh_installchangelogs ChangeLog 73 | dh_installdocs -s README.md 74 | dh_install -s --sourcedir=$(CURDIR)/debian/tmp 75 | # dh_installman qgcoder.1 76 | dh_installmime 77 | dh_installmenu 78 | dh_link 79 | dh_strip 80 | dh_compress -s 81 | dh_fixperms 82 | dh_makeshlibs 83 | dh_installdeb 84 | dh_shlibdeps 85 | dh_gencontrol 86 | dh_md5sums 87 | dh_builddeb 88 | 89 | binary: binary-indep binary-arch 90 | .PHONY: build clean binary-indep binary-arch binary install configure 91 | -------------------------------------------------------------------------------- /g2m/point.hpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #ifndef POINT_HPP 6 | #define POINT_HPP 7 | 8 | //#include 9 | 10 | namespace g2m { 11 | 12 | /// a point in 3D space 13 | struct Point { 14 | /// Create point at (0,0,0) 15 | Point():x(0),y(0),z(0) {} 16 | /// Create point at given coordinates 17 | Point(double a, double b, double c):x(a),y(b),z(c) {} 18 | /// copy-constructor 19 | Point(const Point& other): x(other.x),y(other.y),z(other.z) {} 20 | /// distance to given other Point 21 | double Distance( Point other ) { 22 | return sqrt( (other.x-x)*(other.x-x) + (other.y-y)*(other.y-y) + (other.z-z)*(other.z-z) ); 23 | } 24 | /// string representation 25 | std::string str() const { 26 | std::ostringstream o; 27 | o << "(" << x << ", " << y << ", " << z << ")"; 28 | return o.str(); 29 | } 30 | /// multiply Point with scalar 31 | Point& operator*=(const double &a) { 32 | x*=a; 33 | y*=a; 34 | z*=a; 35 | return *this; 36 | } 37 | /// multiply Point with scalar 38 | Point operator*(const double &a) const { 39 | return Point(*this) *= a; 40 | } 41 | /// vector addition 42 | Point& operator+=( const Point& p) { 43 | x+=p.x; 44 | y+=p.y; 45 | z+=p.z; 46 | return *this; 47 | } 48 | 49 | /// vector addition 50 | const Point operator+( const Point &p) const { 51 | return Point(*this) += p; 52 | } 53 | 54 | /// vector subtraction 55 | Point& operator-=( const Point& p) { 56 | x-=p.x; 57 | y-=p.y; 58 | z-=p.z; 59 | return *this; 60 | } 61 | /// vector subtraction 62 | const Point operator-( const Point &p) const { 63 | return Point(*this) -= p; 64 | } 65 | 66 | // DATA 67 | /// x-coordinate 68 | double x; 69 | /// y-coordinate 70 | double y; 71 | /// z-coordinate 72 | double z; 73 | }; 74 | 75 | /// a pose is a complete 6-dimensional description of the position and rotation 76 | /// of an object in 3D space (e.g. a tool). This includes the position of the origin 77 | /// of the tool (loc) and the direction of the tool axis (dir) 78 | struct Pose { 79 | Pose() {} 80 | /// Create Pose with given location and direction 81 | /// \param a location 82 | /// \param b direction 83 | Pose( Point a, Point b ) { 84 | loc = a; 85 | dir = b; 86 | } 87 | /// location 88 | Point loc; 89 | /// direction -> angle 90 | Point dir; 91 | }; 92 | 93 | } // end namespace 94 | #endif 95 | -------------------------------------------------------------------------------- /g2m/canonMotion.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * modified by Kazuyasu Hamada 2015, k-hamada@gifu-u.ac.jp * 5 | * * 6 | * This program is free software; you can redistribute it and/or modify * 7 | * it under the terms of the GNU General Public License as published by * 8 | * the Free Software Foundation; either version 2 of the License, or * 9 | * (at your option) any later version. * 10 | * * 11 | * This program is distributed in the hope that it will be useful, * 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | * GNU General Public License for more details. * 15 | * * 16 | * You should have received a copy of the GNU General Public License * 17 | * along with this program; if not, write to the * 18 | * Free Software Foundation, Inc., * 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 20 | ***************************************************************************/ 21 | 22 | #include 23 | #include 24 | 25 | #include "canonMotion.hpp" 26 | #include "canonLine.hpp" 27 | #include "point.hpp" 28 | 29 | namespace g2m { 30 | 31 | canonMotion::canonMotion(std::string canonL, machineStatus prevStatus): canonLine(canonL,prevStatus) { 32 | 33 | } 34 | 35 | /// for STRAIGHT_* and ARC_FEED, first 3 are always xyz and last 3 always abc 36 | Pose canonMotion::getPoseFromCmd() { 37 | double x,y,z; 38 | 39 | //need 3,4,5,and -3,-2,-1 40 | x = tok2d(3); 41 | y = tok2d(4); 42 | z = tok2d(5); 43 | Point p(x,y,z); 44 | 45 | /* FIXME */ 46 | double a,b,c; 47 | int s = canonTokens.size(); //a,b,c are last 3 numbers 48 | c = tok2d(s-1); 49 | b = tok2d(s-2); 50 | a = tok2d(s-3); 51 | // assert (a+b+c < 3.0 * Precision::Confusion()); 52 | //now how to convert those angles to a unit vector (i.e. gp_Dir)? 53 | 54 | //for now we take the easy way out 55 | 56 | // Point d(0,0,1); //vertical, NO ROTATION! 57 | Point d(a,b,c); 58 | return Pose(p,d); 59 | } 60 | 61 | } // end namespace 62 | -------------------------------------------------------------------------------- /g2m/linearMotion.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * modified by Kazuyasu Hamada 2015, k-hamada@gifu-u.ac.jp * 5 | * * 6 | * This program is free software; you can redistribute it and/or modify * 7 | * it under the terms of the GNU General Public License as published by * 8 | * the Free Software Foundation; either version 2 of the License, or * 9 | * (at your option) any later version. * 10 | * * 11 | * This program is distributed in the hope that it will be useful, * 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | * GNU General Public License for more details. * 15 | * * 16 | * You should have received a copy of the GNU General Public License * 17 | * along with this program; if not, write to the * 18 | * Free Software Foundation, Inc., * 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 20 | ***************************************************************************/ 21 | #ifndef LINEARMOTION_HH 22 | #define LINEARMOTION_HH 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "canonMotion.hpp" 30 | #include "machineStatus.hpp" 31 | 32 | namespace g2m { 33 | 34 | /** 35 | \class linearMotion 36 | \brief For the canonical commands STRAIGHT_FEED and STRAIGHT_TRAVERSE. 37 | This class inherits from canonMotion. 38 | */ 39 | 40 | class linearMotion: protected canonMotion { 41 | friend canonLine* canonLine::canonLineFactory(std::string l, machineStatus s); 42 | public: 43 | /// create linear motion 44 | linearMotion(std::string canonL, machineStatus prevStatus); 45 | MOTION_TYPE getMotionType(); 46 | //std::vector points(); // points sampled along the motion 47 | /// return interpolated point along this move, a distance s from the start of the move 48 | Point point(double s); 49 | // std::cout << " linear feed: " << start.str() << " to " << end.str() << "\n"; 50 | Point end; 51 | #ifdef MULTI_AXIS 52 | Point angle(double s); 53 | #endif 54 | /// return length of this move 55 | double length(); 56 | }; 57 | 58 | } // end namespace 59 | 60 | #endif //LINEARMOTION_HH 61 | -------------------------------------------------------------------------------- /g2m/canonMotionless.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 | ***************************************************************************/ 20 | #ifndef CANONMOTIONLESS_HH 21 | #define CANONMOTIONLESS_HH 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include "canonLine.hpp" 30 | #include "machineStatus.hpp" 31 | 32 | namespace g2m { 33 | 34 | /** 35 | \class canonMotionless 36 | \brief A canonical command that (generally) does not cause or alter axis motion 37 | This class is for anything other than STRAIGHT_FEED, STRAIGHT_TRAVERSE, and ARC_FEED - 38 | * including changes in feedrate, spindle speed, tool, coolant, ending the program, etc 39 | */ 40 | 41 | class canonMotionless: protected canonLine { 42 | friend canonLine* canonLine::canonLineFactory(std::string l, machineStatus s); 43 | public: 44 | /// create motionless canon-line 45 | canonMotionless(std::string canonL, machineStatus prevStatus); 46 | /// return false 47 | bool isMotion() {return false;}; 48 | /// return type of motion 49 | MOTION_TYPE getMotionType() {return MOTIONLESS;}; 50 | ///returns true if this command is a valid terminator for the NC file (i.e. 51 | bool isNCend() {return ncEnd;}; 52 | protected: 53 | /// flag(?) 54 | bool match; 55 | /// flag indicating that this canon-line is handled 56 | bool handled; 57 | /// flag indicating end of g-code program 58 | bool ncEnd; 59 | }; 60 | 61 | } // end namespace 62 | 63 | #endif //CANONMOTIONLESS_HH 64 | -------------------------------------------------------------------------------- /qgcoder.pro: -------------------------------------------------------------------------------- 1 | TARGET = qgcoder 2 | 3 | TEMPLATE = app 4 | 5 | CONFIG += link_pkgconfig 6 | 7 | QMAKE_CXXFLAGS_RELEASE += -O2 8 | QMAKE_CXXFLAGS_DEBUG += -O0 9 | 10 | QMAKE_CXXFLAGS += -Wno-deprecated-copy 11 | 12 | MOC_DIR = .moc 13 | #OBJECTS_DIR = .obj 14 | UI_DIR = .ui 15 | RCC_DIR = .rcc 16 | 17 | CONFIG += qgcodeeditor 18 | CONFIG += qglviewer 19 | 20 | INCLUDEPATH += g2m 21 | INCLUDEPATH += /usr/include/QGCodeEditor 22 | 23 | HEADERS = \ 24 | mainwin.h \ 25 | view.h \ 26 | g2m/canonLine.hpp g2m/canonMotionless.hpp g2m/gplayer.hpp g2m/linearMotion.hpp g2m/nanotimer.hpp \ 27 | g2m/canonMotion.hpp g2m/g2m.hpp g2m/helicalMotion.hpp g2m/machineStatus.hpp g2m/point.hpp \ 28 | lex_analyzer.hpp settings_dlg.h 29 | 30 | 31 | SOURCES = \ 32 | main.cpp \ 33 | mainwin.cpp \ 34 | view.cpp \ 35 | g2m/canonLine.cpp g2m/canonMotionless.cpp g2m/helicalMotion.cpp g2m/machineStatus.cpp \ 36 | g2m/canonMotion.cpp g2m/g2m.cpp g2m/linearMotion.cpp g2m/nanotimer.cpp \ 37 | lex_analyzer.cpp settings_dlg.cpp 38 | 39 | target.path = /usr/bin 40 | 41 | INSTALLS += target 42 | 43 | FORMS += \ 44 | mainwin.ui settings.ui 45 | 46 | link_pkgconfig { 47 | # message("Using pkg-config "$$system(pkg-config --version)".") 48 | 49 | LSB_RELEASE_ID = $$system(. /etc/os-release; echo "$NAME") 50 | LSB_RELEASE_REL = $$system(. /etc/os-release; echo "$VERSION_ID") 51 | 52 | message(This is $$LSB_RELEASE_ID $$LSB_RELEASE_REL) 53 | 54 | contains(LSB_RELEASE_ID, Ubuntu): { 55 | contains(LSB_RELEASE_REL, 21.04) : { 56 | LIBS += -lQGLViewer-qt5 -lGLEW -lGLU -lGL -lglut 57 | } 58 | contains(LSB_RELEASE_REL, 22.04) : { 59 | LIBS += -lQGLViewer-qt5 -lGLEW -lGLU -lGL -lglut 60 | } 61 | contains(LSB_RELEASE_REL, 22.10) : { 62 | LIBS += -lQGLViewer-qt5 -lGLEW -lGLU -lGL -lglut 63 | } 64 | contains(LSB_RELEASE_REL, 24.04) : { 65 | LIBS += -lQGLViewer-qt5 -lGLEW -lGLU -lGL -lglut 66 | } 67 | } else { 68 | LIBS += -lQGLViewer-qt5 -lGLEW -lGLU -lGL -lglut 69 | } 70 | } 71 | 72 | CONFIG *= debug_and_release 73 | CONFIG *= qt opengl 74 | CONFIG += warn_on 75 | CONFIG += thread 76 | 77 | QT *= opengl xml gui core 78 | 79 | OTHER_FILES += README.md 80 | 81 | unix { 82 | qgcoder-deskop.path = /usr/share/applications 83 | qgcoder-deskop.files = qgcoder.desktop 84 | qgcoder-icon.path = /usr/share/icons/hicolor/256x256/apps 85 | qgcoder-icon.files = icons/qgcoder.png 86 | 87 | INSTALLS += qgcoder-deskop 88 | INSTALLS += qgcoder-icon 89 | } 90 | 91 | DIRS_DC = object_script.* .ui .moc .rcc .obj *.pro.user $$TARGET 92 | 93 | unix:QMAKE_DISTCLEAN += -r $$DIRS_DC 94 | win32:QMAKE_DISTCLEAN += /s /f /q $$DIRS_DC && rd /s /q $$DIRS_DC 95 | 96 | DISTFILES += \ 97 | icons/qgcoder.png 98 | 99 | RESOURCES += \ 100 | res.qrc 101 | 102 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ main ] 4 | pull_request: 5 | branches: [ main ] 6 | 7 | jobs: 8 | build: 9 | name: ${{ matrix.target }} 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | target: 14 | - ubuntu-latest 15 | runs-on: ${{ matrix.target }} 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Install devscripts 20 | run: | 21 | sudo apt update -qq 22 | sudo apt full-upgrade -y 23 | sudo DEBIAN_FRONTEND=noninteractive apt -qq -y install devscripts equivs lintian 24 | mk-build-deps -i -s sudo -t "apt --yes --no-install-recommends" 25 | 26 | - name: Build source package 27 | env: 28 | DEBFULLNAME: "Github Actions" 29 | DEBEMAIL: "jakob.flierl@gmail.com" 30 | run: | 31 | export TARGET=$(. /etc/lsb-release && echo $DISTRIB_CODENAME) 32 | git fetch --unshallow 33 | git fetch --tags 34 | VERSION="$(git tag -l | tail -n1 | sed -e "s/^v//" -e "s/-/+git/")" 35 | dch --create \ 36 | --distribution ${TARGET} \ 37 | --package qgcoder \ 38 | --newversion ${VERSION}~${TARGET}1 \ 39 | "Automatic build from Github" 40 | debuild -S -sa -us -uc -d 41 | 42 | - name: Build and install libqgcodeeditor 43 | env: 44 | DEBFULLNAME: "Jakob Flierl" 45 | DEBEMAIL: "jakob.flierl@gmail.com" 46 | run: | 47 | git clone https://github.com/QGCoder/libqgcodeeditor 48 | pushd libqgcodeeditor 49 | export TARGET=$(. /etc/lsb-release && echo $DISTRIB_CODENAME) 50 | VERSION="$(git tag -l | tail -n1 | sed -e "s/^v//" -e "s/-/+git/")" 51 | dch --create \ 52 | --distribution ${TARGET} \ 53 | --package libqgcodeeditor \ 54 | --newversion ${VERSION}~${TARGET}1 \ 55 | "Automatic build from Github" 56 | debuild -S -sa -us -uc -d 57 | mk-build-deps -i -s sudo -t "apt --yes --no-install-recommends" 58 | dpkg-buildpackage -b -rfakeroot -us -uc 59 | sudo dpkg -i ../libqgcodeeditor*deb 60 | sudo apt -y -f install 61 | qmake && make && sudo make install # fixme 62 | popd 63 | 64 | - name: Build binary package 65 | run: dpkg-buildpackage -b -rfakeroot -us -uc 66 | 67 | - name: Install binary package 68 | run: sudo dpkg -i ../qgcoder*deb 69 | 70 | - name: Install binary package dependencies 71 | run: sudo apt -f install 72 | 73 | - name: Run lintian 74 | run: lintian ../qgcoder*deb | lintian-info 75 | 76 | - name: Upload artifacts 77 | uses: actions/upload-artifact@v4 78 | with: 79 | name: ${{ matrix.target }} 80 | if-no-files-found: error 81 | path: | 82 | *.buildinfo 83 | *.changes 84 | *.dsc 85 | *.tar.* 86 | *.deb 87 | ~/**/*/*.buildinfo 88 | ~/**/*/*.changes 89 | ~/**/*/*.dsc 90 | ~/**/*/*.tar.* 91 | ~/**/*/*.deb 92 | 93 | 94 | -------------------------------------------------------------------------------- /g2m/nanotimer.cpp: -------------------------------------------------------------------------------- 1 | // adapted from http://allmybrain.com/2008/06/10/timing-cc-code-on-linux/ 2 | 3 | /************************************************************************** 4 | * Copyright (C) 2010 by Mark Pictor * 5 | * mpictor@gmail.com * 6 | * * 7 | * This program is free software; you can redistribute it and/or modify * 8 | * it under the terms of the GNU General Public License as published by * 9 | * the Free Software Foundation; either version 2 of the License, or * 10 | * (at your option) any later version. * 11 | * * 12 | * This program is distributed in the hope that it will be useful, * 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | * GNU General Public License for more details. * 16 | * * 17 | * You should have received a copy of the GNU General Public License * 18 | * along with this program; if not, write to the * 19 | * Free Software Foundation, Inc., * 20 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 21 | **************************************************************************/ 22 | 23 | #include 24 | #include 25 | 26 | #include "nanotimer.hpp" 27 | 28 | //clock_gettime requires librt 29 | //CLOCK_MONOTONIC_RAW requires kernel 2.6.28 and libc6 >> 2.11.2 30 | //with earlier kernels use CLOCK_MONOTONIC or CLOCK_REALTIME 31 | 32 | namespace g2m { 33 | 34 | #ifndef CLOCK_MONOTONIC_RAW 35 | #define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC 36 | #endif 37 | 38 | 39 | 40 | void nanotimer::start() { 41 | clock_gettime(CLOCK_MONOTONIC_RAW, &begin); 42 | } 43 | 44 | long nanotimer::getElapsed(){ 45 | timespec now,delta; 46 | clock_gettime(CLOCK_MONOTONIC_RAW, &now); 47 | delta.tv_sec = now.tv_sec - begin.tv_sec; 48 | delta.tv_nsec = now.tv_nsec - begin.tv_nsec; 49 | return delta.tv_sec*1000000000 + delta.tv_nsec; 50 | } 51 | 52 | double nanotimer::getElapsedS(){ 53 | timespec now,delta; 54 | clock_gettime(CLOCK_MONOTONIC_RAW, &now); 55 | delta.tv_sec = now.tv_sec - begin.tv_sec; 56 | delta.tv_nsec = now.tv_nsec - begin.tv_nsec; 57 | return delta.tv_sec + delta.tv_nsec/1000000000.0; 58 | } 59 | 60 | QString nanotimer::humanreadable(double s) { 61 | QString out; 62 | if (s > 60) { 63 | int m; 64 | m = s/60; 65 | s = s-(double)(m*60); 66 | out = QString::number(m) + QString("m, "); 67 | } 68 | if (s > .5) { 69 | out += QString::number(s); 70 | out += QString(" s"); 71 | } else if (s> 0.0005) { 72 | out = QString::number(s*1000); 73 | out += QString(" ms"); 74 | } else { 75 | out = QString::number(s*1000000); 76 | out += QString(" us"); 77 | } 78 | return out; 79 | } 80 | 81 | 82 | } // end namespace 83 | -------------------------------------------------------------------------------- /lex_analyzer.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * modified by Kazuyasu Hamada 2015, k-hamada@gifu-u.ac.jp * 5 | * * 6 | * This program is free software; you can redistribute it and/or modify * 7 | * it under the terms of the GNU General Public License as published by * 8 | * the Free Software Foundation; either version 2 of the License, or * 9 | * (at your option) any later version. * 10 | * * 11 | * This program is distributed in the hope that it will be useful, * 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | * GNU General Public License for more details. * 15 | * * 16 | * You should have received a copy of the GNU General Public License * 17 | * along with this program; if not, write to the * 18 | * Free Software Foundation, Inc., * 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 20 | ***************************************************************************/ 21 | #ifndef LEX_ANALIZER_HH 22 | #define LEX_ANALIZER_HH 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace lex_analyzer { 33 | /** 34 | \class LexAnalyzer 35 | \brief A canonLine object represents one canonical command. 36 | Each gcode line produces one or more canonical commands. It can either be a 37 | * motion command (one of LINEAR_TRAVERSE LINEAR_FEED ARC_FEED), or a motionless 38 | * command (anything else) 39 | You cannot create objects of this class - instead, create an object of a class 40 | * that inherits from this class via canonLineFactory() 41 | */ 42 | class LexAnalyzer { 43 | 44 | public: 45 | LexAnalyzer(std::string line): myLine(line) { 46 | tokenize(myLine, tokens); 47 | } 48 | /// return the line as a string 49 | const std::string getLine() { return myLine; }; 50 | /// return the n:th token 51 | std::string getToken(unsigned int n); 52 | 53 | // keyword matching 54 | bool wordMatch(std::string word, unsigned int n = 0); 55 | // get double number 56 | double token2d(unsigned int n); 57 | // get integer number 58 | int token2i(unsigned int n, unsigned int offset = 0); 59 | 60 | protected: 61 | // tokenizer 62 | void tokenize(std::string str, std::vector& tokenV, const std::string& delimiters = "(), \t"); 63 | 64 | // DATA 65 | /// the line as a string 66 | std::string myLine; 67 | /// the tokens in this line, set after tokenizing myLine 68 | std::vector tokens; 69 | }; 70 | 71 | } // end namespace 72 | 73 | #endif //LEX_ANALIZER_HH 74 | -------------------------------------------------------------------------------- /g2m/canonMotion.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * modified by Kazuyasu Hamada 2015, k-hamada@gifu-u.ac.jp * 5 | * * 6 | * This program is free software; you can redistribute it and/or modify * 7 | * it under the terms of the GNU General Public License as published by * 8 | * the Free Software Foundation; either version 2 of the License, or * 9 | * (at your option) any later version. * 10 | * * 11 | * This program is distributed in the hope that it will be useful, * 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | * GNU General Public License for more details. * 15 | * * 16 | * You should have received a copy of the GNU General Public License * 17 | * along with this program; if not, write to the * 18 | * Free Software Foundation, Inc., * 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 20 | ***************************************************************************/ 21 | #ifndef CANONMOTION_HH 22 | #define CANONMOTION_HH 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "machineStatus.hpp" 30 | #include "canonLine.hpp" 31 | 32 | namespace g2m { 33 | 34 | /** 35 | \class canonMotion 36 | \brief This class is for the canonical commands STRAIGHT_TRAVERSE, STRAIGHT_FEED, and ARC_FEED. 37 | canonMotion is an ABC. Its children should only be instantiated via 38 | * canonLine::canonLineFactory(), which creates 39 | * linearMotion objects for STRAIGHT_TRAVERSE and STRAIGHT_FEED commands, 40 | * and helicalMotion objects for ARC_FEED commands. 41 | 42 | Note, you may find variations in the terminology I use - I misremembered some of 43 | * the canonical commands issued by the interpreter. For example, I thought it 44 | * issued LINEAR_FEED but it's actually STRAIGHT_FEED. So the class 45 | * linearMotion could have been named straightMotion. 46 | 47 | Also, rapid and traverse are used interchangeably, at least in my comments... 48 | */ 49 | 50 | class canonMotion: protected canonLine { 51 | 52 | public: 53 | /// return type of motion 54 | virtual MOTION_TYPE getMotionType() {return NOT_DEFINED;} 55 | /// return true 56 | bool isMotion() {return true;}; 57 | /// return start 58 | Point getStart() const {return start;} 59 | /// return end 60 | Point getEnd() const {return end;} 61 | 62 | protected: 63 | /// create canonMotion 64 | canonMotion(std::string canonL, machineStatus prevStatus); 65 | Pose getPoseFromCmd(); 66 | /// start of this move 67 | Point start; 68 | /// end of this move 69 | Point end; 70 | #ifdef MULTI_AXIS 71 | /// the direction in which the current move starts 72 | Point startDir; 73 | /// the direction in which the current move ends 74 | Point endDir; 75 | #endif 76 | }; 77 | 78 | } // end namespace 79 | 80 | #endif //CANONMOTION_HH 81 | -------------------------------------------------------------------------------- /g2m/helicalMotion.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * modified by Kazuyasu Hamada 2015, k-hamada@gifu-u.ac.jp * 5 | * * 6 | * This program is free software; you can redistribute it and/or modify * 7 | * it under the terms of the GNU General Public License as published by * 8 | * the Free Software Foundation; either version 2 of the License, or * 9 | * (at your option) any later version. * 10 | * * 11 | * This program is distributed in the hope that it will be useful, * 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | * GNU General Public License for more details. * 15 | * * 16 | * You should have received a copy of the GNU General Public License * 17 | * along with this program; if not, write to the * 18 | * Free Software Foundation, Inc., * 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 20 | ***************************************************************************/ 21 | 22 | #ifndef HELICALMOTION_HH 23 | #define HELICALMOTION_HH 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "point.hpp" 31 | #include "canonMotion.hpp" 32 | #include "canonLine.hpp" 33 | #include "machineStatus.hpp" 34 | 35 | namespace g2m { 36 | 37 | /** 38 | \class helicalMotion 39 | \brief For the canonical command ARC_FEED. 40 | This class handles both planar arcs and helical arcs. Inherits from canonMotion. 41 | */ 42 | 43 | class helicalMotion: protected canonMotion { 44 | friend canonLine* canonLine::canonLineFactory(std::string l, machineStatus s); 45 | 46 | public: 47 | /// create helical motion 48 | helicalMotion(std::string canonL, machineStatus prevStatus); 49 | MOTION_TYPE getMotionType() {return HELICAL;}; 50 | /// return interpolated point along helix, a distance s from the start 51 | Point point(double s); 52 | #ifdef MULTI_AXIS 53 | Point angle(double s); 54 | #endif 55 | /// return the length of this helix move 56 | double length(); 57 | 58 | private: 59 | void rotate(double &x, double &y, double c, double s); 60 | 61 | // DATA, this corresponds to the "tokens" on the ARC_FEED canon-line 62 | double x1,y1,z1; // endpoint for this move 63 | double a,b,c; // abc axis endpoints 64 | double rot; // rotation for this move 65 | double cx,cy; // center-point for this move 66 | 67 | // these are the parameters calculated from the canon-params x1,y1,z1,a,b,c,rot,cx,cy 68 | unsigned int X, Y, Z; // for shuffling around coordinate-indexes 69 | double d[6]; // 6-axis delta for this move, applies to linear interpolation 70 | double o[6]; // o=origin/start-point 71 | double dtheta; // change in angle for this move 72 | double tx,ty; // vector from start to center 73 | double radius; 74 | }; 75 | 76 | } // end namespace 77 | 78 | #endif //HELICALMOTION_HH 79 | -------------------------------------------------------------------------------- /lex_analyzer.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * modified by Kazuyasu Hamada 2015, k-hamada@gifu-u.ac.jp * 5 | * * 6 | * This program is free software; you can redistribute it and/or modify * 7 | * it under the terms of the GNU General Public License as published by * 8 | * the Free Software Foundation; either version 2 of the License, or * 9 | * (at your option) any later version. * 10 | * * 11 | * This program is distributed in the hope that it will be useful, * 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | * GNU General Public License for more details. * 15 | * * 16 | * You should have received a copy of the GNU General Public License * 17 | * along with this program; if not, write to the * 18 | * Free Software Foundation, Inc., * 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 20 | ***************************************************************************/ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #include "lex_analyzer.hpp" 30 | 31 | namespace lex_analyzer { 32 | 33 | /** converts tokens[n] to double 34 | \param n this is the token to convert 35 | \returns token n, converted to double 36 | */ 37 | double LexAnalyzer::token2d(uint n) { 38 | if (tokens.size() < n+1 ) 39 | return NAN; 40 | char* end; 41 | double d = strtod( tokens[n].c_str(), &end ); 42 | // assert ( *end == 0 ); 43 | return d; 44 | } 45 | 46 | /** converts tokens[n] to int 47 | \param n this is the token to convert 48 | \param offset skip this many chars at the beginning of the token 49 | \returns token n, converted to integer 50 | */ 51 | int LexAnalyzer::token2i(unsigned int n, unsigned int offset) { 52 | if (tokens.size() < n+1 ) 53 | return INT_MIN; 54 | char * end; 55 | int i = strtol( &tokens[n].c_str()[offset], &end, 10 ); 56 | //assert ( *end != 0 ); 57 | return i; 58 | } 59 | 60 | /// return the n:th token 61 | std::string LexAnalyzer::getToken(unsigned int n) { 62 | if (n < tokens.size()) { 63 | return tokens[n]; 64 | } else { 65 | std::cout << "malformed input line " << myLine << std::endl; 66 | std::string s = ""; 67 | return s; 68 | } 69 | } 70 | 71 | ///return true if the n:th token matches 'm' 72 | bool LexAnalyzer::wordMatch(std::string m, unsigned int n) { 73 | if (tokens.size() < n+1) 74 | return false; 75 | return (m.compare(tokens[n]) == 0); //compare returns zero for a match 76 | } 77 | 78 | //from http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html 79 | /** Splits a string using delimiters. 80 | \param str the string to be split 81 | \param tokenV a vector of strings, each of which is a piece of str 82 | \param delimiters defaults to: both parenthesis, comma, space 83 | \sa tokenize() 84 | */ 85 | void LexAnalyzer::tokenize( std::string str, 86 | std::vector& tokenV, 87 | const std::string& delimiters ) { 88 | // Skip delimiters at beginning. 89 | std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); 90 | // Find first "non-delimiter". 91 | std::string::size_type pos = str.find_first_of(delimiters, lastPos); 92 | 93 | while (std::string::npos != pos || std::string::npos != lastPos) 94 | { 95 | // Found a token, add it to the vector. 96 | tokenV.push_back(str.substr(lastPos, pos - lastPos)); 97 | // Skip delimiters. Note the "not_of" 98 | lastPos = str.find_first_not_of(delimiters, pos); 99 | // Find next "non-delimiter" 100 | pos = str.find_first_of(delimiters, lastPos); 101 | } 102 | } 103 | 104 | } // end namespace 105 | 106 | -------------------------------------------------------------------------------- /g2m/canonLine.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 | ***************************************************************************/ 20 | #ifndef CANONLINE_HH 21 | #define CANONLINE_HH 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "machineStatus.hpp" 32 | #include "point.hpp" 33 | 34 | namespace g2m { 35 | /** 36 | \class canonLine 37 | \brief A canonLine object represents one canonical command. 38 | Each gcode line produces one or more canonical commands. It can either be a 39 | * motion command (one of LINEAR_TRAVERSE LINEAR_FEED ARC_FEED), or a motionless 40 | * command (anything else) 41 | You cannot create objects of this class - instead, create an object of a class 42 | * that inherits from this class via canonLineFactory() 43 | */ 44 | class canonLine { 45 | 46 | public: 47 | /// return the canon-line as a string 48 | const std::string getLine() { return myLine; }; 49 | /// return Pose at start of this move 50 | const Pose getStart() { return status.getStartPose(); }; 51 | /// return the Pose at end of this move 52 | const Pose getEnd() { return status.getEndPose(); }; 53 | int getN(); 54 | ///returns the canon line number 55 | int getLineNum() { return tok2i(0); } 56 | //const std::string getCanonType(); 57 | /// returns the machine's status after execution of this canon line 58 | const machineStatus* getStatus() { return &status; } 59 | /// return type of motion 60 | virtual MOTION_TYPE getMotionType() { return NOT_DEFINED; } //= 0; 61 | /// return false for motion 62 | virtual bool isMotion() { return false; } 63 | /// return true if this is the end of the nc-program 64 | virtual bool isNCend() { return false; } 65 | /// return length of the motion 66 | virtual double length() { assert(0); return -1; } 67 | 68 | #define UNUSED(x) (void)(x) 69 | 70 | /// return interpolated point at position t along the motion 71 | virtual Point point(double t) { UNUSED(t); assert(0); return Point(); } 72 | #ifdef MULTI_AXIS 73 | virtual Point angle(double t) { UNUSED(t); assert(0); return Point(); } 74 | #endif 75 | // produce a canonLine based on string l, and previous machineStatus s 76 | static canonLine* canonLineFactory (std::string l, machineStatus s); 77 | 78 | std::string cantok(unsigned int n); 79 | const std::string getLnum(); 80 | 81 | protected: 82 | // protected ctor, create through factory 83 | canonLine(std::string canonL, machineStatus prevStatus); 84 | double tok2d(unsigned int n); 85 | int tok2i(unsigned int n, unsigned int offset=0); 86 | void tokenize(std::string str, std::vector& tokenV, const std::string& delimiters = "(), "); 87 | const std::string getCanonicalCommand(); 88 | bool cmdMatch(std::string m); 89 | // DATA 90 | /// the canon-line as a string 91 | std::string myLine; 92 | /// the machine's status *after* execution of this canon line 93 | machineStatus status; 94 | /// the tokens in this canonLine, set after tokenizing myLine 95 | std::vector canonTokens; 96 | }; 97 | 98 | } // end namespace 99 | 100 | #endif //CANONLINE_HH 101 | -------------------------------------------------------------------------------- /g2m/linearMotion.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * modified by Kazuyasu Hamada 2015, k-hamada@gifu-u.ac.jp * 5 | * * 6 | * This program is free software; you can redistribute it and/or modify * 7 | * it under the terms of the GNU General Public License as published by * 8 | * the Free Software Foundation; either version 2 of the License, or * 9 | * (at your option) any later version. * 10 | * * 11 | * This program is distributed in the hope that it will be useful, * 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | * GNU General Public License for more details. * 15 | * * 16 | * You should have received a copy of the GNU General Public License * 17 | * along with this program; if not, write to the * 18 | * Free Software Foundation, Inc., * 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 20 | ***************************************************************************/ 21 | #include "calc_tolerance.hpp" 22 | 23 | #include "linearMotion.hpp" 24 | 25 | #include 26 | #include 27 | 28 | #include "machineStatus.hpp" 29 | #include "canonMotion.hpp" 30 | #include "canonLine.hpp" 31 | 32 | namespace g2m { 33 | 34 | linearMotion::linearMotion(std::string canonL, machineStatus prevStatus): canonMotion(canonL,prevStatus) { 35 | status.setMotionType(getMotionType()); 36 | status.setEndPose(getPoseFromCmd()); 37 | //Point start, end; 38 | start = status.getStartPose().loc + status.getOrigin().loc; 39 | end = status.getEndPose().loc + status.getOrigin().loc; 40 | #ifdef MULTI_AXIS 41 | //Point startDir, endDir; 42 | // startDir = (status.getStartPose().dir + status.getOrigin().dir) * (SIGN * M_PI/180.0); 43 | startDir.x = (status.getStartPose().dir.x + status.getOrigin().dir.x) * (SIGN_A * M_PI/180.0); 44 | startDir.y = (status.getStartPose().dir.y + status.getOrigin().dir.y) * (SIGN_B * M_PI/180.0); 45 | startDir.z = (status.getStartPose().dir.z + status.getOrigin().dir.z) * (SIGN_C * M_PI/180.0); 46 | // endDir = (status.getEndPose().dir + status.getOrigin().dir) * (SIGN * M_PI/180.0); 47 | endDir.x = (status.getEndPose().dir.x + status.getOrigin().dir.x) * (SIGN_A * M_PI/180.0); 48 | endDir.y = (status.getEndPose().dir.y + status.getOrigin().dir.y) * (SIGN_B * M_PI/180.0); 49 | endDir.z = (status.getEndPose().dir.z + status.getOrigin().dir.z) * (SIGN_C * M_PI/180.0); 50 | #endif 51 | } 52 | 53 | Point linearMotion::point(double s) { 54 | if ( length() == 0.0 ) { 55 | return start; 56 | } else { 57 | double t = s/this->length(); 58 | //if ( !(t >= 0.0) || !(t <= 1.0 + CALC_TOLERANCE) ) 59 | // std::cerr << "linearMotion::point() ERROR at s= " << s << " length= " << length() << " t evaluates to t= " << t << "\n"; 60 | // assert( t >= 0.0); assert( t <= 1.0 + CALC_TOLERANCE ); 61 | return start + (end-start)*t; 62 | } 63 | } 64 | 65 | double linearMotion::length() { 66 | #ifdef MULTI_AXIS 67 | double llinear = start.Distance(end); 68 | double lsdir = start.Distance(Point(0.0, 0.0, 0.0)); 69 | double ledir = end.Distance(Point(0.0, 0.0, 0.0)); 70 | double langle = startDir.Distance(endDir); 71 | 72 | langle = ((lsdir > ledir) ? lsdir : ledir) * langle; 73 | return llinear > langle ? llinear : langle; 74 | #else 75 | return start.Distance(end); 76 | #endif 77 | } 78 | 79 | #ifdef MULTI_AXIS 80 | Point linearMotion::angle(double s) { 81 | if ( length() == 0.0 ) { 82 | return startDir; 83 | } else { 84 | double t = s/this->length(); 85 | if ( !(t >= 0.0) || !(t <= 1.0 + CALC_TOLERANCE) ) 86 | std::cout << "linearMotion::angle() ERROR at s= " << s << " length= " << length() << " t evaluates to t= " << t << "\n"; 87 | assert( t >= 0.0); assert( t <= 1.0 + CALC_TOLERANCE ); 88 | return startDir + (endDir-startDir)*t; 89 | } 90 | } 91 | #endif 92 | 93 | //need to return RAPID for rapids... 94 | MOTION_TYPE linearMotion::getMotionType() { 95 | bool traverse = cmdMatch("STRAIGHT_TRAVERSE"); 96 | if (traverse) { 97 | return TRAVERSE; 98 | } else { 99 | return STRAIGHT_FEED; 100 | } 101 | } 102 | 103 | } // end namespace 104 | -------------------------------------------------------------------------------- /g2m/machineStatus.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 | ***************************************************************************/ 20 | 21 | #include 22 | #include 23 | 24 | #include "machineStatus.hpp" 25 | 26 | namespace g2m { 27 | 28 | /// copy-constructor 29 | machineStatus::machineStatus(const machineStatus& oldStatus) { 30 | spindleStat = oldStatus.spindleStat; 31 | F = oldStatus.F; 32 | S = oldStatus.S; 33 | coolant = oldStatus.coolant; 34 | plane = oldStatus.plane; 35 | origin = oldStatus.origin; 36 | endPose = startPose = oldStatus.endPose; 37 | myTool = oldStatus.myTool; 38 | endDir = Point(0,0,-1); 39 | prevEndDir = oldStatus.endDir; 40 | first = oldStatus.first; 41 | motionType = NOT_DEFINED; 42 | /// lastMotionWasTraverse gets copied from the previous machine status, 43 | /// and only gets changed if the prev status was motion at feedrate 44 | /// (this way, motionless cmds don't mess things up) 45 | lastMotionWasTraverse = oldStatus.lastMotionWasTraverse; 46 | if ( oldStatus.motionType == STRAIGHT_FEED || oldStatus.motionType == HELICAL) { 47 | lastMotionWasTraverse = false; 48 | } 49 | } 50 | 51 | /** 52 | This constructor is only to be used when initializing the simulation; it would not be useful elsewhere. 53 | \param initial is the initial pose of the machine, as determined by the interp from the variable file. 54 | \sa machineStatus(machineStatus const& oldStatus) 55 | */ 56 | machineStatus::machineStatus(Pose initial) { 57 | clearAll(); 58 | startPose = endPose = initial; 59 | first = true; 60 | setTool(0); 61 | } 62 | 63 | machineStatus::machineStatus(Pose initial, Pose userOrigin) { 64 | clearAll(); 65 | startPose = endPose = initial; 66 | origin = userOrigin; 67 | first = true; 68 | setTool(0); 69 | } 70 | 71 | /// reset machineStatus to reasonable defaults 72 | void machineStatus::clearAll() { 73 | F=S=0.0; 74 | plane = CANON_PLANE_XY; 75 | origin = Pose( Point(0,0,0), Point(0,0,0)); 76 | coolant.flood = false; 77 | coolant.mist = false; 78 | coolant.spindle = false; 79 | endPose = startPose = Pose( Point(0,0,0), Point(0,0,1)); 80 | endDir = prevEndDir = Point(0,0,-1); 81 | spindleStat = OFF; 82 | myTool = -1; 83 | motionType = NOT_DEFINED; 84 | lastMotionWasTraverse = false; 85 | } 86 | 87 | ///sets motion type, and checks whether this is the second (or later) motion command. 88 | void machineStatus::setMotionType(MOTION_TYPE m) { 89 | motionType = m; 90 | if (motionType == NOT_DEFINED) { 91 | std::cout << "motion type undef!! \n"; 92 | } else if (motionType == TRAVERSE) { 93 | lastMotionWasTraverse = true; 94 | } 95 | static int count = 0; 96 | if ((first) && ((m == STRAIGHT_FEED) || (m == TRAVERSE) || (m == HELICAL)) ) { 97 | if (count == 0) 98 | count++; 99 | else 100 | first = false; 101 | } 102 | } 103 | 104 | 105 | /// setEndPose 106 | void machineStatus::setEndPose( Point p) { 107 | #ifdef MULTI_AXIS 108 | endPose = Pose( p, Point(0,0,0) ); 109 | #else 110 | endPose = Pose( p, Point(0,0,1) ); 111 | #endif 112 | } 113 | 114 | /// setEndPose 115 | void machineStatus::setEndPose( Pose newPose) { 116 | endPose = newPose; 117 | } 118 | 119 | /// set the current tool index 120 | void machineStatus::setTool(int n) { 121 | //std::cout << "adding tool " << n << ".\n"; 122 | myTool = n; 123 | //canon::addTool(n); 124 | } 125 | 126 | 127 | } 128 | 129 | 130 | -------------------------------------------------------------------------------- /g2m/g2m.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * modifications Copyright (C) 2011 by Anders Wallin * 5 | * anders.e.e.wallin@gmail.com * 6 | * modified by Kazuyasu Hamada 2015, k-hamada@gifu-u.ac.jp * 7 | * * 8 | * This program is free software; you can redistribute it and/or modify * 9 | * it under the terms of the GNU General Public License as published by * 10 | * the Free Software Foundation; either version 2 of the License, or * 11 | * (at your option) any later version. * 12 | * * 13 | * This program is distributed in the hope that it will be useful, * 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 | * GNU General Public License for more details. * 17 | * * 18 | * You should have received a copy of the GNU General Public License * 19 | * along with this program; if not, write to the * 20 | * Free Software Foundation, Inc., * 21 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 22 | ***************************************************************************/ 23 | 24 | #ifndef GTOM_HH 25 | #define GTOM_HH 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include "canonLine.hpp" 37 | 38 | namespace g2m { 39 | 40 | /// \brief This class runs the interpreter ("rs274"), and creates a canonLine object for each canonical 41 | /// command generated by the interpreter. 42 | class g2m : public QObject { 43 | Q_OBJECT; 44 | 45 | public: 46 | g2m() { debug = false; initialPos = Pose( Point(0,0,0), Point(0,0,0) ); userOrigin = Pose( Point(0,0,0), Point(0,0,0) ); total_gcode_lines = 0; } 47 | /// return vector of canonLines 48 | std::vector getCanonLines() { return lineVector; } 49 | void setInitialPos(Pose init) { initialPos = init; } 50 | void setOrigin(Pose origin) { userOrigin = origin; } 51 | Pose getOrigin() { return userOrigin; } 52 | 53 | /* 54 | int toGcodeLineNo(int canonLineNo) { 55 | if ((unsigned)canonLineNo + 1 >= lineTable.size()) 56 | return ++canonLineNo; 57 | else 58 | return lineTable[canonLineNo]; 59 | } 60 | */ 61 | 62 | public slots: 63 | /// run the interpreter 64 | void interpret_file(); 65 | /// set the path to the .ngc g-code file 66 | void setFile(QString infile) { 67 | emit debugMessage( tr("g2m: setting file %1").arg(infile) ); 68 | file = infile; 69 | } 70 | /// set the path to the tootable 71 | void setToolTable(QString tbl_file) { 72 | emit debugMessage( tr("g2m: setting tooltable %1").arg(tbl_file) ); 73 | tooltable = tbl_file; 74 | } 75 | /// set the path to the rs274 binary 76 | void setInterp(QString interp_binary) { 77 | emit debugMessage( tr("g2m: setting rs274 path: %1").arg(interp_binary) ); 78 | interp = interp_binary; 79 | } 80 | 81 | /// set debug mode on/off 82 | void setDebug(bool d) {debug = d;} 83 | 84 | signals: 85 | /// debug messages 86 | void debugMessage(QString s); 87 | /// emitted while .ngc g-code file is read. the g-code line read. 88 | void gcodeLineMessage(QString s); 89 | /// emitted during interpret(), the canon line as a string 90 | void canonLineMessage(QString s); 91 | /// emitted during interpret(), the current canonLine object 92 | void signalCanonLine(canonLine* line); 93 | // emitted when M2 is reached 94 | void signalNCend(); 95 | // emitted when interpreter errored 96 | void signalError(QString s); 97 | 98 | protected: 99 | bool chooseToolTable(); 100 | bool startInterp(QProcess &tc); 101 | void interpret(); 102 | bool processCanonLine(std::string l); 103 | void infoMsg(std::string s); 104 | bool startInterp2(QProcess &tc, QString tempFile); 105 | void interpret2(QString tempFile); 106 | /// the canonLines produced when interpreting g-code 107 | std::vector lineVector; 108 | /// path to .ngc g-code file 109 | QString file; 110 | /// path to tooltable 111 | QString tooltable; 112 | /// path to rs274 executable 113 | QString interp; 114 | /// flag for debug mode 115 | bool debug; 116 | /// number of lines in the .ngc g-code file 117 | int gcode_lines; 118 | 119 | private: 120 | Pose initialPos; 121 | Pose userOrigin; 122 | 123 | int total_gcode_lines; 124 | //std::vector lineTable; not used for now 125 | }; 126 | 127 | } // end namespace 128 | #endif //GTOM_HH 129 | -------------------------------------------------------------------------------- /settings.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Settings 4 | 5 | 6 | 7 | 0 8 | 0 9 | 607 10 | 282 11 | 12 | 13 | 14 | Settings 15 | 16 | 17 | 18 | 19 | 20 | rs274ngc interpreter: 21 | 22 | 23 | 24 | 25 | 26 | rs274 will be in the /bin dir 27 | of your machinekit installation 28 | 29 | 30 | 31 | 32 | 33 | 34 | Browse 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Tool table (*.tbl) file: 45 | 46 | 47 | 48 | 49 | 50 | A xxx.tbl file will be in your 51 | machinekit machine config dir 52 | 53 | 54 | 55 | 56 | 57 | 58 | Browse 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Temporary G-code file: 69 | 70 | 71 | 72 | 73 | 74 | Only change this if you wish to start with a specific file 75 | Beware it could be overwritten. 76 | 77 | 78 | /tmp/gcode.ngc 79 | 80 | 81 | 82 | 83 | 84 | 85 | Browse 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | Qt::Horizontal 96 | 97 | 98 | 99 | 269 100 | 20 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | Cancel 109 | 110 | 111 | 112 | 113 | 114 | 115 | OK 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | pb_browse1 125 | clicked() 126 | Settings 127 | onFileBrowse1() 128 | 129 | 130 | 362 131 | 50 132 | 133 | 134 | 389 135 | 80 136 | 137 | 138 | 139 | 140 | pb_browse2 141 | clicked() 142 | Settings 143 | onFileBrowse2() 144 | 145 | 146 | 357 147 | 130 148 | 149 | 150 | 382 151 | 167 152 | 153 | 154 | 155 | 156 | pb_browse3 157 | clicked() 158 | Settings 159 | onFileBrowse3() 160 | 161 | 162 | 352 163 | 210 164 | 165 | 166 | 389 167 | 237 168 | 169 | 170 | 171 | 172 | pushButton_2 173 | clicked() 174 | Settings 175 | onAccept() 176 | 177 | 178 | 345 179 | 263 180 | 181 | 182 | 327 183 | 280 184 | 185 | 186 | 187 | 188 | pushButton 189 | clicked() 190 | Settings 191 | reject() 192 | 193 | 194 | 217 195 | 265 196 | 197 | 198 | 137 199 | 267 200 | 201 | 202 | 203 | 204 | 205 | onFileBrowse1() 206 | onFileBrowse2() 207 | onFileBrowse3() 208 | onAccept() 209 | 210 | 211 | -------------------------------------------------------------------------------- /g2m/machineStatus.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 | ***************************************************************************/ 20 | #ifndef MACHINESTATUS_HH 21 | #define MACHINESTATUS_HH 22 | 23 | #include 24 | #include //to fix "error: INT_MIN was not declared in this scope" 25 | #include "point.hpp" 26 | 27 | namespace g2m { 28 | 29 | /// the current plane, XY, YZ, or XZ 30 | enum CANON_PLANE { CANON_PLANE_XY, CANON_PLANE_YZ, CANON_PLANE_XZ }; 31 | /// the status of the spindle 32 | enum SPINDLE_STATUS { OFF = 0x100, CW = 0x200, CCW = 0x400, BRAKE = 0x800 }; 33 | /// motion type 34 | enum MOTION_TYPE { NOT_DEFINED = 0x0, MOTIONLESS = 0x1, HELICAL = 0x2, STRAIGHT_FEED = 0x4, TRAVERSE = 0x8 }; 35 | 36 | 37 | /// coolant-status 38 | struct coolantStruct { 39 | /// is flood on? 40 | bool flood; 41 | /// is mist on? 42 | bool mist; 43 | /// is the spindle on? 44 | bool spindle; 45 | }; 46 | 47 | /** 48 | \class machineStatus 49 | \brief This class contains the machine's state for one canonical command. 50 | The information stored includes the 51 | * coolant state, spindle speed and direction, feedrate, start and end pose, tool in use 52 | 53 | Important: 'pose' refers to how the machine's axes are positioned, 54 | * while 'direction' refers to the direction of motion 55 | */ 56 | class machineStatus { 57 | public: 58 | machineStatus(machineStatus const& oldStatus); 59 | machineStatus(Pose initial); 60 | machineStatus(Pose initial, Pose userOrigin); 61 | void setMotionType(MOTION_TYPE m); 62 | void setEndPose(Pose newPose); 63 | /// set endPose 64 | void setEndPose(Point p); 65 | /// set current feedrate 66 | void setFeed(const double f) { F = f; }; 67 | /// set spindle speed 68 | void setSpindleSpeed(const double s) { S = s; }; 69 | /// set spindle status 70 | void setSpindleStatus(const SPINDLE_STATUS s) { spindleStat = s; }; 71 | /// set coolant status 72 | void setCoolant(coolantStruct c) { coolant = c; }; 73 | /// set the current plane 74 | void setPlane(CANON_PLANE p) { plane = p; }; 75 | /// set the current origin 76 | void setOrigin(Pose newOrigin) { origin = newOrigin; }; 77 | /// return the current feedrate 78 | double getFeed() const { return F; }; 79 | /// return spindle speed 80 | double getSpindleSpeed() const { return S; }; 81 | /// return the spindle-status structure 82 | SPINDLE_STATUS getSpindleStatus() const { return spindleStat; }; 83 | /// return the coolant structure 84 | const coolantStruct getCoolant() { return coolant; }; 85 | /// return startPose 86 | const Pose getStartPose() { return startPose; }; 87 | /// return endPose 88 | const Pose getEndPose() { return endPose; }; 89 | /// return the current plane 90 | CANON_PLANE getPlane() const { return plane; }; 91 | /// return the current origin 92 | Pose getOrigin() const { return origin; }; 93 | /// set startDir 94 | void setStartDir( Point d) { startDir = d; }; 95 | /// return startDir 96 | const Point getStartDir() const { return startDir; }; 97 | /// set endDir 98 | void setEndDir( Point d) { endDir = d; }; 99 | /// return endDir 100 | const Point getEndDir() const { return endDir; }; 101 | /// return prevEndDir 102 | const Point getPrevEndDir() const { return prevEndDir; }; 103 | void clearAll(void); 104 | /// return first 105 | bool isFirst() { return first; }; 106 | /// set the tool index 107 | void setTool(int n); //n is the ID of the tool to be used. 108 | /// return the current tool index 109 | int getTool() const { return myTool; }; 110 | /// return the current spindle status & motion type 111 | int getSpindleMotionStatus() const { return (spindleStat | motionType); } 112 | 113 | protected: 114 | /// machine Pose at start of this move 115 | Pose startPose; 116 | /// machine Pose at end of this move 117 | Pose endPose; 118 | /// feed-rate 119 | double F; 120 | /// spindle speed 121 | double S; 122 | /// coolant status 123 | coolantStruct coolant; 124 | /// the direction in which the current move starts 125 | Point startDir; 126 | /// the direction in which the current move ends 127 | Point endDir; 128 | /// endDir of the previous move 129 | Point prevEndDir; 130 | /// flag indicating very first move of g-code program(?) 131 | bool first; 132 | /// misc flag(?) 133 | bool lastMotionWasTraverse; 134 | /// index of current tool 135 | int myTool; 136 | /// the current motion type 137 | MOTION_TYPE motionType; 138 | /// the status of the spindle 139 | SPINDLE_STATUS spindleStat; 140 | /// the current plane (used e.g. for G2 and G3 moves), either XY, XZ, or YZ 141 | CANON_PLANE plane; 142 | /// the current origin 143 | Pose origin; 144 | 145 | private: 146 | machineStatus(); //prevent use of this ctor by making it private 147 | }; 148 | 149 | } // end namespace 150 | 151 | #endif //MACHINESTATUS_HH 152 | -------------------------------------------------------------------------------- /g2m/canonLine.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 | ***************************************************************************/ 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include "canonLine.hpp" 28 | 29 | #include "machineStatus.hpp" 30 | #include "canonMotionless.hpp" 31 | #include "linearMotion.hpp" 32 | #include "helicalMotion.hpp" 33 | 34 | namespace g2m { 35 | 36 | /// note, the constructor is protected! 37 | canonLine::canonLine(std::string canonL, machineStatus prevStatus): myLine(canonL), status(prevStatus) { 38 | tokenize(myLine,canonTokens); 39 | } 40 | 41 | ///returns the number after N on the line, -1 if none 42 | int canonLine::getN() { 43 | if ( (cantok(1).compare("N.....") == 0)) 44 | return -1; 45 | else 46 | return tok2i(1,1); 47 | } 48 | 49 | /** converts canonTokens[n] to double 50 | \param n this is the token to convert 51 | \returns token n, converted to double 52 | */ 53 | double canonLine::tok2d(uint n) { 54 | if (canonTokens.size() < n+1 ) 55 | return NAN; 56 | char* end; 57 | double d = strtod( canonTokens[n].c_str(), &end ); 58 | // assert ( *end == 0 ); 59 | return d; 60 | } 61 | 62 | /** converts canonTokens[n] to int 63 | \param n this is the token to convert 64 | \param offset skip this many chars at the beginning of the token 65 | \returns token n, converted to integer 66 | */ 67 | int canonLine::tok2i(uint n,uint offset) { 68 | if (canonTokens.size() < n+1 ) 69 | return INT_MIN; 70 | char * end; 71 | int i = strtol( &canonTokens[n].c_str()[offset], &end, 10 ); 72 | //assert ( *end != 0 ); 73 | return i; 74 | } 75 | 76 | /// return the n:th canon-token 77 | std::string canonLine::cantok(unsigned int n) { 78 | if (n < canonTokens.size()) { 79 | return canonTokens[n]; 80 | } else { 81 | std::cout << "malformed input line " << myLine << std::endl; 82 | std::string s = ""; 83 | return s; 84 | } 85 | } 86 | 87 | ///return true if the canonical command for this line matches 'm' 88 | bool canonLine::cmdMatch(std::string m) { 89 | if (canonTokens.size() < 3) 90 | return false; 91 | return (m.compare(canonTokens[2]) == 0); //compare returns zero for a match 92 | } 93 | 94 | /// return canonTokens[2] 95 | const std::string canonLine::getCanonicalCommand() { 96 | if (canonTokens.size() < 3 ) 97 | return "BAD_LINE_NO_CMD"; 98 | return canonTokens[2]; 99 | } 100 | 101 | ///return a line identifier as string: getN() if !=-1, else getLineNum() 102 | const std::string canonLine::getLnum() { 103 | return ((getN()==-1) ? (cantok(0)) : (cantok(1))); 104 | } 105 | 106 | //from http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html 107 | //0 is canon line 108 | //1 is gcode Nnnnnn line 109 | //2 is canonical command 110 | /** Splits a string using delimiters. 111 | \param str the string to be split 112 | \param tokenV a vector of strings, each of which is a piece of str 113 | \param delimiters defaults to: both parenthesis, comma, space 114 | \sa tokenize() 115 | */ 116 | void canonLine::tokenize( std::string str, 117 | std::vector& tokenV, 118 | const std::string& delimiters ) { 119 | // Skip delimiters at beginning. 120 | std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); 121 | // Find first "non-delimiter". 122 | std::string::size_type pos = str.find_first_of(delimiters, lastPos); 123 | 124 | while (std::string::npos != pos || std::string::npos != lastPos) 125 | { 126 | // Found a token, add it to the vector. 127 | tokenV.push_back(str.substr(lastPos, pos - lastPos)); 128 | // Skip delimiters. Note the "not_of" 129 | lastPos = str.find_first_not_of(delimiters, pos); 130 | // Find next "non-delimiter" 131 | pos = str.find_first_of(delimiters, lastPos); 132 | } 133 | } 134 | 135 | /**Create objects that inherit from canonLine. It determines which type 136 | * of object to create, and returns a pointer to that new object 137 | */ 138 | canonLine * canonLine::canonLineFactory (std::string l , machineStatus s) { 139 | //check if canonical command is motion or something else 140 | //motion commands: STRAIGHT_TRAVERSE STRAIGHT_FEED ARC_FEED 141 | size_t lin,af,cmnt,msg; 142 | cmnt = l.find("COMMENT"); 143 | msg = l.find("MESSAGE"); 144 | lin = l.find("STRAIGHT_"); 145 | af = l.find("ARC_FEED"); 146 | /* 147 | ** check for comments first because it is not impossible 148 | ** for one to contain the text "STRAIGHT" or "ARC_FEED" 149 | */ 150 | std::cout << l; // print the line 151 | if ( (cmnt!=std::string::npos) || (msg!=std::string::npos) ) { 152 | return new canonMotionless(l,s); // comment or message, no motion 153 | } else if (lin!=std::string::npos) { 154 | return new linearMotion(l,s); // straight traverse or straight feed 155 | } else if (af!=std::string::npos) { 156 | return new helicalMotion(l,s); // arc or helix 157 | } else { 158 | return new canonMotionless(l,s); //canonical command is not a motion command 159 | } 160 | } 161 | 162 | } // end namespace 163 | -------------------------------------------------------------------------------- /g2m/canonMotionless.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 | ***************************************************************************/ 20 | #include "canonMotionless.hpp" 21 | #include "machineStatus.hpp" 22 | 23 | #include 24 | 25 | namespace g2m { 26 | 27 | canonMotionless::canonMotionless(std::string canonL, machineStatus prevStatus):canonLine(canonL, prevStatus) { 28 | match = true; 29 | handled = true; 30 | ncEnd = false; 31 | status.setMotionType(MOTIONLESS); 32 | status.setEndPose(status.getStartPose()); 33 | 34 | //match canonical commands. the string MUST be the complete command name 35 | //NOTE: cmdMatch ONLY looks at the command part of the line, canonTokens[2]. 36 | if (cmdMatch("COMMENT")) { 37 | //do nothing 38 | } else if (cmdMatch("MESSAGE")) { 39 | 40 | /*size_t a,b; 41 | a = myLine.find_first_of("\"") + 1; 42 | b = myLine.find_last_of("\""); 43 | */ 44 | } else if (cmdMatch("STOP_SPINDLE_TURNING")) { 45 | status.setSpindleStatus(SPINDLE_STATUS(OFF)); 46 | } else if (cmdMatch("START_SPINDLE_CLOCKWISE")) { 47 | status.setSpindleStatus(SPINDLE_STATUS(CW)); 48 | } else if (cmdMatch("START_SPINDLE_COUNTERCLOCKWISE")) { 49 | status.setSpindleStatus(SPINDLE_STATUS(CCW)); 50 | } else if (cmdMatch("SET_SPINDLE_SPEED")) { 51 | status.setSpindleSpeed(tok2d(3)); 52 | } else if (cmdMatch("MIST_ON")) { 53 | coolantStruct c = status.getCoolant(); 54 | c.mist = true; 55 | status.setCoolant(c); 56 | } else if (cmdMatch("MIST_OFF")) { 57 | coolantStruct c = status.getCoolant(); 58 | c.mist = false; 59 | status.setCoolant(c); 60 | } else if (cmdMatch("FLOOD_ON")) { 61 | coolantStruct c = status.getCoolant(); 62 | c.flood = true; 63 | status.setCoolant(c); 64 | } else if (cmdMatch("FLOOD_OFF")) { 65 | coolantStruct c = status.getCoolant(); 66 | c.flood = false; 67 | status.setCoolant(c); 68 | } else if (cmdMatch("DWELL")) { 69 | } else if (cmdMatch("SET_FEED_MODE")) { // do nothing? 70 | } else if (cmdMatch("SET_SPINDLE_MODE")) { // do nothing? 71 | } else if (cmdMatch("PALLET_SHUTTLE")) { // do nothing? 72 | } else if (cmdMatch("SET_FEED_RATE")) { 73 | status.setFeed(tok2d(3)); 74 | } else if (cmdMatch("SET_FEED_REFERENCE")) { 75 | if (cantok(3).compare("CANON_XYZ") == 0) {//this is the standard feed reference, do nothing 76 | } else if (cantok(3).compare("CANON_WORKPIECE")) { // do nothing? 77 | } else handled = false; 78 | } else if (cmdMatch("SELECT_TOOL")) { 79 | //this only tells the machine to reposition the tool carousel, correct? if so it can be ignored 80 | } else if (cmdMatch("CHANGE_TOOL")) { 81 | status.setTool(tok2i(3)); 82 | } else if (cmdMatch("USE_TOOL_LENGTH_OFFSET")) { //don't need to do anything, unless the toolholder/spindle is modeled 83 | } else if ( (cmdMatch("SET_ORIGIN_OFFSETS")) || cmdMatch("SET_G5X_OFFSET")) { 84 | unsigned int i = 3; 85 | if (cmdMatch("SET_G5X_OFFSET")) { 86 | i=4; 87 | } 88 | // else if (uio::debuggingOn()) { 89 | // std::cout << "Using old version of interpreter!"; 90 | //} 91 | if (cantok(i).compare("0.0000") == 0) { 92 | /* 93 | ** this is a common canon statement. we are going to hijack it to produce a warning, 94 | ** because the data we're getting was produced with a format of %.4f or so. The 95 | ** reduced precision may give OCC indigestion. 96 | */ 97 | //if (uio::debuggingOn()) 98 | // uio::infoMsg("Warning, input has reduced precision - expected more zeros: \n" + myLine +"\nModel may fail!"); 99 | } else if (canonTokens.size() == (i+6) ) { //6 axes, tokens 3-8 (old interp) or 4-9 (new) 100 | if ((tok2d(i++)==0) && (tok2d(i++)==0) && (tok2d(i++)==0) && (tok2d(i++)==0) && (tok2d(i++)==0) && (tok2d(i)==0)) { 101 | //do nothing if all zeros, interp issues this when it starts up and it has no effect 102 | } else { 103 | handled = false; //because I still don't know what to do with the correct data... 104 | } 105 | } else { 106 | handled = false; //not 9 tokens - so not 6 axes! 107 | } 108 | } else if (cmdMatch("USE_LENGTH_UNITS")) { 109 | // handled = false; 110 | handled = true; 111 | } else if (cmdMatch("ENABLE_FEED_OVERRIDE")) { 112 | handled = true; 113 | } else if (cmdMatch("ENABLE_SPEED_OVERRIDE")) { 114 | handled = true; 115 | } else if (cmdMatch("SET_MOTION_CONTROL_MODE")) { 116 | handled = false; 117 | } else if (cmdMatch("SET_XY_ROTATION")) { 118 | if (canonTokens.size() == 6) { 119 | if ((tok2d(3)==0) && (tok2d(4)==0) && (tok2d(5)==0) ) { 120 | //no rotation. interp issues this when starting up 121 | } else { 122 | handled = false; 123 | } 124 | } 125 | } else if (cmdMatch("SET_NAIVECAM_TOLERANCE")) { 126 | handled = false; 127 | } else if (cmdMatch("PROGRAM_END")) { 128 | ncEnd = true; 129 | } else if (cmdMatch("PROGRAM_STOP")) { 130 | ncEnd = true; 131 | } else if (cmdMatch("SELECT_PLANE" )) { 132 | if (cantok(3).compare("CANON_PLANE_XZ")==0) { 133 | status.setPlane(CANON_PLANE_XZ); 134 | } else if (cantok(3).compare("CANON_PLANE_YZ")==0) { 135 | status.setPlane(CANON_PLANE_YZ); 136 | } else if (cantok(3).compare("CANON_PLANE_XY")==0) { 137 | status.setPlane(CANON_PLANE_XY); 138 | } else {// sanity check 139 | std::cout << "Error: Failed to detect CANON_PLANE in _ "; // +cantok(3)+"_:\n" + myLine); 140 | } 141 | } else match = false; 142 | 143 | //if ( (uio::debuggingOn()) && (!match || !handled) ) { 144 | if ( (!match || !handled) ) { 145 | std::string m; 146 | if (!handled) { 147 | m = "Unhandled canonical command ("+cantok(2)+")\n"; 148 | } else { 149 | //m = "Unknown canonical command ("+cantok(2)+") at " + cantok(0) + " " + cantok(1); 150 | m = "No match for " + canonL; 151 | } 152 | std::cout << m; 153 | } 154 | } 155 | 156 | 157 | } // end namespace 158 | -------------------------------------------------------------------------------- /g2m/gplayer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Modification & Copyright 2015 Kazuyasu Hamada (k-hamada@gifu-u.ac.jp) 3 | */ 4 | 5 | #ifndef GPLAYER_HH 6 | #define GPLAYER_HH 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "canonLine.hpp" 19 | #include "nanotimer.hpp" 20 | 21 | #define DEFAULT_STEP_SIZE (0.1) 22 | #define DEFAULT_FEED_RATE (200.0) 23 | #define DEFAULT_TRAVERSE_FEED_RATE (1000.0) 24 | #define TOLERANCE (1e-2) 25 | #define DEFAULT_ANIMATE_INTERVAL (3) 26 | 27 | namespace g2m { 28 | 29 | enum PLUNGE_STATUS { NO_PLUNGE = 0x0, POSITIVE_PLUNGE = 0x10000, NEGATIVE_PLUNGE = 0x20000, }; 30 | 31 | /** 32 | \class GPlayer 33 | \brief This class loops through the list of canonLine objects produced by g2m. 34 | */ 35 | class GPlayer : public QObject { 36 | Q_OBJECT; 37 | 38 | public: 39 | GPlayer() { 40 | first = true; 41 | current_line = 0; 42 | inv_ds = 1.0 / DEFAULT_STEP_SIZE; 43 | m = 0; 44 | play_flag = false; 45 | feed_rate = DEFAULT_FEED_RATE; 46 | traverse_feed_rate = DEFAULT_TRAVERSE_FEED_RATE; 47 | total_length = 0.0; 48 | total_time = 0.0; 49 | } 50 | 51 | void setStepSize(double ds) { 52 | if (ds > 0.0) 53 | inv_ds = 1.0 / ds; 54 | } 55 | 56 | void setTraverseFeedRate(double rate) { 57 | if (rate > 0.0) 58 | traverse_feed_rate = rate; 59 | } 60 | 61 | canonLine* getCanonLine(unsigned int line) { return lines[line]; } 62 | 63 | public slots: 64 | /// start or resume executing the program 65 | void play() { 66 | if (play_flag == false) { 67 | play_flag = true; 68 | qDebug() << " gplayer::play() "; 69 | emit debugMessage( tr("GPlayer: play") ); 70 | slotRequestMove(); 71 | } 72 | } 73 | /// signal the next move 74 | void slotRequestMove() { 75 | if (!(lines.size() > 0)) { play_flag = false; return; } 76 | if (current_line >= (lines.size()-1)) { play_flag = false; return; } 77 | if (play_flag == false) { 78 | if (lines.size() - 1) 79 | emit signalProgress( (int)(100*current_line/(lines.size()-1)) , current_line, total_time, true); // report progress to ui 80 | 81 | return; 82 | } 83 | // UI request that we signal the next signalToolPosition() 84 | canonLine* cl = lines[current_line]; 85 | 86 | if (first) {// first ever call here 87 | current_tool = cl->getStatus()->getTool(); 88 | first = false; 89 | } 90 | if (cl->isMotion() ) { 91 | // divide motion into sampled points. signal motion along path. 92 | if (m == 0) { 93 | move_length = cl->length(); 94 | n_samples = std::max( (int)( move_length * inv_ds ) , 2 ); // want at least two points: start-end 95 | interval_size = move_length /(double)(n_samples-1); 96 | motionStatus = cl->getStatus()->getSpindleMotionStatus(); 97 | feed_rate = cl->getStatus()->getFeed(); 98 | if (feed_rate <= 0.0) feed_rate = DEFAULT_FEED_RATE; 99 | double diff_z = cl->point((n_samples-1)*interval_size).z - cl->point(0.0).z; 100 | plunge = (diff_z > TOLERANCE) ? POSITIVE_PLUNGE : (diff_z < -TOLERANCE) ? NEGATIVE_PLUNGE : NO_PLUNGE; 101 | motionStatus |= plunge; 102 | } 103 | // FIXME: handle first and last moves differently? 104 | Point pos = cl->point( (double)(m) * interval_size ); 105 | #ifdef MULTI_AXIS 106 | Point angle = cl->angle( (double)(m) * interval_size ); 107 | #endif 108 | if (m == (n_samples-1) ) 109 | move_done = true; 110 | #ifdef MULTI_AXIS 111 | emit signalToolPosition( pos.x, pos.y, pos.z, angle.x, angle.y, angle.z, current_line, motionStatus, feed_rate ); 112 | #else 113 | emit signalToolPosition( pos.x, pos.y, pos.z, current_line, motionStatus, feed_rate); 114 | #endif 115 | m++; // advance along the move 116 | } else { 117 | // not motion, check for toolchange 118 | if ( current_tool != cl->getStatus()->getTool() ) { 119 | emit signalToolChange( cl->getStatus()->getTool() ); 120 | current_tool = cl->getStatus()->getTool(); 121 | } 122 | current_line++; 123 | slotRequestMove(); // call myself! 124 | } 125 | 126 | if (move_done) { 127 | if (cl->isMotion()) { 128 | total_length += move_length; 129 | if (cl->getMotionType() == TRAVERSE) 130 | total_time += move_length / traverse_feed_rate; 131 | else 132 | total_time += move_length / feed_rate; 133 | } 134 | current_line++; 135 | move_done = false; 136 | m = 0; 137 | } 138 | if (lines.size() - 1) 139 | if (m == 0 || (m & DEFAULT_ANIMATE_INTERVAL) == 0x0) { 140 | emit signalProgress( (int)(100*current_line/(lines.size()-1)) , current_line, total_time, current_line == (lines.size()-1)); // report progress to ui 141 | } 142 | } 143 | /// pause program 144 | void pause() { 145 | play_flag = false; 146 | emit debugMessage( tr("GPlayer: pause") ); 147 | } 148 | /// stop the execution of the g-code program 149 | void stop() { 150 | if (play_flag == false) 151 | emit signalProgress(0, 0, 0.0, true); 152 | play_flag = false; 153 | first = true; 154 | current_line = 0; 155 | m = 0; 156 | total_time = 0; 157 | emit debugMessage( tr("GPlayer: stop") ); 158 | } 159 | /// add a canonLine to the vector of lines to be processed 160 | /// \param l the new canonLine to be added 161 | void appendCanonLine( canonLine* l) { 162 | lines.push_back(l); 163 | } 164 | 165 | signals: 166 | /// signal a new tool position 167 | #ifdef MULTI_AXIS 168 | void signalToolPosition( double x, double y, double z, double a, double b, double c, int line, int mstatus, double feedrate ); // 5-axis for now.. 169 | #else 170 | void signalToolPosition( double x, double y, double z, int line, int mstatus, double feedrate ); // 3-axis. 171 | #endif 172 | /// signal a tool change to new tool \param t 173 | void signalToolChange( int t ); 174 | /// signal the UI how far along the g-code program we are 175 | void signalProgress( int p, int line, double time, bool force ); 176 | /// signal a debug message 177 | void debugMessage(QString s); 178 | 179 | protected: 180 | /// flag for first move of g-code 181 | bool first; 182 | /// index of current tool 183 | int current_tool; 184 | /// the current canonLine being processed 185 | unsigned int current_line; 186 | /// loop variable 187 | int m; 188 | double move_length; 189 | int n_samples; 190 | double interval_size; 191 | /// flag indicating when current move done 192 | bool move_done; 193 | /// vector of canonLines to process 194 | std::vector lines; 195 | 196 | bool play_flag; 197 | 198 | private: 199 | int plunge; 200 | int motionStatus; 201 | double inv_ds; 202 | double feed_rate; 203 | double traverse_feed_rate; 204 | double total_length; 205 | double total_time; 206 | }; 207 | 208 | } // end namespace 209 | #endif // GPlayer_HH 210 | -------------------------------------------------------------------------------- /view.cpp: -------------------------------------------------------------------------------- 1 | #include "view.h" 2 | 3 | #include 4 | 5 | #ifdef WIN32 6 | #include 7 | #endif 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "canonMotion.hpp" 16 | 17 | void View::keyPressEvent(QKeyEvent *e) { 18 | int keyInt = e->key(); 19 | Qt::Key key = static_cast(keyInt); 20 | 21 | if (key == Qt::Key_unknown) { 22 | qDebug() << "Unknown key from a macro probably"; 23 | return; 24 | } 25 | 26 | // the user have clicked just and only the special keys Ctrl, Shift, Alt, Meta. 27 | if (key == Qt::Key_Control || 28 | key == Qt::Key_Shift || 29 | key == Qt::Key_Alt || 30 | key == Qt::Key_Meta) 31 | { 32 | // qDebug() << "Single click of special key: Ctrl, Shift, Alt or Meta"; 33 | // qDebug() << "New KeySequence:" << QKeySequence(keyInt).toString(QKeySequence::NativeText); 34 | // return; 35 | } 36 | 37 | // check for a combination of user clicks 38 | Qt::KeyboardModifiers modifiers = e->modifiers(); 39 | QString keyText = e->text(); 40 | // if the keyText is empty than it's a special key like F1, F5, ... 41 | // qDebug() << "Pressed Key:" << keyText; 42 | 43 | QList modifiersList; 44 | if (modifiers & Qt::ShiftModifier) 45 | keyInt += Qt::SHIFT; 46 | if (modifiers & Qt::ControlModifier) 47 | keyInt += Qt::CTRL; 48 | if (modifiers & Qt::AltModifier) 49 | keyInt += Qt::ALT; 50 | if (modifiers & Qt::MetaModifier) 51 | keyInt += Qt::META; 52 | 53 | QString seq = QKeySequence(keyInt).toString(QKeySequence::NativeText); 54 | // qDebug() << "KeySequence:" << seq; 55 | 56 | return; // skip built in command if overridden by shortcut 57 | 58 | /* 59 | switch (e->key()) { 60 | case Qt::Key_S : 61 | break; 62 | case Qt::Key_P : 63 | break; 64 | case Qt::Key_C : 65 | resetCamView(); 66 | break; 67 | default: 68 | QGLViewer::keyPressEvent(e); 69 | } 70 | */ 71 | QGLViewer::keyPressEvent(e); 72 | } 73 | 74 | View::View(QWidget *parent) : QGLViewer(parent) { 75 | setAttribute(Qt::WA_DeleteOnClose); 76 | 77 | lines.reserve(20000); 78 | } 79 | 80 | View::~View() { 81 | // qDebug() << "View::~View()"; 82 | } 83 | 84 | void View::close() { 85 | // qDebug() << "View::close()"; 86 | // savePrefs(); 87 | QGLViewer::close(); 88 | } 89 | 90 | void View::clear() { 91 | //QMutexLocker locker(&mutex); 92 | 93 | // reset bounding box 94 | aabb[0] = std::numeric_limits::max(); 95 | aabb[1] = std::numeric_limits::max(); 96 | aabb[2] = std::numeric_limits::max(); 97 | 98 | aabb[3] = std::numeric_limits::min(); 99 | aabb[4] = std::numeric_limits::min(); 100 | aabb[5] = std::numeric_limits::min(); 101 | 102 | //setSceneRadius(1); 103 | 104 | lines.clear(); 105 | dirty = false; 106 | updateGLViewer(); 107 | } 108 | 109 | void View::resetCamView() { 110 | camera()->setPosition(initialCameraPosition); 111 | camera()->setOrientation(initialCameraOrientation); 112 | updateGLViewer(); 113 | } 114 | 115 | void View::init() { 116 | setShortcut(DISPLAY_FPS, Qt::CTRL+Qt::Key_F); 117 | setShortcut(DRAW_GRID, 0); 118 | 119 | setManipulatedFrame(new ManipulatedFrame()); 120 | 121 | glMatrixMode(GL_MODELVIEW); 122 | glLoadIdentity(); 123 | 124 | glEnable(GL_DEPTH_TEST); 125 | glDrawBuffer(GL_BACK); 126 | //glDisable(GL_CULL_FACE); 127 | //glLineStipple(2, 0xFFFF); 128 | glDisable(GL_LIGHTING); 129 | glClearColor(0.0,0.0,0.25,0); 130 | //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 131 | //glLineWidth(3); 132 | 133 | setGridIsDrawn(); 134 | setFPSIsDisplayed(); 135 | setAxisIsDrawn(); 136 | 137 | setMouseBinding(Qt::AltModifier, Qt::RightButton, SHOW_ENTIRE_SCENE, true, Qt::MidButton); 138 | 139 | clear(); 140 | 141 | //startAnimation(); 142 | } 143 | 144 | void View::initializeGL() { 145 | QGLViewer::initializeGL(); 146 | //camera()->setZNearCoefficient(0.000001); 147 | //camera()->setZClippingCoefficient(1000.0); 148 | } 149 | 150 | void View::appendCanonLine(canonLine *l) { 151 | //QMutexLocker locker(&mutex); 152 | lines.push_back(l); 153 | dirty = true; // for updateGLViewer() 154 | 155 | if (l->isMotion()) { 156 | Point pos1 = l->point(0); 157 | Point pos2 = l->point(1); 158 | 159 | double oaabbmin[3], oaabbmax[3]; 160 | 161 | oaabbmin[0] = qMin(pos1.x, pos2.x); 162 | oaabbmin[1] = qMin(pos1.y, pos2.y); 163 | oaabbmin[2] = qMin(pos1.z, pos2.z); 164 | 165 | oaabbmax[0] = qMax(pos1.x, pos2.x); 166 | oaabbmax[1] = qMax(pos1.y, pos2.y); 167 | oaabbmax[2] = qMax(pos1.z, pos2.z); 168 | 169 | for (int i = 0; i < 3; ++i) { 170 | aabb[ i] = qMin(aabb[ i], oaabbmin[i]); 171 | aabb[3+i] = qMax(aabb[3+i], oaabbmax[i]); 172 | } 173 | 174 | //qDebug() << "getAABB()" << aabb[0] << aabb[1] << aabb[2] << aabb[3] << aabb[4] << aabb[5]; 175 | } 176 | } 177 | 178 | void View::drawObjects(bool simplified = false) { 179 | Q_UNUSED(simplified); 180 | 181 | if (lines.size() == 0) 182 | return; 183 | 184 | //int nbSteps = 600; 185 | //int nbSub = 50; 186 | 187 | // QMutexLocker locker(&mutex); 188 | 189 | /* 190 | if (simplified) { 191 | nbSteps = 60; 192 | nbSub = 2; 193 | }*/ 194 | 195 | // qDebug() << "lines.size: " << lines.size(); 196 | 197 | double step_size = 0.1; 198 | 199 | if (simplified) 200 | step_size = 0.75; 201 | 202 | double inv_ds = 1.0 / step_size; 203 | 204 | int skip_dyn = 1; 205 | double line_width = 3.0; 206 | 207 | if (lines.size() > 10000) { // skip some segments in big gcode files 208 | skip_dyn = 2; 209 | line_width = 2.0; // thinner lines 210 | } 211 | 212 | if (lines.size() > 100000) { 213 | skip_dyn = 4; 214 | line_width = 1.0; 215 | } 216 | 217 | int skip = (simplified) ? skip_dyn * 2 : skip_dyn; 218 | 219 | //glEnable(GL_MULTISAMPLE); 220 | //glEnable(GL_LINE_SMOOTH); 221 | //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); 222 | //glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST); 223 | 224 | /* 225 | glEnable(GL_BLEND); 226 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 227 | glHint (GL_LINE_SMOOTH_HINT, GL_DONT_CARE); 228 | */ 229 | 230 | for (unsigned long i = 0; i < lines.size(); i+=skip) { 231 | g2m::canonLine *l = lines[i]; 232 | if (l->isMotion()) { 233 | double move_length = l->length(); 234 | int n_samples = std::max((int)(move_length * inv_ds ), 2); 235 | double interval_size = move_length /(double)(n_samples-1); 236 | 237 | if (l->getMotionType() == TRAVERSE) { 238 | glColor3f(0.0, 128.0/255.0 , 0.0/255.0); 239 | glLineWidth(2); 240 | } else { 241 | glColor3f(255.0/255.0, 215.0/255.0, 94.0/255.0); 242 | glLineWidth(line_width); 243 | } 244 | 245 | glBegin(GL_LINES); 246 | for (double m = 0; m <= n_samples-1; m+=0.5) { 247 | Point pos1 = l->point( (double)(m) * interval_size); 248 | glVertex3f(pos1.x, pos1.y, pos1.z); 249 | Point pos2 = l->point( std::min(1.0, (double)(m+0.5) * interval_size)); 250 | glVertex3f(pos2.x, pos2.y, pos2.z); 251 | } 252 | glEnd(); 253 | } 254 | 255 | //glDisable(GL_BLEND); 256 | } 257 | 258 | glLineWidth(2); 259 | glColor3f(255.0, 0.0/255.0 , 0.0/255.0); 260 | glBegin(GL_LINES); 261 | 262 | // Top 263 | glVertex3f(aabb[0], aabb[1], aabb[2]); 264 | glVertex3f(aabb[3], aabb[1], aabb[2]); 265 | glVertex3f(aabb[3], aabb[1], aabb[2]); 266 | glVertex3f(aabb[3], aabb[1], aabb[5]); 267 | glVertex3f(aabb[3], aabb[1], aabb[5]); 268 | glVertex3f(aabb[0], aabb[1], aabb[5]); 269 | glVertex3f(aabb[0], aabb[1], aabb[5]); 270 | glVertex3f(aabb[0], aabb[1], aabb[2]); 271 | 272 | // Bottom 273 | glVertex3f(aabb[0], aabb[4], aabb[2]); 274 | glVertex3f(aabb[3], aabb[4], aabb[2]); 275 | glVertex3f(aabb[3], aabb[4], aabb[2]); 276 | glVertex3f(aabb[3], aabb[4], aabb[5]); 277 | glVertex3f(aabb[3], aabb[4], aabb[5]); 278 | glVertex3f(aabb[0], aabb[4], aabb[5]); 279 | glVertex3f(aabb[0], aabb[4], aabb[5]); 280 | glVertex3f(aabb[0], aabb[4], aabb[2]); 281 | 282 | // Sides 283 | glVertex3f(aabb[0], aabb[1], aabb[2]); 284 | glVertex3f(aabb[0], aabb[4], aabb[2]); 285 | glVertex3f(aabb[3], aabb[1], aabb[2]); 286 | glVertex3f(aabb[3], aabb[4], aabb[2]); 287 | glVertex3f(aabb[3], aabb[1], aabb[5]); 288 | glVertex3f(aabb[3], aabb[4], aabb[5]); 289 | glVertex3f(aabb[0], aabb[1], aabb[5]); 290 | glVertex3f(aabb[0], aabb[4], aabb[5]); 291 | 292 | glEnd(); 293 | 294 | Vec qmin, qmax; 295 | qmin.x = aabb[0]; 296 | qmin.y = aabb[1]; 297 | qmin.z = aabb[2]; 298 | 299 | qmax.x = aabb[3]; 300 | qmax.y = aabb[4]; 301 | qmax.z = aabb[5]; 302 | setSceneBoundingBox(qmin, qmax); 303 | 304 | Vec c((aabb[0] + aabb[3])/2, (aabb[1] + aabb[4])/2, (aabb[2] + aabb[5])/2); 305 | setSceneCenter(c); 306 | } 307 | 308 | void View::draw() { 309 | drawObjects(); 310 | } 311 | 312 | void View::fastDraw() { 313 | drawObjects(true); 314 | } 315 | 316 | void View::postDraw() { 317 | QGLViewer::postDraw(); 318 | } 319 | 320 | void View::update() { 321 | // qDebug() << "View::update"; 322 | 323 | if (_autoZoom) 324 | showEntireScene(); 325 | 326 | if (dirty) { 327 | dirty = false; 328 | updateGLViewer(); 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /mainwin.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwin.h" 2 | #include "ui_mainwin.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | MainWindow::MainWindow(QWidget *parent, bool fileMode, QString fileName) : 12 | QMainWindow(parent), 13 | ui(new Ui::MainWindow) 14 | { 15 | bFileMode = fileMode; 16 | 17 | setAttribute(Qt::WA_QuitOnClose); 18 | 19 | ui->setupUi(this); 20 | 21 | settings = new QSettings(); 22 | 23 | view = new View(this); 24 | 25 | setCentralWidget(view); 26 | 27 | g2m = new g2m::g2m(); // g-code interpreter 28 | 29 | connect( this, SIGNAL( setGcodeFile(QString) ), g2m, SLOT( setFile(QString)) ); 30 | connect( this, SIGNAL( setRS274(QString) ), g2m, SLOT( setInterp(QString)) ); 31 | connect( this, SIGNAL( setToolTable(QString) ), g2m, SLOT( setToolTable(QString)) ); 32 | connect( this, SIGNAL( interpret() ), g2m, SLOT( interpret_file() ) ); 33 | 34 | //connect( g2m, SIGNAL( debugMessage(QString) ), this, SLOT( debugMessage(QString) ) ); 35 | //connect( g2m, SIGNAL( gcodeLineMessage(QString) ), this, SLOT( appendGcodeLine(QString) ) ); 36 | //connect( g2m, SIGNAL( canonLineMessage(QString) ), this, SLOT( appendCanonLine(QString) ) ); 37 | 38 | connect( g2m, SIGNAL( signalCanonLine(canonLine*) ), view, SLOT( appendCanonLine(canonLine*) )); 39 | connect( g2m, SIGNAL( signalNCend() ), view, SLOT( update() ) ); 40 | connect( g2m, SIGNAL( signalError(QString) ), view, SLOT( update() ) ); 41 | 42 | connect( g2m, SIGNAL( signalError(QString) ), ui->stderror, SLOT( setPlainText(QString) ) ); 43 | 44 | connect(ui->gcode, SIGNAL(textChanged()), this, SLOT(changedGcode())); 45 | 46 | connect(ui->action_AutoZoom, SIGNAL(triggered()), this, SLOT(toggleAutoZoom())); 47 | connect(ui->actionZoom_In, SIGNAL(triggered()), this, SLOT(zoomIn())); 48 | connect(ui->actionZoom_out, SIGNAL(triggered()), this, SLOT(zoomOut())); 49 | 50 | //connect(ui->command, SIGNAL(keyPressed(QKeyEvent *)), view, SLOT(keyPressEvent(QKeyEvent *))); 51 | 52 | connect(ui->action_Issues, SIGNAL(triggered(bool)), this, SLOT(helpIssues())); 53 | connect(ui->action_Chat, SIGNAL(triggered(bool)), this, SLOT(helpChat())); 54 | 55 | home_dir = QStandardPaths::locate(QStandardPaths::HomeLocation, QString(), QStandardPaths::LocateDirectory); 56 | openFile = fileName; 57 | 58 | loadSettings(); 59 | 60 | setStyle(); 61 | 62 | if (bFileMode == false) { 63 | connect(ui->command, SIGNAL(textChanged()), this, SLOT(changedCommand())); 64 | QTimer::singleShot(0, this, SLOT(loadSettingsCommand())); 65 | ui->dockWidget->setHidden(false); 66 | ui->dockWidget_2->setHidden(false); 67 | } else { 68 | QTimer::singleShot(0, this, SLOT(loadGCodeFile())); 69 | ui->dockWidget->setHidden(true); 70 | ui->dockWidget_2->setHidden(true); 71 | } 72 | } 73 | 74 | MainWindow::~MainWindow() 75 | { 76 | 77 | } 78 | 79 | void MainWindow::loadGCodeFile() { 80 | if(openFile.length()) 81 | { 82 | if(openInViewer(openFile) == 0) { 83 | openInBrowser(openFile); 84 | } 85 | } 86 | } 87 | 88 | void MainWindow::toggleAutoZoom() { 89 | view->setAutoZoom(ui->action_AutoZoom->isChecked()); 90 | } 91 | 92 | void MainWindow::showFullScreen() 93 | { 94 | if( ui->action_showFullScreen->isChecked() ) 95 | showMaximized(); 96 | else 97 | showNormal(); 98 | } 99 | 100 | void MainWindow::zoomIn() { 101 | fontSize += 1; 102 | setStyle(); 103 | } 104 | 105 | void MainWindow::zoomOut() { 106 | fontSize -= 1; if (fontSize <1) fontSize = 1; 107 | setStyle(); 108 | } 109 | 110 | void MainWindow::setStyle() { 111 | setStyleSheet(QString("QWidget { font-size: %1pt; font-family: \"Courier\"; background-color: #00003B; color: #FFA700; font: bold }").arg(fontSize)); 112 | } 113 | 114 | void MainWindow::changedCommand() 115 | { 116 | QString str; 117 | 118 | openFile = ""; 119 | bFileMode = false; 120 | connect(ui->gcode, SIGNAL(textChanged()), this, SLOT(changedGcode())); 121 | str = "QGCoder :- "; 122 | setWindowTitle(str); 123 | parseCommand(); 124 | } 125 | 126 | void MainWindow::changedGcode() { 127 | 128 | if(bFileMode) 129 | return; 130 | 131 | if (ui->gcode->toPlainText().isEmpty()) 132 | { 133 | view->clear(); 134 | ui->stderror->setPlainText(""); 135 | return; 136 | } 137 | 138 | QFile f(gcodefile); 139 | if ( f.open( QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text) ) 140 | { 141 | QTextStream out(&f); 142 | out << ui->gcode->toPlainText(); 143 | f.close(); 144 | 145 | view->clear(); 146 | 147 | emit setRS274(rs274); 148 | emit setToolTable(tooltable); 149 | emit setGcodeFile(gcodefile); 150 | 151 | emit interpret(); 152 | } 153 | } 154 | 155 | void MainWindow::appendCanonLine(g2m::canonLine* l) { 156 | view->appendCanonLine(l); 157 | } 158 | 159 | void MainWindow::parseCommand() { 160 | QProcess sh; 161 | //sh.setProcessChannelMode(QProcess::MergedChannels); 162 | sh.setProcessChannelMode(QProcess::SeparateChannels); 163 | 164 | QFile f( "/tmp/gcoder.sh" ); 165 | if ( f.open( QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text) ) { 166 | QTextStream out(&f); 167 | out << ui->command->toPlainText(); 168 | f.close(); 169 | } 170 | 171 | if (!f.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ExeOwner|QFile::ReadGroup|QFile::ExeGroup|QFile::ReadOther|QFile::ExeOther)) { 172 | qDebug("XXX"); 173 | } 174 | 175 | sh.start("bash", QStringList() << "-c" << "timeout 1 /tmp/gcoder.sh"); 176 | 177 | if (!sh.waitForStarted()) { 178 | sh.close(); 179 | //sh.kill(); 180 | sh.waitForFinished(-1); 181 | return; // report error 182 | } 183 | 184 | if (!sh.waitForFinished(-1)) { 185 | // XXX http://stackoverflow.com/questions/10777147/terminate-an-ongoing-qprocess-that-is-running-inside-a-qthread 186 | } 187 | 188 | ui->gcode->setPlainText(sh.readAllStandardOutput()); 189 | ui->stderror->setPlainText(sh.readAllStandardError()); 190 | 191 | sh.close(); 192 | } 193 | 194 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 195 | 196 | void MainWindow::loadSettings() 197 | { 198 | 199 | settings->beginGroup("gui"); 200 | 201 | restoreGeometry(settings->value("geometry", saveGeometry() ).toByteArray()); 202 | restoreState(settings->value("state", saveState() ).toByteArray()); 203 | move(settings->value("pos", pos()).toPoint()); 204 | resize(settings->value("size", size()).toSize()); 205 | 206 | if (settings->value("maximized", isMaximized() ).toBool()) 207 | showMaximized(); 208 | ui->action_showFullScreen->setChecked(settings->value("maximized", isMaximized() ).toBool()); 209 | 210 | view->setAutoZoom(settings->value("autoZoom", view->autoZoom()).toBool()); 211 | ui->action_AutoZoom->setChecked(settings->value("autoZoom", view->autoZoom()).toBool()); 212 | 213 | fontSize = settings->value("fontsize", 12).toInt(); 214 | 215 | rs274 = settings->value("rs274", "").toString(); 216 | tooltable = settings->value("tooltable", "").toString(); 217 | gcodefile = settings->value("gcodefile", "").toString(); 218 | 219 | settings->endGroup(); 220 | // if any of these paths is not known, cannot work properly, so insist 221 | if(rs274.isEmpty() || tooltable.isEmpty() || gcodefile.isEmpty()) 222 | { 223 | int ret = 1; 224 | while(ret) 225 | ret = onSettings(); 226 | } 227 | } 228 | 229 | void MainWindow::loadSettingsCommand() { 230 | QString command("/bin/echo -en 'Hello, World!' | hf2gcode"); 231 | settings->beginGroup("gui"); 232 | ui->command->document()->setPlainText(settings->value("command", command).toString()); 233 | settings->endGroup(); 234 | } 235 | 236 | void MainWindow::saveSettings() { 237 | settings->beginGroup("gui"); 238 | 239 | settings->setValue("geometry", saveGeometry()); 240 | settings->setValue("state", saveState()); 241 | settings->setValue("maximized", isMaximized()); 242 | 243 | if ( !isMaximized() ) { 244 | settings->setValue("pos", pos()); 245 | settings->setValue("size", size()); 246 | } 247 | 248 | settings->setValue("command", ui->command->toPlainText()); 249 | 250 | settings->setValue("autoZoom", ui->action_AutoZoom->isChecked()); 251 | settings->setValue("fontsize", fontSize); 252 | 253 | settings->setValue("rs274", rs274); 254 | settings->setValue("tooltable", tooltable); 255 | settings->setValue("gcodefile", gcodefile); 256 | 257 | settings->endGroup(); 258 | } 259 | 260 | ///////////////////////////////////////////////////////////////////////////////////////////////// 261 | 262 | void MainWindow::closeEvent(QCloseEvent * event) 263 | { 264 | // qDebug() << "MainWindow::closeEvent"; 265 | saveSettings(); 266 | // ui.viewer->close(); 267 | 268 | // we are leaving it to QMainwindow to decide if to accept 269 | // event->accept(); 270 | 271 | QMainWindow::closeEvent(event); 272 | } 273 | 274 | //////////////////////////////////////////////////////////////////////////////////////////////// 275 | 276 | void MainWindow::onOpenFile() 277 | { 278 | QString filename; 279 | QString path = home_dir + "machinekit"; 280 | 281 | filename = QFileDialog::getOpenFileName(this, tr("Open G-code"), path, tr("GCode Files (*.ngc *.nc);; All files (*.*)")); 282 | if(filename.length()) 283 | { 284 | if(openInViewer(filename) == 0) 285 | openInBrowser(filename); 286 | } 287 | } 288 | 289 | int MainWindow::openInViewer(QString filename) 290 | { 291 | QFile fin(filename); 292 | QFile fout(gcodefile); 293 | QString str; 294 | 295 | if (fin.open(QFile::ReadOnly | QFile::Text) && ( fout.open( QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text) ) ) 296 | { 297 | QTextStream ints(&fin); 298 | QTextStream outts(&fout); 299 | while(!ints.atEnd()) 300 | outts << ints.readLine() << "\n"; 301 | fin.close(); 302 | fout.close(); 303 | 304 | view->clear(); 305 | bFileMode = true; 306 | 307 | emit setRS274(rs274); 308 | emit setToolTable(tooltable); 309 | emit setGcodeFile(gcodefile); 310 | 311 | emit interpret(); 312 | return 0; 313 | } 314 | else 315 | return -1; 316 | } 317 | 318 | void MainWindow::openInBrowser(QString filename) 319 | { 320 | QFile file(filename); 321 | QString str; 322 | 323 | if (file.open(QFile::ReadOnly | QFile::Text)) 324 | { 325 | str = "Loading file " + filename; 326 | ui->statusbar->showMessage(str, 5000); 327 | ui->gcode->clear(); 328 | 329 | QTextStream ts(&file); 330 | 331 | while( !ts.atEnd()) 332 | { 333 | str = ts.readLine(); 334 | ui->gcode->appendNewPlainText(str); 335 | } 336 | file.close(); 337 | 338 | str = "QGCoder :- " + filename; 339 | setWindowTitle(str); 340 | 341 | // ui->gcode->highlightLine(1); 342 | 343 | openFile = filename; 344 | bFileMode = true; 345 | } 346 | else 347 | { 348 | str = "Error loading file " + filename ; 349 | ui->statusbar->showMessage(str, 5000); 350 | } 351 | } 352 | 353 | 354 | //////////////////////////////////////////////////////////////////////////////////////////////////// 355 | 356 | void MainWindow::onSaveAs() 357 | { 358 | QString fileName = QFileDialog::getSaveFileName(this, tr("Save G-code (As)"), openFile, tr("G-code Files (*.ngc *.nc);; All files (*.*)")); 359 | saveInBrowser(fileName); 360 | } 361 | 362 | 363 | 364 | int MainWindow::saveInBrowser(QString& filename) 365 | { 366 | QFile file(filename); 367 | QString str; 368 | 369 | if (file.open(QIODevice::ReadWrite | QIODevice::Text)) 370 | { 371 | QTextStream out(&file); 372 | out << ui->gcode->toPlainText(); 373 | file.close(); 374 | 375 | str = "QGCoder:- " + filename; 376 | setWindowTitle(str); 377 | return 0; 378 | } 379 | else 380 | { 381 | str = "Error saving file " + filename ; 382 | ui->statusbar->showMessage(str, 5000); 383 | return -1; 384 | } 385 | } 386 | ///////////////////////////////////////////////////////////////////////////////////////////////////// 387 | 388 | int MainWindow::onSettings() 389 | { 390 | 391 | SettingsDialog *dlg = new SettingsDialog(this, home_dir); 392 | 393 | dlg->setValues(rs274, tooltable, gcodefile); 394 | 395 | dlg->exec(); 396 | 397 | if(dlg->result()) 398 | { 399 | rs274 = dlg->rs274; 400 | tooltable = dlg->tooltable; 401 | gcodefile = dlg->gcodefile; 402 | } 403 | 404 | if(rs274.isEmpty() || tooltable.isEmpty() || gcodefile.isEmpty()) 405 | return 1; 406 | else 407 | return 0; 408 | } 409 | 410 | //////////////////////////////////////////////////////////////////////////////////////////////////////////// 411 | 412 | void MainWindow::helpIssues() { 413 | QDesktopServices::openUrl(QUrl("https://github.com/QGCoder/qgcoder/issues")); 414 | } 415 | 416 | void MainWindow::helpChat() { 417 | QDesktopServices::openUrl(QUrl("https://gitter.im/QGCoder/qgcoder")); 418 | } 419 | -------------------------------------------------------------------------------- /g2m/g2m.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * modified by Anders Wallin 2011, anders.e.e.wallin@gmail.com * 5 | * modified by Kazuyasu Hamada 2015, k-hamada@gifu-u.ac.jp * 6 | * * 7 | * This program is free software; you can redistribute it and/or modify * 8 | * it under the terms of the GNU General Public License as published by * 9 | * the Free Software Foundation; either version 2 of the License, or * 10 | * (at your option) any later version. * 11 | * * 12 | * This program is distributed in the hope that it will be useful, * 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | * GNU General Public License for more details. * 16 | * * 17 | * You should have received a copy of the GNU General Public License * 18 | * along with this program; if not, write to the * 19 | * Free Software Foundation, Inc., * 20 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 21 | ***************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | 42 | #include "g2m.hpp" 43 | #include "nanotimer.hpp" 44 | #include "machineStatus.hpp" 45 | 46 | namespace g2m { 47 | 48 | void g2m::interpret_file() { 49 | lineVector.clear(); 50 | nanotimer timer; 51 | timer.start(); 52 | gcode_lines = 0; 53 | 54 | if ( file.endsWith(".ngc") ) { 55 | // push g-code lines to ui: 56 | QFile fileHandle( file ); 57 | QString gline; 58 | QString glinebuffer; 59 | QString tempFile = "/tmp/cutsim.temp"; 60 | QFile tempFileHandle( tempFile ); 61 | if ( !tempFileHandle.open(QIODevice::ReadWrite | QIODevice::Text)) 62 | return; 63 | 64 | if ( fileHandle.open( QIODevice::ReadOnly | QIODevice::Text) ) { 65 | // file opened successfully 66 | QTextStream t( &fileHandle ); // use a text stream 67 | QTextStream out( &tempFileHandle ); 68 | // until end of file... 69 | while ( !t.atEnd() ) { 70 | // read and parse the command line 71 | gline = t.readLine(); // line of text excluding '\n' 72 | glinebuffer += gline + '\n'; 73 | out << "(Gcode Line No." << gcode_lines << ")\n"; 74 | out << gline + '\n'; 75 | gcode_lines++; 76 | } 77 | fileHandle.close(); 78 | tempFileHandle.close(); 79 | } 80 | 81 | while (glinebuffer.right(1) == "\n") 82 | glinebuffer.remove(glinebuffer.length() - 1, 1); // remove last '\n' s 83 | 84 | emit gcodeLineMessage(glinebuffer); 85 | 86 | emit debugMessage( tr("g2m: interpreting %1").arg(file) ); 87 | //interpret(); // reads from file 88 | interpret2(tempFileHandle.fileName()); 89 | } else if (file.endsWith(".canon")) { //just process each line 90 | if (!chooseToolTable()) { 91 | infoMsg("Can't find tool table. Aborting."); 92 | emit debugMessage("Can't find tool table. Aborting."); 93 | return; 94 | } 95 | 96 | std::ifstream inFile(file.toLatin1()); 97 | std::string sLine; 98 | QString sLinebuffer; 99 | 100 | while(std::getline(inFile, sLine)) { 101 | if (sLine.length() > 1) { //helps to prevent segfault in canonLine::cmdMatch() 102 | sLinebuffer += sLine.c_str() + QString("\n"); 103 | processCanonLine(sLine); // requires no interpret() 104 | } 105 | } 106 | emit canonLineMessage(sLinebuffer); 107 | } else { 108 | emit debugMessage( tr("File name must end with .ngc or .canon!") ); 109 | return; 110 | } 111 | 112 | double e = timer.getElapsedS(); 113 | emit debugMessage( tr("g2m: Total time to process that file: ") + timer.humanreadable(e) ) ; 114 | //std::cout << "Total time to process that file: " << timer.humanreadable(e).toStdString() << std::endl; 115 | lineVector.clear(); 116 | } 117 | 118 | ///ask for a tool table, even if one is configured - user may wish to change it 119 | bool g2m::chooseToolTable() { 120 | if (!QFileInfo(tooltable).exists()){ 121 | infoMsg(" cannot find tooltable! "); 122 | emit debugMessage(" cannot find tooltable! "); 123 | return false; 124 | } 125 | return true; 126 | } 127 | 128 | /// set the tooltable and start interpreting input from stdin. called from interpret() 129 | bool g2m::startInterp(QProcess &tc) { 130 | if (!chooseToolTable()) 131 | return false; 132 | // run: rs274 file.ngc 133 | tc.start( interp , QStringList(file) ); 134 | tc.write("3\n"); // "read tool file" command to rs274 135 | tc.write(tooltable.toLatin1()); 136 | tc.write("\n"); // "enter" 137 | tc.write("1\n"); // "start interpreting" command to rs274 138 | return true; 139 | } 140 | 141 | /// called after "file" set in constructor 142 | void g2m::interpret() { 143 | //success = false; 144 | QProcess toCanon; 145 | bool foundEOF = false; // checked at the end 146 | 147 | if (!startInterp(toCanon)) // finds rs274, reads tooltable, start interpreter 148 | return; 149 | 150 | if (!toCanon.waitForReadyRead(1000) ) { 151 | if ( toCanon.state() == QProcess::NotRunning ){ 152 | infoMsg("Interpreter died. Bad tool table?"); 153 | } else 154 | infoMsg("Interpreter timed out for an unknown reason."); 155 | std::cout << "stderr: " << (const char*)toCanon.readAllStandardError() << std::endl; 156 | std::cout << "stdout: " << (const char*)toCanon.readAllStandardOutput() << std::endl; 157 | toCanon.close(); 158 | return; 159 | } 160 | 161 | // rs274 has now started correctly, and is ready to read ngc-file 162 | qint64 lineLength; 163 | char line[260]; 164 | int fails = 0; 165 | QString l; 166 | 167 | do { 168 | if (toCanon.canReadLine()) { 169 | lineLength = toCanon.readLine(line, sizeof(line)); // read one output line from rs274 170 | if (lineLength != -1 ) { 171 | l += line; 172 | foundEOF = processCanonLine(line); // line is a canon-line 173 | } else { //shouldn't get here! 174 | std::cout << " ERROR: lineLength= " << lineLength << " fails="<< fails << "\n"; 175 | fails++; 176 | } 177 | } else { 178 | std::cout << " ERROR: toCanon.canReadLine() fails="<< fails << "\n"; 179 | fails++; 180 | } 181 | toCanon.waitForReadyRead(); 182 | } while ( (fails < 1000) && 183 | ( (toCanon.canReadLine()) || 184 | ( toCanon.state() != QProcess::NotRunning ) ) ); 185 | 186 | emit canonLineMessage( l.left(l.size()-1) ); 187 | 188 | if (fails > 1) { 189 | if (fails < 1000) { 190 | infoMsg("Waited for interpreter over 1000 times."); 191 | } else { 192 | infoMsg("Waited 1000 seconds for interpreter. Giving up."); 193 | toCanon.close(); 194 | return; 195 | } 196 | } 197 | 198 | std::string s = (const char *)toCanon.readAllStandardError(); 199 | s.erase(0,s.find("executing")); 200 | if (s.size() > 10) { 201 | emit signalError(QString("Interpreter exited with error:\n" + 202 | QString::fromUtf8(s.substr(10).c_str()))); 203 | infoMsg("Interpreter exited with error:\n"+s.substr(10)); 204 | return; 205 | } 206 | 207 | if (!foundEOF) { 208 | infoMsg("Warning: file data not terminated correctly. If the file is terminated correctly, this indicates a problem interpreting the file."); 209 | } 210 | 211 | emit debugMessage( tr("g2m: read %1 lines of g-code which produced %2 canon-lines.").arg(gcode_lines).arg(lineVector.size()) ); 212 | 213 | toCanon.close(); 214 | return; 215 | } 216 | 217 | /// process a canon-line input string. this is a canon-string from rs274. 218 | /// call canonLineFactory to produce a canonLine and save it to lineVector 219 | bool g2m::processCanonLine(std::string l) { 220 | canonLine* cl; 221 | if (lineVector.size() == 0) { 222 | // no status exists, so make one up. 223 | cl = canonLine::canonLineFactory(l, machineStatus( initialPos, userOrigin )); 224 | } else { 225 | // use the last element status 226 | cl = canonLine::canonLineFactory(l, *(lineVector.back())->getStatus()); 227 | } 228 | emit signalCanonLine(cl); 229 | lineVector.push_back(cl); 230 | 231 | //if ( debug ) 232 | // std::cout << "Line " << cl->getLineNum() << "/N" << cl->getN() << std::endl; 233 | 234 | // return true when we reach end-of-program 235 | if (!cl->isMotion()) { 236 | if (cl->isNCend()) { 237 | emit signalNCend(); 238 | } 239 | return cl->isNCend(); 240 | } 241 | 242 | return false; 243 | } 244 | 245 | /// output information to std::cout 246 | void g2m::infoMsg(std::string s) { 247 | std::cout << s << std::endl; 248 | } 249 | 250 | /// set the tooltable and start interpreting input from stdin. called from interpret() 251 | bool g2m::startInterp2(QProcess &tc, QString tempFile) { 252 | if (!chooseToolTable()) 253 | return false; 254 | // run: rs274 file.ngc 255 | 256 | // qDebug() << QStringList(tempFile); 257 | 258 | tc.start( interp , QStringList(tempFile) ); 259 | 260 | tc.write("3\n"); // "read tool file" command to rs274 261 | 262 | // qDebug() << tooltable.toLatin1(); 263 | 264 | tc.write(tooltable.toLatin1()); 265 | tc.write("\n"); // "enter" 266 | tc.write("1\n"); // "start interpreting" command to rs274 267 | return true; 268 | } 269 | 270 | /// called after "file" set in constructor 271 | void g2m::interpret2(QString tempFile) { 272 | //success = false; 273 | QProcess toCanon; 274 | bool foundEOF = false; // checked at the end 275 | 276 | if (!startInterp2(toCanon, tempFile)) // finds rs274, reads tooltable, start interpreter 277 | return; 278 | 279 | if (!toCanon.waitForReadyRead(1000) ) { 280 | if ( toCanon.state() == QProcess::NotRunning ){ 281 | infoMsg("Interpreter died. Bad tool table?"); 282 | emit debugMessage("Interpreter died. Bad tool table?"); 283 | } else { 284 | infoMsg("Interpreter timed out for an unknown reason."); 285 | emit debugMessage("Interpreter timed out for an unknown reason."); 286 | } 287 | //std::cout << "stderr: " << (const char*)toCanon.readAllStandardError() << std::endl; 288 | //std::cout << "stdout: " << (const char*)toCanon.readAllStandardOutput() << std::endl; 289 | toCanon.close(); 290 | return; 291 | } 292 | 293 | // rs274 has now started correctly, and is ready to read ngc-file 294 | qint64 lineLength; 295 | char line[260]; 296 | int fails = 0; 297 | QString l; 298 | QString cmt; 299 | 300 | do { 301 | if (toCanon.canReadLine()) { 302 | lineLength = toCanon.readLine(line, sizeof(line)); // read one output line from rs274 303 | if (lineLength != -1 ) { 304 | cmt = line; 305 | if (cmt.contains("COMMENT(\"Gcode Line No.")) 306 | total_gcode_lines++; 307 | else { 308 | l += line; 309 | foundEOF = processCanonLine(line); // line is a canon-line 310 | //lineTable.push_back(total_gcode_lines); 311 | } 312 | } else { //shouldn't get here! 313 | //std::cout << " ERROR: lineLength= " << lineLength << " fails="<< fails << "\n"; 314 | fails++; 315 | } 316 | } else { 317 | //std::cout << " ERROR: toCanon.canReadLine() fails="<< fails << "\n"; 318 | fails++; 319 | } 320 | toCanon.waitForReadyRead(); 321 | } while ( (fails < 1000) && 322 | ( (toCanon.canReadLine()) || 323 | ( toCanon.state() != QProcess::NotRunning ) ) ); 324 | 325 | emit canonLineMessage( l.left(l.size()-1) ); 326 | 327 | if (fails > 1) { 328 | if (fails < 1000) { 329 | infoMsg("Waited for interpreter over 1000 times."); 330 | emit debugMessage("Waited for interpreter over 1000 times."); 331 | } else { 332 | infoMsg("Waited 1000 seconds for interpreter. Giving up."); 333 | emit debugMessage("Waited 1000 seconds for interpreter. Giving up."); 334 | toCanon.close(); 335 | return; 336 | } 337 | } 338 | 339 | std::string s = (const char *)toCanon.readAllStandardError(); 340 | s.erase(0,s.find("executing")); 341 | if (s.size() > 10) { 342 | infoMsg("Interpreter exited with error:\n"+s.substr(10)); 343 | emit debugMessage(("Interpreter exited with error:\n"+s.substr(10)).c_str()); 344 | return; 345 | } 346 | 347 | if (!foundEOF) { 348 | infoMsg("Warning: file data not terminated correctly. If the file is terminated correctly, this indicates a problem interpreting the file."); 349 | emit debugMessage("Warning: file data not terminated correctly. If the file is terminated correctly, this indicates a problem interpreting the file."); 350 | } 351 | 352 | emit debugMessage( tr("g2m: read %1 lines of g-code which produced %2 canon-lines.").arg(gcode_lines).arg(lineVector.size()) ); 353 | return; 354 | } 355 | 356 | } // end namespace 357 | -------------------------------------------------------------------------------- /mainwin.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1146 10 | 626 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | qgcoder 21 | 22 | 23 | 24 | :/icons/qgcoder.png:/icons/qgcoder.png 25 | 26 | 27 | QTabWidget::Rounded 28 | 29 | 30 | 31 | 32 | 0 33 | 34 | 35 | 0 36 | 37 | 38 | 0 39 | 40 | 41 | 0 42 | 43 | 44 | 0 45 | 46 | 47 | 48 | 49 | 50 | 51 | 0 52 | 0 53 | 1146 54 | 22 55 | 56 | 57 | 58 | 59 | &File 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | &View 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | &Help 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | &Edit 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | false 101 | 102 | 103 | G-code 104 | 105 | 106 | 1 107 | 108 | 109 | 110 | 111 | 0 112 | 113 | 114 | 0 115 | 116 | 117 | 0 118 | 119 | 120 | 0 121 | 122 | 123 | 0 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | true 134 | 135 | 136 | 137 | 524287 138 | 524287 139 | 140 | 141 | 142 | false 143 | 144 | 145 | Bash 146 | 147 | 148 | 8 149 | 150 | 151 | 152 | 153 | 0 154 | 155 | 156 | 0 157 | 158 | 159 | 0 160 | 161 | 162 | 0 163 | 164 | 165 | 0 166 | 167 | 168 | 169 | 170 | 171 | 0 172 | 0 173 | 174 | 175 | 176 | 177 | 16777215 178 | 16777215 179 | 180 | 181 | 182 | echo "Hello, World!" | hf2gcode 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | toolBar 192 | 193 | 194 | TopToolBarArea 195 | 196 | 197 | false 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | Standard error 211 | 212 | 213 | 8 214 | 215 | 216 | 217 | 218 | 0 219 | 220 | 221 | 0 222 | 223 | 224 | 0 225 | 226 | 227 | 0 228 | 229 | 230 | 0 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | .. 242 | 243 | 244 | &Quit 245 | 246 | 247 | Ctrl+Q 248 | 249 | 250 | Qt::ApplicationShortcut 251 | 252 | 253 | QAction::HighPriority 254 | 255 | 256 | 257 | 258 | true 259 | 260 | 261 | 262 | .. 263 | 264 | 265 | &Fullscreen 266 | 267 | 268 | Switch fullscreen on/off. 269 | 270 | 271 | F11 272 | 273 | 274 | Qt::ApplicationShortcut 275 | 276 | 277 | 278 | 279 | &About 280 | 281 | 282 | 283 | 284 | true 285 | 286 | 287 | 288 | .. 289 | 290 | 291 | Auto zoom (fit best) 292 | 293 | 294 | Automatic zoom (fit best) on/off. 295 | 296 | 297 | F11 298 | 299 | 300 | 301 | 302 | &Issues 303 | 304 | 305 | 306 | 307 | &Wiki 308 | 309 | 310 | 311 | 312 | &Chat 313 | 314 | 315 | 316 | 317 | 318 | .. 319 | 320 | 321 | Open G-code file... 322 | 323 | 324 | Open G-code (*.ngc) file. 325 | 326 | 327 | Ctrl+O 328 | 329 | 330 | 331 | 332 | &Settings.. 333 | 334 | 335 | F9 336 | 337 | 338 | 339 | 340 | 341 | .. 342 | 343 | 344 | &Save "%1" &As... 345 | 346 | 347 | Save G-code file as.. 348 | 349 | 350 | Ctrl+Shift+S 351 | 352 | 353 | 354 | 355 | 356 | .. 357 | 358 | 359 | &Save "%1" 360 | 361 | 362 | Save G-code. 363 | 364 | 365 | Ctrl+S 366 | 367 | 368 | 369 | 370 | Zoom in 371 | 372 | 373 | Ctrl++ 374 | 375 | 376 | 377 | 378 | Zoom out 379 | 380 | 381 | Ctrl+- 382 | 383 | 384 | 385 | 386 | 387 | QGCodeEditor 388 | QPlainTextEdit 389 |
QGCodeEditor/QGCodeEditor.h
390 |
391 |
392 | 393 | 394 | 395 | 396 | 397 | action_Quit 398 | triggered() 399 | MainWindow 400 | close() 401 | 402 | 403 | -1 404 | -1 405 | 406 | 407 | 399 408 | 299 409 | 410 | 411 | 412 | 413 | action_showFullScreen 414 | triggered() 415 | MainWindow 416 | showFullScreen() 417 | 418 | 419 | -1 420 | -1 421 | 422 | 423 | 399 424 | 299 425 | 426 | 427 | 428 | 429 | action_Open 430 | triggered() 431 | MainWindow 432 | onOpenFile() 433 | 434 | 435 | -1 436 | -1 437 | 438 | 439 | 285 440 | 299 441 | 442 | 443 | 444 | 445 | action_Settings 446 | triggered() 447 | MainWindow 448 | onSettings() 449 | 450 | 451 | -1 452 | -1 453 | 454 | 455 | 285 456 | 299 457 | 458 | 459 | 460 | 461 | action_Save_As 462 | triggered() 463 | MainWindow 464 | onSaveAs() 465 | 466 | 467 | -1 468 | -1 469 | 470 | 471 | 285 472 | 299 473 | 474 | 475 | 476 | 477 | 478 | onOpenFile() 479 | onSettings() 480 | onSaveAs() 481 | 482 |
483 | -------------------------------------------------------------------------------- /g2m/helicalMotion.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2010 by Mark Pictor * 3 | * mpictor@gmail.com * 4 | * modified by Kazuyasu Hamada 2015, k-hamada@gifu-u.ac.jp * 5 | * * 6 | * This program is free software; you can redistribute it and/or modify * 7 | * it under the terms of the GNU General Public License as published by * 8 | * the Free Software Foundation; either version 2 of the License, or * 9 | * (at your option) any later version. * 10 | * * 11 | * This program is distributed in the hope that it will be useful, * 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | * GNU General Public License for more details. * 15 | * * 16 | * You should have received a copy of the GNU General Public License * 17 | * along with this program; if not, write to the * 18 | * Free Software Foundation, Inc., * 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 20 | ***************************************************************************/ 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "calc_tolerance.hpp" 27 | 28 | #include "helicalMotion.hpp" 29 | #include "machineStatus.hpp" 30 | 31 | namespace g2m { 32 | 33 | #define CIRCLE_FUZZ (0.000001) // from libnml/posemath/posemath.h 34 | 35 | // example from cds.ngc: 36 | // 231 N2250 ARC_FEED(3.5884, 1.9116, 3.5000, 2.0000, -1, 1.8437, 0.0000, 0.0000, 0.0000) 37 | //tok: 0 1 2 3 4 5 6 7 8 9 10 11 38 | helicalMotion::helicalMotion(std::string canonL, machineStatus prevStatus): canonMotion(canonL,prevStatus) { 39 | // ( comments relate to XY-plane ) 40 | // see the rs274 spec, www.isd.mel.nist.gov/documents/kramer/RS274NGC_22.pdf or similar 41 | // If rotation is positive, the arc is traversed counterclockwise as viewed from the positive end of 42 | // the coordinate axis perpendicular to the currently selected plane. If rotation is negative, the 43 | // arc is traversed clockwise. 44 | 45 | x1 = tok2d(3); // first_end (x-coord of endpoint) 46 | y1 = tok2d(4); // second_end (y-coord of endpoint 47 | cx = tok2d(5); // first_axis (x-coord of centerpoint) 48 | cy = tok2d(6); // second_axis (y-coord of centerpoint) 49 | rot = tok2d(7); //rotation (ccw if rotation==1,cw if rotation==-1), for multipple turns we can have -2, +2, etc. 50 | z1 = tok2d(8); // z-coord of endpoint 51 | a = tok2d(9); // a 52 | b = tok2d(10); // b 53 | c = tok2d(11); // c 54 | start = status.getStartPose().loc + status.getOrigin().loc; 55 | #ifdef MULTI_AXIS 56 | // startDir = (status.getStartPose().dir + status.getOrigin().dir) * (SIGN * M_PI/180.0); 57 | startDir.x = (status.getStartPose().dir.x + status.getOrigin().dir.x) * (SIGN_A * M_PI/180.0); 58 | startDir.y = (status.getStartPose().dir.y + status.getOrigin().dir.y) * (SIGN_B * M_PI/180.0); 59 | startDir.z = (status.getStartPose().dir.z + status.getOrigin().dir.z) * (SIGN_C * M_PI/180.0); 60 | #endif 61 | 62 | /* 63 | switch (status.getPlane()) { 64 | case CANON_PLANE_XZ: 65 | status.setEndPose(Point(y1,z1,x1)); 66 | break; 67 | case CANON_PLANE_YZ: 68 | status.setEndPose(Point(z1,x1,y1)); 69 | break; 70 | case CANON_PLANE_XY: 71 | default: 72 | 73 | }*/ 74 | 75 | status.setMotionType(HELICAL); 76 | 77 | 78 | 79 | // code adapted from emc2: src/emc/rs274ngc/gcodemodule.cc 80 | // function rs274_arc_to_segments() 81 | double n[6]; // n=endpoint, 82 | //double o[6]; // o=origin/start-point 83 | // numbering of axes, depending on plane 84 | if ( status.getPlane() == CANON_PLANE_XY) { // XY-plane 85 | X=0; Y=1; Z=2; 86 | } else if (status.getPlane() == CANON_PLANE_YZ) { // YZ-plane 87 | X=2; Y=0; Z=1; 88 | } else if (status.getPlane() == CANON_PLANE_XZ) { 89 | X=1; Y=2; Z=0; // XZ-plane 90 | } 91 | n[X] = x1 + status.getOrigin().loc.x; // end-point, first-coord 92 | n[Y] = y1 + status.getOrigin().loc.y; // end-point, second-coord 93 | n[Z] = z1 + status.getOrigin().loc.z; // end-point, third-coord, i.e helix translation 94 | #ifdef MULTI_AXIS 95 | n[3] = (a + status.getOrigin().dir.x) * (SIGN_A * M_PI/180.0); 96 | n[4] = (b + status.getOrigin().dir.y) * (SIGN_B * M_PI/180.0); 97 | n[5] = (c + status.getOrigin().dir.z) * (SIGN_C * M_PI/180.0); 98 | #else 99 | n[3] = a; 100 | n[4] = b; 101 | n[5] = c; 102 | #endif 103 | 104 | #ifdef MULTI_AXIS 105 | status.setEndPose( Pose( Point( n[X], n[Y], n[Z]) - status.getOrigin().loc, Point( a, b, c ) ) ); 106 | endDir = Point( n[3], n[4], n[5] ); 107 | #else 108 | status.setEndPose( Point( n[X], n[Y], n[Z]) ); 109 | #endif 110 | end = status.getEndPose().loc; 111 | 112 | o[0] = start.x; 113 | o[1] = start.y; 114 | o[2] = start.z; 115 | #ifdef MULTI_AXIS 116 | o[3] = startDir.x; 117 | o[4] = startDir.y; 118 | o[5] = startDir.z; 119 | #else 120 | o[3] = 0; //FIXME 121 | o[4] = 0; //FIXME 122 | o[5] = 0; //FIXME 123 | #endif 124 | double theta1 = atan2( o[Y]-cy, o[X]-cx); // angle of vector from center to start 125 | double theta2 = atan2( n[Y]-cy, n[X]-cx); // angle of vector from center to end 126 | if(rot < 0) { 127 | while(theta2 - theta1 > -CIRCLE_FUZZ) 128 | theta2 -= 2 * M_PI; 129 | } else { 130 | while(theta2 - theta1 < CIRCLE_FUZZ) 131 | theta2 += 2 * M_PI; 132 | } 133 | // if multi-turn, add the right number of full circles 134 | if(rot < -1) 135 | theta2 += 2 * M_PI * (rot+1); 136 | if(rot > 1) 137 | theta2 += 2 * M_PI * (rot-1); 138 | // number of steps 139 | //int steps = std::max( 3, int(max_segments * fabs(theta1 - theta2) / M_PI) ); // max_segments per PI of rotation 140 | //double rsteps = 1. / steps; 141 | 142 | dtheta = theta2 - theta1; // the rotation angle for this helix 143 | 144 | // n is endpoint 145 | // o is startpoint 146 | // d is diff. x,y,z,a,b,c 147 | d[0]=0; d[1]=0; d[2]=0; 148 | d[3]=n[3]-o[3]; 149 | d[4]=n[4]-o[4]; 150 | d[5]=n[5]-o[5]; 151 | d[Z] = n[Z] - o[Z]; // helix-translation diff 152 | tx = o[X] - cx; // center to start 153 | ty = o[Y] - cy; // center to start 154 | radius = sqrt( tx*tx + ty*ty ); 155 | //double dc = cos(dtheta*rsteps); // delta-cos 156 | //double ds = sin(dtheta*rsteps); // delta-sin 157 | 158 | /* 159 | std::vector output; 160 | for(int i=0; i ledir) ? lsdir : ledir) * langle; 185 | return lhelical > langle ? lhelical : langle; 186 | #else 187 | // helix: x= a*cos(t) y=a*sin(t) z=c*t 188 | 189 | // d[Z] is rise during dtheta radians 190 | // so d[Z]/dtheta is rise/radian 191 | // and (d[Z]/dtheta) 192 | double c = d[Z]/dtheta; 193 | // c= rise in one turn divided by 2pi 194 | // t in [0,T] 195 | // helix length L =T*sqrt(a^2 + c^2) 196 | return fabs(dtheta)*sqrt(radius*radius + c*c); 197 | #endif 198 | } 199 | 200 | Point helicalMotion::point(double s) { 201 | // 0) relate s to t=[0...1] and theta=[0...dtheta] 202 | double t= s/this->length(); 203 | //XXX assert( t >= 0.0); assert( t <= 1.0 + CALC_TOLERANCE ); 204 | double theta = t*dtheta; 205 | // 1) rotate center-start vector 206 | double cos_t = cos(theta); 207 | double sin_t = sin(theta); 208 | double txr(tx), tyr(ty); 209 | rotate(txr,tyr,cos_t,sin_t); 210 | // 2) center + center-start 211 | double p[6]; // the output-point 212 | p[X] = cx + txr; 213 | p[Y] = cy + tyr; 214 | p[Z] = o[Z] + t*d[Z]; 215 | p[3] = o[3] + d[3] * t; 216 | p[4] = o[4] + d[4] * t; 217 | p[5] = o[5] + d[5] * t; 218 | return Point( p[0], p[1], p[2] ); 219 | } 220 | 221 | // rotate by cos/sin. from emc2 gcodemodule.cc 222 | void helicalMotion::rotate(double &x, double &y, double c, double s) { 223 | double tx = x * c - y * s; 224 | y = x * s + y * c; 225 | x = tx; 226 | } 227 | 228 | #ifdef MULTI_AXIS 229 | Point helicalMotion::angle(double s) { 230 | double t= s/this->length(); 231 | assert( t >= 0.0); assert( t <= 1.0 + CALC_TOLERANCE ); 232 | double p[6]; // the output-point 233 | p[3] = o[3] + d[3] * t; 234 | p[4] = o[4] + d[4] * t; 235 | p[5] = o[5] + d[5] * t; 236 | return Point( p[3], p[4], p[5] );; 237 | } 238 | #endif 239 | 240 | } // end namespace 241 | 242 | 243 | 244 | 245 | #ifdef EMC2_GCODEMODULE_RS274ARC_CODE 246 | static PyObject *rs274_arc_to_segments(PyObject *self, PyObject *args) { 247 | //PyObject *canon; 248 | double x1, y1, cx, cy, z1, a, b, c, u, v, w; 249 | double o[9], n[9], g5xoffset[9], g92offset[9]; 250 | int rot, plane; 251 | int X, Y, Z; 252 | double rotation_cos, rotation_sin; 253 | int max_segments = 128; 254 | 255 | 256 | 257 | 258 | if(!get_attr(canon, "plane", &plane)) return NULL; 259 | if(!get_attr(canon, "rotation_cos", &rotation_cos)) return NULL; // rotation-offsets 260 | if(!get_attr(canon, "rotation_sin", &rotation_sin)) return NULL; // rotation-offset 261 | 262 | 263 | if(!get_attr(canon, "g5x_offset_x", &g5xoffset[0])) return NULL; 264 | if(!get_attr(canon, "g5x_offset_y", &g5xoffset[1])) return NULL; 265 | if(!get_attr(canon, "g5x_offset_z", &g5xoffset[2])) return NULL; 266 | if(!get_attr(canon, "g5x_offset_a", &g5xoffset[3])) return NULL; 267 | if(!get_attr(canon, "g5x_offset_b", &g5xoffset[4])) return NULL; 268 | if(!get_attr(canon, "g5x_offset_c", &g5xoffset[5])) return NULL; 269 | if(!get_attr(canon, "g5x_offset_u", &g5xoffset[6])) return NULL; 270 | if(!get_attr(canon, "g5x_offset_v", &g5xoffset[7])) return NULL; 271 | if(!get_attr(canon, "g5x_offset_w", &g5xoffset[8])) return NULL; 272 | if(!get_attr(canon, "g92_offset_x", &g92offset[0])) return NULL; 273 | if(!get_attr(canon, "g92_offset_y", &g92offset[1])) return NULL; 274 | if(!get_attr(canon, "g92_offset_z", &g92offset[2])) return NULL; 275 | if(!get_attr(canon, "g92_offset_a", &g92offset[3])) return NULL; 276 | if(!get_attr(canon, "g92_offset_b", &g92offset[4])) return NULL; 277 | if(!get_attr(canon, "g92_offset_c", &g92offset[5])) return NULL; 278 | if(!get_attr(canon, "g92_offset_u", &g92offset[6])) return NULL; 279 | if(!get_attr(canon, "g92_offset_v", &g92offset[7])) return NULL; 280 | if(!get_attr(canon, "g92_offset_w", &g92offset[8])) return NULL; 281 | */ 282 | 283 | // numbering of axes, depending on plane 284 | if(plane == 1) { // XY-plane 285 | X=0; Y=1; Z=2; 286 | } else if(plane == 3) { // YZ-plane 287 | X=2; Y=0; Z=1; 288 | } else { 289 | X=1; Y=2; Z=0; // XZ-plane 290 | } 291 | n[X] = x1; // end-point, first-coord 292 | n[Y] = y1; // end-point, second-coord 293 | n[Z] = z1; // end-point, third-coord, i.e helix translation 294 | 295 | n[3] = a; 296 | n[4] = b; 297 | n[5] = c; 298 | 299 | n[6] = u; 300 | n[7] = v; 301 | n[8] = w; 302 | 303 | for(int ax=0; ax<9; ax++) 304 | o[ax] -= g5xoffset[ax]; // offset 305 | 306 | unrotate(o[0], o[1], rotation_cos, rotation_sin); // only rotates in XY (?) 307 | 308 | for(int ax=0; ax<9; ax++) 309 | o[ax] -= g92offset[ax]; // offset 310 | 311 | double theta1 = atan2( o[Y]-cy, o[X]-cx); // vector from center to start 312 | 313 | double theta2 = atan2( n[Y]-cy, n[X]-cx); // vector from center to end 314 | 315 | // "unwind" theta2 ? 316 | if(rot < 0) { // rot = -1 317 | while(theta2 - theta1 > -CIRCLE_FUZZ) 318 | theta2 -= 2 * M_PI; 319 | } else { // rot = 1 320 | while(theta2 - theta1 < CIRCLE_FUZZ) 321 | theta2 += 2 * M_PI; 322 | } 323 | 324 | // if multi-turn, add the right number of full circles 325 | if(rot < -1) 326 | theta2 += 2 * M_PI * (rot+1); 327 | if(rot > 1) 328 | theta2 += 2 * M_PI * (rot-1); 329 | 330 | // number of steps 331 | int steps = std::max( 3, int(max_segments * fabs(theta1 - theta2) / M_PI) ); // max_segments per PI of rotation 332 | double rsteps = 1. / steps; 333 | double dtheta = theta2 - theta1; // the rotation angle for this helix 334 | // n is endpoint 335 | // o is startpoint 336 | 337 | // d is diff. x,y,z,a,b,c,u,v,w 338 | // 0 1 2 3 4 5 6 7 339 | double d[9] = {0, 0, 0, n[4]-o[4], n[5]-o[5], n[6]-o[6], n[7]-o[7], n[8]-o[8]}; 340 | d[Z] = n[Z] - o[Z]; // helix-translation diff 341 | 342 | double tx = o[X] - cx; // center to start 343 | double ty = o[Y] - cy; // center to start 344 | double dc = cos(dtheta*rsteps); 345 | double ds = sin(dtheta*rsteps); 346 | 347 | PyObject *segs = PyList_New(steps); // list of output segments 348 | // generate segments: 349 | for(int i=0; i 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | --------------------------------------------------------------------------------