Johannes Kreutz 3 anni fa
parent
commit
220a30a3dd

+ 13 - 2
trackpoint-app/CMakeLists.txt

@@ -12,7 +12,11 @@ SET(CMAKE_CXX_STANDARD_REQUIRED True)
 SET(CMAKE_CXX_FLAGS_DEBUG "-O0")
 SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
 
+SET(CMAKE_AUTOUIC ON)
 SET(CMAKE_AUTOMOC ON)
+SET(CMAKE_AUTORCC ON)
+
+SET(CMAKE_INCLUDE_CURRENT_DIR ON)
 
 # OpenSceneGraph
 INCLUDE(thirdparty/openscenegraph.cmake)
@@ -29,12 +33,17 @@ find_package(Qt6 COMPONENTS Widgets OpenGLWidgets REQUIRED)
 
 # The executable we want to build
 ADD_EXECUTABLE(TrackpointApp
-  include/MainWindow.hpp
+  src/main.cpp
   src/MainWindow.cpp
+  include/MainWindow.hpp
+  gui/MainWindow.ui
   include/OSGWidget.hpp
   src/OSGWidget.cpp
   src/PickHandler.cpp
-  src/main.cpp
+  src/StoreHandler.cpp
+  src/TrackPoint.cpp
+  src/ThreeMFWriter.cpp
+  src/OpenScadRenderer.cpp
 )
 
 INCLUDE_DIRECTORIES(
@@ -42,12 +51,14 @@ INCLUDE_DIRECTORIES(
   ${${LIB3MF_PREFIX}_BINARY_DIR}/Autogenerated/Bindings/Cpp
   ${${JSON_PREFIX}_SOURCE_DIR}/include
   include
+  gui
 )
 
 TARGET_LINK_LIBRARIES(TrackpointApp
   osg osgViewer osgDB osgGA osgText osgUtil
   lib3mf
   nlohmann_json::nlohmann_json
+  Qt6::Core
   Qt6::Widgets
   Qt6::OpenGLWidgets
 )

+ 63 - 0
trackpoint-app/gui/MainWindow.ui

@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QHBoxLayout" name="horizontalLayout">
+    <item>
+     <widget class="QWidget" name="controlWidget" native="true">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+        <horstretch>1</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <widget class="QPushButton" name="pushButton">
+         <property name="text">
+          <string>PushButton</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+    <item>
+     <widget class="QWidget" name="sceneWidget" native="true">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+        <horstretch>2</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <layout class="QVBoxLayout" name="verticalLayout_2"/>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>800</width>
+     <height>30</height>
+    </rect>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 11 - 12
trackpoint-app/include/MainWindow.hpp

@@ -1,22 +1,21 @@
-#ifndef MainWindow_h__
-#define MainWindow_h__
+#pragma once
+
+#include "OSGWidget.hpp"
 
 #include <QMainWindow>
-#include <QMdiArea>
 
-class MainWindow : public QMainWindow
-{
+QT_BEGIN_NAMESPACE
+namespace Ui { class MainWindow; }
+QT_END_NAMESPACE
+
+class MainWindow: public QMainWindow {
   Q_OBJECT
 
 public:
-  MainWindow( QWidget* parent = 0, Qt::WindowFlags flags = {} );
+  MainWindow( QWidget* parent = nullptr );
   ~MainWindow();
 
-private slots:
-  void onCreateView();
-
 private:
-  QMdiArea* mdiArea_;
+  Ui::MainWindow* ui;
+  OSGWidget* osgWidget;
 };
-
-#endif

+ 16 - 17
trackpoint-app/include/OSGWidget.hpp

@@ -1,5 +1,8 @@
-#ifndef OSGWidget_h__
-#define OSGWidget_h__
+#pragma once
+
+#include "StoreHandler.hpp"
+#include "OpenScadRenderer.hpp"
+#include "ThreeMFWriter.hpp"
 
 #include <QPoint>
 #include <QOpenGLWidget>
@@ -9,33 +12,27 @@
 #include <osgViewer/GraphicsWindow>
 #include <osgViewer/CompositeViewer>
 
-namespace osgWidget
-{
+namespace osgWidget {
   //! The subclass of osgViewer::CompositeViewer we use
   /*!
    * This subclassing allows us to remove the annoying automatic
    * setting of the CPU affinity to core 0 by osgViewer::ViewerBase,
    * osgViewer::CompositeViewer's base class.
    */
-  class Viewer : public osgViewer::CompositeViewer
-  {
+  class Viewer: public osgViewer::CompositeViewer {
     public:
 	    virtual void setupThreading();
   };
 }
 
-class OSGWidget : public QOpenGLWidget
-{
+class OSGWidget : public QOpenGLWidget {
   Q_OBJECT
 
 public:
-  OSGWidget( QWidget* parent = 0,
-             Qt::WindowFlags f = {} );
-
+  OSGWidget(QWidget* parent = nullptr);
   virtual ~OSGWidget();
 
 protected:
-
   virtual void paintEvent( QPaintEvent* paintEvent );
   virtual void paintGL();
   virtual void resizeGL( int width, int height );
@@ -51,14 +48,16 @@ protected:
   virtual bool event( QEvent* event );
 
 private:
-
   virtual void onHome();
   virtual void onResize( int width, int height );
 
   osgGA::EventQueue* getEventQueue() const;
 
   osg::ref_ptr<osgViewer::GraphicsWindowEmbedded> graphicsWindow_;
-  osg::ref_ptr<osgWidget::Viewer> viewer_;
+  osg::ref_ptr<osgWidget::Viewer> _viewer;
+  osgViewer::View* _view;
+  osg::ref_ptr<osg::Group> _root;
+  osg::ref_ptr<osg::Node> _axesNode;
 
   QPoint selectionStart_;
   QPoint selectionEnd_;
@@ -66,7 +65,7 @@ private:
   bool selectionActive_;
   bool selectionFinished_;
 
-  void processSelection();
+  StoreHandler* _storeHandler;
+  OpenScadRenderer* _openScadRenderer;
+  ThreeMFWriter* _threeMFWriter;
 };
-
-#endif

+ 10 - 0
trackpoint-app/include/OpenScadRenderer.hpp

@@ -0,0 +1,10 @@
+#pragma once
+
+#include "TrackPoint.hpp"
+
+#include <vector>
+
+class OpenScadRenderer {
+public:
+  void render(std::vector<TrackPoint*> points);
+};

+ 22 - 11
trackpoint-app/include/PickHandler.hpp

@@ -1,19 +1,30 @@
-#ifndef PickHandler_h__
-#define PickHandler_h__
+#pragma once
+
+#include "StoreHandler.hpp"
+#include "OpenScadRenderer.hpp"
+#include "ThreeMFWriter.hpp"
 
 #include <osgGA/GUIEventHandler>
 
-class PickHandler : public osgGA::GUIEventHandler
-{
+class PickHandler: public osgGA::GUIEventHandler {
 public:
-  PickHandler( double devicePixelRatio = 1.0 );
-  virtual ~PickHandler();
+  PickHandler(StoreHandler* storeHandler, OpenScadRenderer* openScadRenderer, ThreeMFWriter* threeMFWriter, osg::ref_ptr<osg::Node> axesNode);
+  osg::Node* getOrCreateSelectionCylinder();
+  virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);
+  void moveTo(osg::Vec3f position);
+  void rotateToNormalVector(osg::Vec3f normal);
+  void setVisibility(bool mode);
 
-  virtual bool handle( const osgGA::GUIEventAdapter&  ea,
-                             osgGA::GUIActionAdapter& aa );
+protected:
+  osg::ref_ptr<osg::Switch> _selectionSwitch;
+  osg::ref_ptr<osg::MatrixTransform> _selectionTranslateGroup;
+  osg::ref_ptr<osg::MatrixTransform> _selectionRotateGroup;
+  osg::ref_ptr<osg::MatrixTransform> _selectionMoveToEndGroup;
+  bool isSelection = true;
 
 private:
-  double devicePixelRatio_;
+  StoreHandler* _storeHandler;
+  OpenScadRenderer* _openScadRenderer;
+  ThreeMFWriter* _threeMFWriter;
+  osg::ref_ptr<osg::Node> _axesNode;
 };
-
-#endif

+ 21 - 0
trackpoint-app/include/StoreHandler.hpp

@@ -0,0 +1,21 @@
+#pragma once
+
+#include "TrackPoint.hpp"
+
+#include <osg/ref_ptr>
+#include <osg/Group>
+#include <osg/Vec3>
+#include <vector>
+
+class StoreHandler {
+public:
+  void addTrackingPoint(osg::Vec3 point, osg::Vec3 normal);
+  StoreHandler(osg::ref_ptr<osg::Group> root);
+  std::vector<TrackPoint*> getPoints();
+
+protected:
+  std::vector<TrackPoint*> points;
+
+private:
+  osg::ref_ptr<osg::Group> _root;
+};

+ 17 - 0
trackpoint-app/include/ThreeMFWriter.hpp

@@ -0,0 +1,17 @@
+#pragma once
+
+#include "TrackPoint.hpp"
+
+#include "lib3mf_implicit.hpp"
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+class ThreeMFWriter {
+public:
+  ThreeMFWriter();
+  void writeTrackPoints(std::vector<TrackPoint*> points, std::string path);
+
+private:
+  Lib3MF::PWrapper _wrapper;
+};

+ 24 - 0
trackpoint-app/include/TrackPoint.hpp

@@ -0,0 +1,24 @@
+#pragma once
+
+#include <osg/ref_ptr>
+#include <osg/Vec3>
+#include <osg/MatrixTransform>
+
+class TrackPoint {
+public:
+  TrackPoint(osg::Vec3 point, osg::Vec3 normal);
+  osg::ref_ptr<osg::MatrixTransform> getUppermostRoot();
+  osg::Vec3 getTranslation();
+  osg::Vec3 getRotation();
+  osg::Vec3 getTrackPoint();
+
+protected:
+  osg::ref_ptr<osg::MatrixTransform> _translationGroup;
+  osg::ref_ptr<osg::MatrixTransform> _rotationGroup;
+  osg::ref_ptr<osg::MatrixTransform> _originFixGroup;
+
+private:
+  osg::Vec3 _origin;
+  osg::Vec3 _normal;
+  osg::Vec3 _trackOrigin;
+};

+ 6 - 22
trackpoint-app/src/MainWindow.cpp

@@ -1,30 +1,14 @@
 #include "MainWindow.hpp"
-#include "OSGWidget.hpp"
+#include "../gui/ui_MainWindow.h"
 
-#include <QDebug>
-#include <QMdiSubWindow>
-#include <QMenuBar>
+MainWindow::MainWindow(QWidget* parent): QMainWindow(parent), ui(new Ui::MainWindow) {
+  ui->setupUi(this);
 
-MainWindow::MainWindow( QWidget* parent, Qt::WindowFlags flags )
-  : QMainWindow( parent, flags ),
-    mdiArea_( new QMdiArea( this ) )
-{
-  QMenuBar* menuBar = this->menuBar();
-
-  QMenu* menu = menuBar->addMenu( "Test" );
-  menu->addAction( "Create view", this, SLOT( onCreateView() ) );
-
-  this->setCentralWidget( mdiArea_ );
+  osgWidget = new OSGWidget(nullptr);
+  ui->sceneWidget->layout()->addWidget(osgWidget);
 }
 
 MainWindow::~MainWindow()
 {
-}
-
-void MainWindow::onCreateView()
-{
-  OSGWidget* osgWidget     = new OSGWidget( this );
-  QMdiSubWindow* subWindow = mdiArea_->addSubWindow( osgWidget );
-
-  subWindow->show();
+  delete ui;
 }

+ 122 - 251
trackpoint-app/src/OSGWidget.cpp

@@ -1,25 +1,22 @@
 #include "OSGWidget.hpp"
 #include "PickHandler.hpp"
 
-#include <osg/Camera>
-
-#include <osg/DisplaySettings>
-#include <osg/Geode>
-#include <osg/Material>
-#include <osg/Shape>
+#include <osgViewer/Viewer>
 #include <osg/ShapeDrawable>
-#include <osg/StateSet>
-
-#include <osgDB/WriteFile>
-
-#include <osgGA/EventQueue>
+#include <osg/Geode>
+#include <osgDB/ReadFile>
+#include <osg/Group>
+#include <osg/Switch>
+#include <osg/MatrixTransform>
+#include <osg/Matrix>
 #include <osgGA/TrackballManipulator>
-
+#include <osgUtil/LineSegmentIntersector>
 #include <osgUtil/IntersectionVisitor>
-#include <osgUtil/PolytopeIntersector>
+#include <osg/PolygonMode>
 
-#include <osgViewer/View>
-#include <osgViewer/ViewerEventHandlers>
+#include <osgDB/WriteFile>
+#include <osg/Material>
+#include <osg/StateSet>
 
 #include <cassert>
 
@@ -31,182 +28,120 @@
 #include <QPainter>
 #include <QWheelEvent>
 
-namespace
-{
-
-#ifdef WITH_SELECTION_PROCESSING
-QRect makeRectangle( const QPoint& first, const QPoint& second )
-{
-  // Relative to the first point, the second point may be in either one of the
-  // four quadrants of an Euclidean coordinate system.
-  //
-  // We enumerate them in counter-clockwise order, starting from the lower-right
-  // quadrant that corresponds to the default case:
-  //
-  //            |
-  //       (3)  |  (4)
-  //            |
-  //     -------|-------
-  //            |
-  //       (2)  |  (1)
-  //            |
-
-  if( second.x() >= first.x() && second.y() >= first.y() )
-    return QRect( first, second );
-  else if( second.x() < first.x() && second.y() >= first.y() )
-    return QRect( QPoint( second.x(), first.y() ), QPoint( first.x(), second.y() ) );
-  else if( second.x() < first.x() && second.y() < first.y() )
-    return QRect( second, first );
-  else if( second.x() >= first.x() && second.y() < first.y() )
-    return QRect( QPoint( first.x(), second.y() ), QPoint( second.x(), first.y() ) );
-
-  // Should never reach that point...
-  return QRect();
-}
-#endif
-
-}
-
-namespace osgWidget
-{
-  void Viewer::setupThreading()
-  {
-    if( _threadingModel == SingleThreaded )
-    {
-      if(_threadsRunning)
+namespace osgWidget {
+  void Viewer::setupThreading() {
+    if (_threadingModel == SingleThreaded) {
+      if (_threadsRunning) {
         stopThreading();
-    }
-    else
-    {
-      if(!_threadsRunning)
+      }
+    } else {
+      if (!_threadsRunning) {
         startThreading();
+      }
     }
   }
 }
 
-OSGWidget::OSGWidget( QWidget* parent,
-                      Qt::WindowFlags f )
-  : QOpenGLWidget( parent,
-                   f )
-  , graphicsWindow_( new osgViewer::GraphicsWindowEmbedded( this->x(),
-                                                            this->y(),
-                                                            this->width(),
-                                                            this->height() ) )
-  , viewer_( new osgWidget::Viewer )
-  , selectionActive_( false )
-  , selectionFinished_( true )
+OSGWidget::OSGWidget(QWidget* parent): QOpenGLWidget(parent),
+  graphicsWindow_(new osgViewer::GraphicsWindowEmbedded(this->x(), this->y(), this->width(), this->height())),
+  _viewer(new osgWidget::Viewer),
+  selectionActive_(false),
+  selectionFinished_(true)
 {
-  osg::Sphere* sphere    = new osg::Sphere( osg::Vec3( 0.f, 0.f, 0.f ), 0.25f );
-  osg::ShapeDrawable* sd = new osg::ShapeDrawable( sphere );
-  sd->setColor( osg::Vec4( 1.f, 0.f, 0.f, 1.f ) );
-  sd->setName( "A nice sphere" );
+  this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+  // Root node of the scene
+  _root = new osg::Group;
 
-  osg::Geode* geode = new osg::Geode;
-  geode->addDrawable( sd );
+  // Create the camera
+  float aspectRatio = static_cast<float>(this->width()) / static_cast<float>(this->height());
+  auto pixelRatio = this->devicePixelRatio();
+  osg::Camera* camera = new osg::Camera;
+  camera->setViewport(0, 0, this->width() * pixelRatio, this->height() * pixelRatio);
+  camera->setClearColor(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
+  camera->setProjectionMatrixAsPerspective(30.0f, aspectRatio, 1.0f, 1000.0f);
+  camera->setGraphicsContext(graphicsWindow_);
+
+  // Create the viewer
+  _view = new osgViewer::View;
+  _view->setCamera(camera);
+  _view->setSceneData(_root);
+
+  // Add axesNode under root
+  _axesNode = osgDB::readNodeFile("../../testdata/testbutton.stl");
+  if (!_axesNode) {
+      printf("Origin node not loaded, model not found\n");
+  }
 
   // Set material for basic lighting and enable depth tests. Else, the sphere
   // will suffer from rendering errors.
   {
-    osg::StateSet* stateSet = geode->getOrCreateStateSet();
+    osg::StateSet* stateSet = _axesNode->getOrCreateStateSet();
     osg::Material* material = new osg::Material;
 
-    material->setColorMode( osg::Material::AMBIENT_AND_DIFFUSE );
+    material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
 
-    stateSet->setAttributeAndModes( material, osg::StateAttribute::ON );
-    stateSet->setMode( GL_DEPTH_TEST, osg::StateAttribute::ON );
+    stateSet->setAttributeAndModes(material, osg::StateAttribute::ON);
+    stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
   }
 
-  float aspectRatio = static_cast<float>( this->width() / 2 ) / static_cast<float>( this->height() );
-  auto pixelRatio   = this->devicePixelRatio();
+  _root->addChild(_axesNode);
 
-  osg::Camera* camera = new osg::Camera;
-  camera->setViewport( 0, 0, this->width() / 2 * pixelRatio, this->height() * pixelRatio );
-  camera->setClearColor( osg::Vec4( 0.f, 0.f, 1.f, 1.f ) );
-  camera->setProjectionMatrixAsPerspective( 30.f, aspectRatio, 1.f, 1000.f );
-  camera->setGraphicsContext( graphicsWindow_ );
-
-  osgViewer::View* view = new osgViewer::View;
-  view->setCamera( camera );
-  view->setSceneData( geode );
-  view->addEventHandler( new osgViewer::StatsHandler );
-#ifdef WITH_PICK_HANDLER
-  view->addEventHandler( new PickHandler( this->devicePixelRatio() ) );
-#endif
-
-  osgGA::TrackballManipulator* manipulator = new osgGA::TrackballManipulator;
-  manipulator->setAllowThrow( false );
-
-  view->setCameraManipulator( manipulator );
-
-  osg::Camera* sideCamera = new osg::Camera;
-  sideCamera->setViewport( this->width() /2 * pixelRatio, 0,
-                           this->width() /2 * pixelRatio, this->height() * pixelRatio );
-
-  sideCamera->setClearColor( osg::Vec4( 0.f, 0.f, 1.f, 1.f ) );
-  sideCamera->setProjectionMatrixAsPerspective( 30.f, aspectRatio, 1.f, 1000.f );
-  sideCamera->setGraphicsContext( graphicsWindow_ );
-
-  osgViewer::View* sideView = new osgViewer::View;
-  sideView->setCamera( sideCamera );
-  sideView->setSceneData( geode );
-  sideView->addEventHandler( new osgViewer::StatsHandler );
-  sideView->setCameraManipulator( new osgGA::TrackballManipulator );
-
-  viewer_->addView( view );
-  viewer_->addView( sideView );
-  viewer_->setThreadingModel( osgViewer::CompositeViewer::SingleThreaded );
-  viewer_->realize();
+  // Attach a manipulator (it's usually done for us when we use viewer.run())
+  osg::ref_ptr<osgGA::TrackballManipulator> tm = new osgGA::TrackballManipulator;
+  tm->setAllowThrow(false);
+  _view->setCameraManipulator(tm);
+
+  _viewer->addView(_view);
+  _viewer->setThreadingModel(osgViewer::CompositeViewer::SingleThreaded);
+  _viewer->realize();
+
+  _storeHandler = new StoreHandler(_root);
+  _openScadRenderer = new OpenScadRenderer();
+  _threeMFWriter = new ThreeMFWriter();
+
+  osg::ref_ptr<PickHandler> picker = new PickHandler(_storeHandler, _openScadRenderer, _threeMFWriter, _axesNode);
+  _root->addChild(picker->getOrCreateSelectionCylinder());
+
+  _view->addEventHandler(picker.get());
 
   // This ensures that the widget will receive keyboard events. This focus
   // policy is not set by default. The default, Qt::NoFocus, will result in
   // keyboard events that are ignored.
-  this->setFocusPolicy( Qt::StrongFocus );
-  this->setMinimumSize( 100, 100 );
+  this->setFocusPolicy(Qt::StrongFocus);
+  this->setMinimumSize(100, 100);
 
   // Ensures that the widget receives mouse move events even though no
   // mouse button has been pressed. We require this in order to let the
   // graphics window switch viewports properly.
-  this->setMouseTracking( true );
+  this->setMouseTracking(true);
 }
 
-OSGWidget::~OSGWidget()
-{
+OSGWidget::~OSGWidget() {
 }
 
-void OSGWidget::paintEvent( QPaintEvent* /* paintEvent */ )
-{
+void OSGWidget::paintEvent(QPaintEvent*) {
   this->makeCurrent();
 
-  QPainter painter( this );
-  painter.setRenderHint( QPainter::Antialiasing );
+  QPainter painter(this);
+  painter.setRenderHint(QPainter::Antialiasing);
 
   this->paintGL();
 
-#ifdef WITH_SELECTION_PROCESSING
-  if( selectionActive_ && !selectionFinished_ )
-  {
-    painter.setPen( Qt::black );
-    painter.setBrush( Qt::transparent );
-    painter.drawRect( makeRectangle( selectionStart_, selectionEnd_ ) );
-  }
-#endif
-
   painter.end();
 
   this->doneCurrent();
 }
 
-void OSGWidget::paintGL()
-{
-  viewer_->frame();
+void OSGWidget::paintGL() {
+  _viewer->frame();
 }
 
-void OSGWidget::resizeGL( int width, int height )
-{
-  this->getEventQueue()->windowResize( this->x(), this->y(), width, height );
-  graphicsWindow_->resized( this->x(), this->y(), width, height );
+void OSGWidget::resizeGL(int width, int height) {
+  this->getEventQueue()->windowResize(this->x(), this->y(), width, height);
+  graphicsWindow_->resized(this->x(), this->y(), width, height);
 
-  this->onResize( width, height );
+  this->onResize(width, height);
 }
 
 void OSGWidget::keyPressEvent( QKeyEvent* event )
@@ -216,16 +151,13 @@ void OSGWidget::keyPressEvent( QKeyEvent* event )
 
   if( event->key() == Qt::Key_S )
   {
-#ifdef WITH_SELECTION_PROCESSING
-    selectionActive_ = !selectionActive_;
-#endif
 
     // Further processing is required for the statistics handler here, so we do
     // not return right away.
   }
   else if( event->key() == Qt::Key_D )
   {
-    osgDB::writeNodeFile( *viewer_->getView(0)->getSceneData(),
+    osgDB::writeNodeFile( *_viewer->getView(0)->getSceneData(),
                           "/tmp/sceneGraph.osg" );
 
     return;
@@ -252,7 +184,7 @@ void OSGWidget::mouseMoveEvent( QMouseEvent* event )
   // Note that we have to check the buttons mask in order to see whether the
   // left button has been pressed. A call to `button()` will only result in
   // `Qt::NoButton` for mouse move events.
-  if( selectionActive_ && event->buttons() & Qt::LeftButton )
+  /*if( selectionActive_ && event->buttons() & Qt::LeftButton )
   {
     selectionEnd_ = event->pos();
 
@@ -261,18 +193,18 @@ void OSGWidget::mouseMoveEvent( QMouseEvent* event )
     this->update();
   }
   else
-  {
+  {*/
     auto pixelRatio = this->devicePixelRatio();
 
     this->getEventQueue()->mouseMotion( static_cast<float>( event->x() * pixelRatio ),
                                         static_cast<float>( event->y() * pixelRatio ) );
-  }
+  //}
 }
 
 void OSGWidget::mousePressEvent( QMouseEvent* event )
 {
   // Selection processing
-  if( selectionActive_ && event->button() == Qt::LeftButton )
+  /*if( selectionActive_ && event->button() == Qt::LeftButton )
   {
     selectionStart_    = event->pos();
     selectionEnd_      = selectionStart_; // Deletes the old selection
@@ -281,7 +213,7 @@ void OSGWidget::mousePressEvent( QMouseEvent* event )
 
   // Normal processing
   else
-  {
+  {*/
     // 1 = left mouse button
     // 2 = middle mouse button
     // 3 = right mouse button
@@ -311,25 +243,23 @@ void OSGWidget::mousePressEvent( QMouseEvent* event )
     this->getEventQueue()->mouseButtonPress( static_cast<float>( event->x() * pixelRatio ),
                                              static_cast<float>( event->y() * pixelRatio ),
                                              button );
-    }
+    //}
 }
 
 void OSGWidget::mouseReleaseEvent(QMouseEvent* event)
 {
   // Selection processing: Store end position and obtain selected objects
   // through polytope intersection.
-  if( selectionActive_ && event->button() == Qt::LeftButton )
+  /*if( selectionActive_ && event->button() == Qt::LeftButton )
   {
     selectionEnd_      = event->pos();
     selectionFinished_ = true; // Will force the painter to stop drawing the
                                // selection rectangle
-
-    this->processSelection();
   }
 
   // Normal processing
   else
-  {
+  {*/
     // 1 = left mouse button
     // 2 = middle mouse button
     // 3 = right mouse button
@@ -359,7 +289,7 @@ void OSGWidget::mouseReleaseEvent(QMouseEvent* event)
     this->getEventQueue()->mouseButtonRelease( static_cast<float>( pixelRatio * event->x() ),
                                                static_cast<float>( pixelRatio * event->y() ),
                                                button );
-  }
+  //}
 }
 
 void OSGWidget::wheelEvent( QWheelEvent* event )
@@ -377,120 +307,61 @@ void OSGWidget::wheelEvent( QWheelEvent* event )
   this->getEventQueue()->mouseScroll( motion );
 }
 
-bool OSGWidget::event( QEvent* event )
-{
-  bool handled = QOpenGLWidget::event( event );
+bool OSGWidget::event(QEvent* event) {
+  bool handled = QOpenGLWidget::event(event);
 
   // This ensures that the OSG widget is always going to be repainted after the
   // user performed some interaction. Doing this in the event handler ensures
   // that we don't forget about some event and prevents duplicate code.
-  switch( event->type() )
-  {
-  case QEvent::KeyPress:
-  case QEvent::KeyRelease:
-  case QEvent::MouseButtonDblClick:
-  case QEvent::MouseButtonPress:
-  case QEvent::MouseButtonRelease:
-  case QEvent::MouseMove:
-  case QEvent::Wheel:
-    this->update();
-    break;
+  switch(event->type()) {
+    case QEvent::KeyPress:
+    case QEvent::KeyRelease:
+    case QEvent::MouseButtonDblClick:
+    case QEvent::MouseButtonPress:
+    case QEvent::MouseButtonRelease:
+    case QEvent::MouseMove:
+    case QEvent::Wheel:
+      this->update();
+      break;
+
+    case QEvent::Resize:
+      this->onResize(this->width(), this->height());
+      break;
 
-  default:
-    break;
+    default:
+      break;
   }
 
   return handled;
 }
 
-void OSGWidget::onHome()
-{
+void OSGWidget::onHome() {
   osgViewer::ViewerBase::Views views;
-  viewer_->getViews( views );
+  _viewer->getViews( views );
 
-  for( std::size_t i = 0; i < views.size(); i++ )
-  {
+  for(std::size_t i = 0; i < views.size(); i++) {
     osgViewer::View* view = views.at(i);
     view->home();
   }
 }
 
-void OSGWidget::onResize( int width, int height )
-{
+void OSGWidget::onResize(int width, int height) {
   std::vector<osg::Camera*> cameras;
-  viewer_->getCameras( cameras );
+  _viewer->getCameras(cameras);
 
-  assert( cameras.size() == 2 );
+  assert(cameras.size() == 1);
 
   auto pixelRatio = this->devicePixelRatio();
 
-  cameras[0]->setViewport( 0, 0, width / 2 * pixelRatio, height * pixelRatio );
-  cameras[1]->setViewport( width / 2 * pixelRatio, 0, width / 2 * pixelRatio, height * pixelRatio );
+  cameras[0]->setViewport(0, 0, width * pixelRatio, height * pixelRatio);
 }
 
-osgGA::EventQueue* OSGWidget::getEventQueue() const
-{
+osgGA::EventQueue* OSGWidget::getEventQueue() const {
   osgGA::EventQueue* eventQueue = graphicsWindow_->getEventQueue();
 
-  if( eventQueue )
+  if (eventQueue) {
     return eventQueue;
-  else
-    throw std::runtime_error( "Unable to obtain valid event queue");
-}
-
-void OSGWidget::processSelection()
-{
-#ifdef WITH_SELECTION_PROCESSING
-  QRect selectionRectangle = makeRectangle( selectionStart_, selectionEnd_ );
-  auto widgetHeight        = this->height();
-  auto pixelRatio          = this->devicePixelRatio();
-
-  double xMin = selectionRectangle.left();
-  double xMax = selectionRectangle.right();
-  double yMin = widgetHeight - selectionRectangle.bottom();
-  double yMax = widgetHeight - selectionRectangle.top();
-
-  xMin *= pixelRatio;
-  yMin *= pixelRatio;
-  xMax *= pixelRatio;
-  yMax *= pixelRatio;
-
-  osgUtil::PolytopeIntersector* polytopeIntersector
-      = new osgUtil::PolytopeIntersector( osgUtil::PolytopeIntersector::WINDOW,
-                                          xMin, yMin,
-                                          xMax, yMax );
-
-  // This limits the amount of intersections that are reported by the
-  // polytope intersector. Using this setting, a single drawable will
-  // appear at most once while calculating intersections. This is the
-  // preferred and expected behaviour.
-  polytopeIntersector->setIntersectionLimit( osgUtil::Intersector::LIMIT_ONE_PER_DRAWABLE );
-
-  osgUtil::IntersectionVisitor iv( polytopeIntersector );
-
-  for( unsigned int viewIndex = 0; viewIndex < viewer_->getNumViews(); viewIndex++ )
-  {
-    qDebug() << "View index:" << viewIndex;
-
-    osgViewer::View* view = viewer_->getView( viewIndex );
-
-    if( !view )
-      throw std::runtime_error( "Unable to obtain valid view for selection processing" );
-
-    osg::Camera* camera = view->getCamera();
-
-    if( !camera )
-      throw std::runtime_error( "Unable to obtain valid camera for selection processing" );
-
-    camera->accept( iv );
-
-    if( !polytopeIntersector->containsIntersections() )
-      continue;
-
-    auto intersections = polytopeIntersector->getIntersections();
-
-    for( auto&& intersection : intersections )
-      qDebug() << "Selected a drawable:" << QString::fromStdString( intersection.drawable->getName() );
+  } else {
+    throw std::runtime_error("Unable to obtain valid event queue");
   }
-#endif
 }

+ 24 - 0
trackpoint-app/src/OpenScadRenderer.cpp

@@ -0,0 +1,24 @@
+#include "OpenScadRenderer.hpp"
+
+#include <iostream>
+#include <fstream>
+
+const char* openScadBase =
+  "$fn = 100;\n"
+  "module optiTrackPointBase(translation, rotation) {\n"
+  "translate(translation) rotate(rotation) cylinder(10, 1, 1, false);\n"
+  "}\n";
+
+void OpenScadRenderer::render(std::vector<TrackPoint*> points) {
+  std::ofstream scadFile;
+  scadFile.open("/tmp/output.scad");
+  scadFile << openScadBase;
+  scadFile << "import(\"testbutton.stl\");\n";
+  for (TrackPoint* point: points) {
+    osg::Vec3 translation = point->getTranslation();
+    osg::Vec3 rotation = point->getRotation();
+    scadFile << "optiTrackPointBase([" << translation.x() << "," << translation.y() << "," << translation.z() << "], [" << rotation.x() << "," << rotation.y() << "," << rotation.z() << "]);\n";
+  }
+  scadFile.close();
+  system("openscad -o /tmp/output.3mf /tmp/output.scad");
+}

+ 93 - 33
trackpoint-app/src/PickHandler.cpp

@@ -4,53 +4,113 @@
 
 #include <osgUtil/IntersectionVisitor>
 #include <osgUtil/LineSegmentIntersector>
+#include <osg/ShapeDrawable>
+#include <osg/MatrixTransform>
+#include <osg/Material>
+#include <osg/StateSet>
 
 #include <osgViewer/Viewer>
 
-#include <iostream>
-
-PickHandler::PickHandler( double devicePixelRatio )
-  : devicePixelRatio_( devicePixelRatio )
-{
+PickHandler::PickHandler(StoreHandler* storeHandler, OpenScadRenderer* openScadRenderer, ThreeMFWriter* threeMFWriter, osg::ref_ptr<osg::Node> axesNode) {
+  _storeHandler = storeHandler;
+  _openScadRenderer = openScadRenderer;
+  _threeMFWriter = threeMFWriter;
+  _axesNode = axesNode;
 }
 
-PickHandler::~PickHandler()
-{
-}
+osg::Node* PickHandler::getOrCreateSelectionCylinder() {
+  if (!_selectionTranslateGroup) {
+    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
+    osg::ref_ptr<osg::ShapeDrawable> cylinder = new osg::ShapeDrawable();
+    cylinder->setShape(new osg::Cylinder(osg::Vec3(0.0f, 0.0f, 0.0f), 1.0f, 10.0f));
+    cylinder->setColor(osg::Vec4(1.0f, 0.0f, 0.0f, 0.2f));
+    geode->addDrawable(cylinder.get());
 
-bool PickHandler::handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
-{
-  if( ea.getEventType() != osgGA::GUIEventAdapter::RELEASE &&
-      ea.getButton()    != osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON )
-  {
-    return false;
-  }
+    {
+      osg::StateSet* stateSet = geode->getOrCreateStateSet();
+      osg::Material* material = new osg::Material;
 
-  osgViewer::View* viewer = dynamic_cast<osgViewer::View*>( &aa );
+      material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
 
-  if( viewer )
-  {
-    osgUtil::LineSegmentIntersector* intersector
-        = new osgUtil::LineSegmentIntersector( osgUtil::Intersector::WINDOW, ea.getX() * devicePixelRatio_, ea.getY() * devicePixelRatio_ );
+      stateSet->setAttributeAndModes(material, osg::StateAttribute::ON);
+      stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
+    }
 
-    osgUtil::IntersectionVisitor iv( intersector );
+    _selectionRotateGroup = new osg::MatrixTransform;
+    _selectionRotateGroup->addChild(geode.get());
 
-    osg::Camera* camera = viewer->getCamera();
-    if( !camera )
-      return false;
+    _selectionMoveToEndGroup = new osg::MatrixTransform;
+    _selectionMoveToEndGroup->addChild(_selectionRotateGroup.get());
 
-    camera->accept( iv );
+    _selectionTranslateGroup = new osg::MatrixTransform;
+    _selectionTranslateGroup->addChild(_selectionMoveToEndGroup.get());
 
-    if( !intersector->containsIntersections() )
-      return false;
+    _selectionSwitch = new osg::Switch;
+    _selectionSwitch->addChild(_selectionTranslateGroup.get());
+  }
+  return _selectionSwitch.get();
+}
 
-    auto intersections = intersector->getIntersections();
+bool PickHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) {
+  if (ea.getModKeyMask()&osgGA::GUIEventAdapter::MODKEY_CTRL) {
+    isSelection = !isSelection;
+    setVisibility(false);
+  }
+  if (ea.getEventType() == osgGA::GUIEventAdapter::RELEASE && ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON && isSelection) {
+    osgViewer::View* viewer = dynamic_cast<osgViewer::View*>(&aa);
+    if (viewer) {
+      osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector = new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, ea.getX(), ea.getY());
+      osgUtil::IntersectionVisitor iv(intersector.get());
+      iv.setTraversalMask(~0x1);
+      viewer->getCamera()->accept(iv);
+      if (intersector->containsIntersections()) {
+        for (const osgUtil::LineSegmentIntersector::Intersection result: intersector->getIntersections()) {
+          if (std::find(result.nodePath.begin(), result.nodePath.end(), _axesNode) != result.nodePath.end()) {
+            moveTo(result.localIntersectionPoint);
+            rotateToNormalVector(result.localIntersectionNormal);
+            _storeHandler->addTrackingPoint(result.localIntersectionPoint, result.localIntersectionNormal);
+            _openScadRenderer->render(_storeHandler->getPoints());
+            _threeMFWriter->writeTrackPoints(_storeHandler->getPoints(), "/tmp/export.3mf");
+            break;
+          }
+        }
+      }
+    }
+  }
+  if (ea.getEventType() == osgGA::GUIEventAdapter::MOVE && isSelection) {
+    osgViewer::View* viewer = dynamic_cast<osgViewer::View*>(&aa);
+    if (viewer) {
+      osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector = new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, ea.getX(), ea.getY());
+      osgUtil::IntersectionVisitor iv(intersector.get());
+      iv.setTraversalMask(~0x1);
+      viewer->getCamera()->accept(iv);
+      if (intersector->containsIntersections()) {
+        for (const osgUtil::LineSegmentIntersector::Intersection result: intersector->getIntersections()) {
+          if (std::find(result.nodePath.begin(), result.nodePath.end(), _axesNode) != result.nodePath.end()) {
+            moveTo(result.localIntersectionPoint);
+            rotateToNormalVector(result.localIntersectionNormal);
+            setVisibility(true);
+            break;
+          }
+        }
+      } else {
+        setVisibility(false);
+      }
+    }
+  }
+  return false;
+}
 
-    std::cout << "Got " << intersections.size() << " intersections:\n";
+void PickHandler::moveTo(osg::Vec3f position) {
+  _selectionTranslateGroup->setMatrix(osg::Matrix::translate(position));
+}
 
-    for( auto&& intersection : intersections )
-      std::cout << "  - Local intersection point = " << intersection.localIntersectionPoint << "\n";
-  }
+void PickHandler::rotateToNormalVector(osg::Vec3f normal) {
+  _selectionRotateGroup->setMatrix(osg::Matrix::rotate(osg::Vec3f(0.0f, 0.0f, 1.0f), normal));
+  osg::Vec3f movementVector = normal.operator*(5.0f);
+  _selectionMoveToEndGroup->setMatrix(osg::Matrix::translate(movementVector));
+}
 
-  return true;
+void PickHandler::setVisibility(bool mode) {
+  _selectionSwitch->setValue(0, mode);
 }

+ 15 - 0
trackpoint-app/src/StoreHandler.cpp

@@ -0,0 +1,15 @@
+#include "StoreHandler.hpp"
+
+void StoreHandler::addTrackingPoint(osg::Vec3 point, osg::Vec3 normal) {
+  TrackPoint* trackPoint = new TrackPoint(point, normal);
+  points.push_back(trackPoint);
+  _root->addChild(trackPoint->getUppermostRoot());
+}
+
+StoreHandler::StoreHandler(osg::ref_ptr<osg::Group> root) {
+  _root = root;
+}
+
+std::vector<TrackPoint*> StoreHandler::getPoints() {
+  return points;
+}

+ 33 - 0
trackpoint-app/src/ThreeMFWriter.cpp

@@ -0,0 +1,33 @@
+#include "ThreeMFWriter.hpp"
+
+ThreeMFWriter::ThreeMFWriter() {
+  _wrapper = Lib3MF::CWrapper::loadLibrary();
+}
+
+void ThreeMFWriter::writeTrackPoints(std::vector<TrackPoint*> points, std::string path) {
+  // Load the file created by OpenSCAD
+  Lib3MF::PModel model = _wrapper->CreateModel();
+  Lib3MF::PReader reader = model->QueryReader("3mf");
+  reader->ReadFromFile("/tmp/output.3mf");
+
+  Lib3MF::PMetaDataGroup metaData = model->GetMetaDataGroup();
+
+  json trackpointData;
+  std::vector<std::vector<float>> pointsList;
+  for (TrackPoint* point: points) {
+    std::vector<float> pointData;
+    osg::Vec3 trackPoint = point->getTrackPoint();
+    pointData.push_back(trackPoint.x());
+    pointData.push_back(trackPoint.y());
+    pointData.push_back(trackPoint.z());
+    pointsList.push_back(pointData);
+  }
+  trackpointData["trackpoints"] = {
+    {"tracking-system", "optitrack"},
+    {"trackpoints", pointsList}
+  };
+  metaData->AddMetaData("tk-ar-tracking", "trackpoints", trackpointData.dump(), "string", true);
+
+  Lib3MF::PWriter writer = model->QueryWriter("3mf");
+  writer->WriteToFile(path);
+}

+ 84 - 0
trackpoint-app/src/TrackPoint.cpp

@@ -0,0 +1,84 @@
+#include "TrackPoint.hpp"
+
+#include <osg/Geode>
+#include <osg/ShapeDrawable>
+#include <osg/Material>
+#include <osg/StateSet>
+
+TrackPoint::TrackPoint(osg::Vec3 point, osg::Vec3 normal) {
+  osg::ref_ptr<osg::Geode> geode = new osg::Geode;
+  osg::ref_ptr<osg::ShapeDrawable> cylinder = new osg::ShapeDrawable();
+  cylinder->setShape(new osg::Cylinder(osg::Vec3(0.0f, 0.0f, 0.0f), 1.0f, 10.0f));
+  cylinder->setColor(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
+  geode->addDrawable(cylinder.get());
+
+  {
+    osg::StateSet* stateSet = geode->getOrCreateStateSet();
+    osg::Material* material = new osg::Material;
+
+    material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
+
+    stateSet->setAttributeAndModes(material, osg::StateAttribute::ON);
+    stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
+  }
+
+  _rotationGroup = new osg::MatrixTransform;
+  _rotationGroup->addChild(geode.get());
+  _rotationGroup->setMatrix(osg::Matrix::rotate(osg::Vec3f(0.0f, 0.0f, 1.0f), normal));
+
+  _originFixGroup = new osg::MatrixTransform;
+  _originFixGroup->addChild(_rotationGroup.get());
+  osg::Vec3f movementVector = normal.operator*(5.0f);
+  _originFixGroup->setMatrix(osg::Matrix::translate(movementVector));
+
+  _translationGroup = new osg::MatrixTransform;
+  _translationGroup->addChild(_originFixGroup.get());
+  _translationGroup->setMatrix(osg::Matrix::translate(point));
+
+  _origin = point;
+  _normal = normal;
+
+  osg::Vec3 shift = normal.operator*(10.0f);
+  _trackOrigin = shift.operator+(point);
+  printf("TrackPoint is at %lf %lf %lf\n", _trackOrigin.x(), _trackOrigin.y(), _trackOrigin.z());
+}
+
+osg::ref_ptr<osg::MatrixTransform> TrackPoint::getUppermostRoot() {
+  return _translationGroup.get();
+}
+
+osg::Vec3 TrackPoint::getTranslation() {
+  return _origin;
+}
+
+osg::Vec3 TrackPoint::getRotation() {
+  printf("YNorm: %lf %lf %lf\n", _normal.x(), _normal.y(), _normal.z());
+
+  osg::Vec3 start = osg::Vec3(0.0f, 0.0f, 1.0f);
+
+  // From https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
+  osg::Quat quat = osg::Quat(0.0f, 0.0f, 0.0f, 0.0f);
+  quat.makeRotate(start, _normal);
+
+  float sinr_cosp = 2 * (quat.w() * quat.x() + quat.y() * quat.z());
+  float cosr_cosp = 1 - 2 * (quat.x() * quat.x() + quat.y() * quat.y());
+  float xRotation = std::atan2(sinr_cosp, cosr_cosp) * 180.0 / M_PI;
+
+  float sinp = 2 * (quat.w() * quat.y() - quat.z() * quat.x());
+  float yRotation;
+  if (std::abs(sinp) >= 1) {
+    yRotation = std::copysign(M_PI / 2, sinp) * 180.0 / M_PI;
+  } else {
+    yRotation = std::asin(sinp) * 180.0 / M_PI;
+  }
+
+  float siny_cosp = 2 * (quat.w() * quat.z() + quat.x() * quat.y());
+  float cosy_cosp = 1 - 2 * (quat.y() * quat.y() + quat.z() * quat.z());
+  float zRotation = std::atan2(siny_cosp, cosy_cosp) * 180.0 / M_PI;
+
+  return osg::Vec3(xRotation, yRotation, zRotation);
+}
+
+osg::Vec3 TrackPoint::getTrackPoint() {
+  return _trackOrigin;
+}

+ 3 - 344
trackpoint-app/src/main.cpp

@@ -1,360 +1,19 @@
-/*#include <osgViewer/Viewer>
-#include <osg/ShapeDrawable>
-#include <osg/Geode>
-#include <osgDB/ReadFile>
-#include <osg/Group>
-#include <osg/Switch>
-#include <osg/MatrixTransform>
-#include <osg/Matrix>
-#include <osgGA/TrackballManipulator>
-#include <osgUtil/LineSegmentIntersector>
-#include <osgUtil/IntersectionVisitor>
-#include <osg/PolygonMode>
-#include <vector>
-#include <iostream>
-#include <fstream>
-#include <string>
-#include <math.h>
-#include "lib3mf_implicit.hpp"
-#include <nlohmann/json.hpp>*/
-
 #include <QApplication>
 #include <QSurfaceFormat>
 
 #include "MainWindow.hpp"
 
-/*using json = nlohmann::json;
-
-const char* openScadBase =
-  "$fn = 100;\n"
-  "module optiTrackPointBase(translation, rotation) {\n"
-  "translate(translation) rotate(rotation) cylinder(10, 1, 1, false);\n"
-  "}\n";
-
-class TrackPoint {
-public:
-  TrackPoint(osg::Vec3 point, osg::Vec3 normal);
-  osg::ref_ptr<osg::MatrixTransform> getUppermostRoot();
-  osg::Vec3 getTranslation();
-  osg::Vec3 getRotation();
-  osg::Vec3 getTrackPoint();
-
-protected:
-  osg::ref_ptr<osg::MatrixTransform> _translationGroup;
-  osg::ref_ptr<osg::MatrixTransform> _rotationGroup;
-  osg::ref_ptr<osg::MatrixTransform> _originFixGroup;
-
-private:
-  osg::Vec3 _origin;
-  osg::Vec3 _normal;
-  osg::Vec3 _trackOrigin;
-};
-
-TrackPoint::TrackPoint(osg::Vec3 point, osg::Vec3 normal) {
-  osg::ref_ptr<osg::Geode> geode = new osg::Geode;
-  osg::ref_ptr<osg::ShapeDrawable> cylinder = new osg::ShapeDrawable();
-  cylinder->setShape(new osg::Cylinder(osg::Vec3(0.0f, 0.0f, 0.0f), 1.0f, 10.0f));
-  cylinder->setColor(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
-  geode->addDrawable(cylinder.get());
-
-  _rotationGroup = new osg::MatrixTransform;
-  _rotationGroup->addChild(geode.get());
-  _rotationGroup->setMatrix(osg::Matrix::rotate(osg::Vec3f(0.0f, 0.0f, 1.0f), normal));
-
-  _originFixGroup = new osg::MatrixTransform;
-  _originFixGroup->addChild(_rotationGroup.get());
-  osg::Vec3f movementVector = normal.operator*(5.0f);
-  _originFixGroup->setMatrix(osg::Matrix::translate(movementVector));
-
-  _translationGroup = new osg::MatrixTransform;
-  _translationGroup->addChild(_originFixGroup.get());
-  _translationGroup->setMatrix(osg::Matrix::translate(point));
-
-  _origin = point;
-  _normal = normal;
-
-  osg::Vec3 shift = normal.operator*(10.0f);
-  _trackOrigin = shift.operator+(point);
-  printf("TrackPoint is at %lf %lf %lf\n", _trackOrigin.x(), _trackOrigin.y(), _trackOrigin.z());
-}
-
-osg::ref_ptr<osg::MatrixTransform> TrackPoint::getUppermostRoot() {
-  return _translationGroup.get();
-}
-
-osg::Vec3 TrackPoint::getTranslation() {
-  return _origin;
-}
-
-osg::Vec3 TrackPoint::getRotation() {
-  printf("YNorm: %lf %lf %lf\n", _normal.x(), _normal.y(), _normal.z());
-
-  osg::Vec3 start = osg::Vec3(0.0f, 0.0f, 1.0f);
-
-  // From https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
-  osg::Quat quat = osg::Quat(0.0f, 0.0f, 0.0f, 0.0f);
-  quat.makeRotate(start, _normal);
-
-  float sinr_cosp = 2 * (quat.w() * quat.x() + quat.y() * quat.z());
-  float cosr_cosp = 1 - 2 * (quat.x() * quat.x() + quat.y() * quat.y());
-  float xRotation = std::atan2(sinr_cosp, cosr_cosp) * 180.0 / M_PI;
-
-  float sinp = 2 * (quat.w() * quat.y() - quat.z() * quat.x());
-  float yRotation;
-  if (std::abs(sinp) >= 1) {
-    yRotation = std::copysign(M_PI / 2, sinp) * 180.0 / M_PI;
-  } else {
-    yRotation = std::asin(sinp) * 180.0 / M_PI;
-  }
-
-  float siny_cosp = 2 * (quat.w() * quat.z() + quat.x() * quat.y());
-  float cosy_cosp = 1 - 2 * (quat.y() * quat.y() + quat.z() * quat.z());
-  float zRotation = std::atan2(siny_cosp, cosy_cosp) * 180.0 / M_PI;
-
-  return osg::Vec3(xRotation, yRotation, zRotation);
-}
-
-osg::Vec3 TrackPoint::getTrackPoint() {
-  return _trackOrigin;
-}
-
-class ThreeMFWriter {
-public:
-  ThreeMFWriter();
-  void writeTrackPoints(std::vector<TrackPoint*> points, std::string path);
-
-private:
-  Lib3MF::PWrapper _wrapper;
-};
-
-ThreeMFWriter::ThreeMFWriter() {
-  _wrapper = Lib3MF::CWrapper::loadLibrary();
-}
-
-void ThreeMFWriter::writeTrackPoints(std::vector<TrackPoint*> points, std::string path) {
-  // Load the file created by OpenSCAD
-  Lib3MF::PModel model = _wrapper->CreateModel();
-  Lib3MF::PReader reader = model->QueryReader("3mf");
-  reader->ReadFromFile("/tmp/output.3mf");
-
-  Lib3MF::PMetaDataGroup metaData = model->GetMetaDataGroup();
-
-  json trackpointData;
-  std::vector<std::vector<float>> pointsList;
-  for (TrackPoint* point: points) {
-    std::vector<float> pointData;
-    osg::Vec3 trackPoint = point->getTrackPoint();
-    pointData.push_back(trackPoint.x());
-    pointData.push_back(trackPoint.y());
-    pointData.push_back(trackPoint.z());
-    pointsList.push_back(pointData);
-  }
-  trackpointData["trackpoints"] = {
-    {"tracking-system", "optitrack"},
-    {"trackpoints", pointsList}
-  };
-  metaData->AddMetaData("tk-ar-tracking", "trackpoints", trackpointData.dump(), "string", true);
-
-  Lib3MF::PWriter writer = model->QueryWriter("3mf");
-  writer->WriteToFile(path);
-}
-
-class OpenScadRenderer {
-public:
-  void render(std::vector<TrackPoint*> points);
-};
-
-void OpenScadRenderer::render(std::vector<TrackPoint*> points) {
-  std::ofstream scadFile;
-  scadFile.open("/tmp/output.scad");
-  scadFile << openScadBase;
-  scadFile << "import(\"testbutton.stl\");\n";
-  for (TrackPoint* point: points) {
-    osg::Vec3 translation = point->getTranslation();
-    osg::Vec3 rotation = point->getRotation();
-    scadFile << "optiTrackPointBase([" << translation.x() << "," << translation.y() << "," << translation.z() << "], [" << rotation.x() << "," << rotation.y() << "," << rotation.z() << "]);\n";
-  }
-  scadFile.close();
-  system("openscad -o /tmp/output.3mf /tmp/output.scad");
-}
-
-class StoreHandler {
-public:
-  void addTrackingPoint(osg::Vec3 point, osg::Vec3 normal);
-  StoreHandler(osg::ref_ptr<osg::Group> root);
-  std::vector<TrackPoint*> getPoints();
-
-protected:
-  std::vector<TrackPoint*> points;
-
-private:
-  osg::ref_ptr<osg::Group> _root;
-};
-
-void StoreHandler::addTrackingPoint(osg::Vec3 point, osg::Vec3 normal) {
-  TrackPoint* trackPoint = new TrackPoint(point, normal);
-  points.push_back(trackPoint);
-  _root->addChild(trackPoint->getUppermostRoot());
-}
-
-StoreHandler::StoreHandler(osg::ref_ptr<osg::Group> root) {
-  _root = root;
-}
-
-std::vector<TrackPoint*> StoreHandler::getPoints() {
-  return points;
-}
-
-StoreHandler* storeHandler;
-OpenScadRenderer* openScadRenderer;
-ThreeMFWriter* threeMFWriter;
-osg::ref_ptr<osg::Node> axesNode;
-
-class PickHandler: public osgGA::GUIEventHandler {
-public:
-  osg::Node* getOrCreateSelectionCylinder();
-  virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);
-  void moveTo(osg::Vec3f position);
-  void rotateToNormalVector(osg::Vec3f normal);
-  void setVisibility(bool mode);
-
-protected:
-  osg::ref_ptr<osg::Switch> _selectionSwitch;
-  osg::ref_ptr<osg::MatrixTransform> _selectionTranslateGroup;
-  osg::ref_ptr<osg::MatrixTransform> _selectionRotateGroup;
-  osg::ref_ptr<osg::MatrixTransform> _selectionMoveToEndGroup;
-  bool isSelection = false;
-};
-
-osg::Node* PickHandler::getOrCreateSelectionCylinder() {
-  if (!_selectionTranslateGroup) {
-    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
-    osg::ref_ptr<osg::ShapeDrawable> cylinder = new osg::ShapeDrawable();
-    cylinder->setShape(new osg::Cylinder(osg::Vec3(0.0f, 0.0f, 0.0f), 1.0f, 10.0f));
-    cylinder->setColor(osg::Vec4(1.0f, 0.0f, 0.0f, 0.2f));
-    geode->addDrawable(cylinder.get());
-
-    _selectionRotateGroup = new osg::MatrixTransform;
-    _selectionRotateGroup->addChild(geode.get());
-
-    _selectionMoveToEndGroup = new osg::MatrixTransform;
-    _selectionMoveToEndGroup->addChild(_selectionRotateGroup.get());
-
-    _selectionTranslateGroup = new osg::MatrixTransform;
-    _selectionTranslateGroup->addChild(_selectionMoveToEndGroup.get());
-
-    _selectionSwitch = new osg::Switch;
-    _selectionSwitch->addChild(_selectionTranslateGroup.get());
-  }
-  return _selectionSwitch.get();
-}
-
-bool PickHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) {
-  if (ea.getModKeyMask()&osgGA::GUIEventAdapter::MODKEY_CTRL) {
-    isSelection = !isSelection;
-    setVisibility(false);
-  }
-  if (ea.getEventType() == osgGA::GUIEventAdapter::RELEASE && ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON && isSelection) {
-    osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
-    if (viewer) {
-      osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector = new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, ea.getX(), ea.getY());
-      osgUtil::IntersectionVisitor iv(intersector.get());
-      iv.setTraversalMask(~0x1);
-      viewer->getCamera()->accept(iv);
-      if (intersector->containsIntersections()) {
-        for (const osgUtil::LineSegmentIntersector::Intersection result: intersector->getIntersections()) {
-          if (std::find(result.nodePath.begin(), result.nodePath.end(), axesNode) != result.nodePath.end()) {
-            moveTo(result.localIntersectionPoint);
-            rotateToNormalVector(result.localIntersectionNormal);
-            storeHandler->addTrackingPoint(result.localIntersectionPoint, result.localIntersectionNormal);
-            openScadRenderer->render(storeHandler->getPoints());
-            threeMFWriter->writeTrackPoints(storeHandler->getPoints(), "/tmp/export.3mf");
-            break;
-          }
-        }
-      }
-    }
-  }
-  if (ea.getEventType() == osgGA::GUIEventAdapter::MOVE && isSelection) {
-    osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
-    if (viewer) {
-      osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector = new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, ea.getX(), ea.getY());
-      osgUtil::IntersectionVisitor iv(intersector.get());
-      iv.setTraversalMask(~0x1);
-      viewer->getCamera()->accept(iv);
-      if (intersector->containsIntersections()) {
-        for (const osgUtil::LineSegmentIntersector::Intersection result: intersector->getIntersections()) {
-          if (std::find(result.nodePath.begin(), result.nodePath.end(), axesNode) != result.nodePath.end()) {
-            moveTo(result.localIntersectionPoint);
-            rotateToNormalVector(result.localIntersectionNormal);
-            setVisibility(true);
-            break;
-          }
-        }
-      } else {
-        setVisibility(false);
-      }
-    }
-  }
-  return false;
-}
-
-void PickHandler::moveTo(osg::Vec3f position) {
-  _selectionTranslateGroup->setMatrix(osg::Matrix::translate(position));
-}
-
-void PickHandler::rotateToNormalVector(osg::Vec3f normal) {
-  _selectionRotateGroup->setMatrix(osg::Matrix::rotate(osg::Vec3f(0.0f, 0.0f, 1.0f), normal));
-  osg::Vec3f movementVector = normal.operator*(5.0f);
-  _selectionMoveToEndGroup->setMatrix(osg::Matrix::translate(movementVector));
-}
-
-void PickHandler::setVisibility(bool mode) {
-  _selectionSwitch->setValue(0, mode);
-}*/
-
 int main(int argc, char** argv) {
-    /*// Root node of the scene
-    osg::ref_ptr<osg::Group> root = new osg::Group;
-
-    // Create the viewer
-    osgViewer::Viewer viewer;
-    viewer.setSceneData(root);
-    viewer.realize();
-
-    // Add axesNode under root
-    axesNode = osgDB::readNodeFile("../../testdata/testbutton.stl");
-    if (!axesNode) {
-        printf("Origin node not loaded, model not found\n");
-        return 1;
-    }
-    root->addChild(axesNode);
-
-    // Attach a manipulator (it's usually done for us when we use viewer.run())
-    osg::ref_ptr<osgGA::TrackballManipulator> tm = new osgGA::TrackballManipulator;
-    viewer.setCameraManipulator(tm);
-
-    storeHandler = new StoreHandler(root);
-    openScadRenderer = new OpenScadRenderer();
-    threeMFWriter = new ThreeMFWriter();
-
-    osg::ref_ptr<PickHandler> picker = new PickHandler();
-    root->addChild(picker->getOrCreateSelectionCylinder());
-
-    viewer.addEventHandler(picker.get());
-
-    return viewer.run();*/
-
-    QApplication application( argc, argv );
+    QApplication application(argc, argv);
 
     QSurfaceFormat format;
     format.setVersion(2, 1);
-    format.setProfile( QSurfaceFormat::CompatibilityProfile );
+    format.setProfile(QSurfaceFormat::CompatibilityProfile);
 
     QSurfaceFormat::setDefaultFormat(format);
 
     MainWindow mainWindow;
     mainWindow.show();
 
-    return( application.exec() );
+    return(application.exec());
 }