From 8506e0c01add08c6ce0e02178cee9dc815568cce Mon Sep 17 00:00:00 2001
From: ganthern <nils.ganther@stud.hs-hannover.de>
Date: Wed, 7 Apr 2021 19:30:45 +0200
Subject: [PATCH] add right-click neighborhood picking

---
 CgQtViewer/CGQtGLRenderWidget.cpp |   1 +
 CgQtViewer/CgQtGui.cpp            |  26 ++--
 CgSceneGraph/CgAABB.cpp           |  87 ++++++++++++++
 CgSceneGraph/CgAABB.h             |   9 ++
 CgSceneGraph/CgPointCloud.cpp     |  90 ++++++++++++--
 CgSceneGraph/CgPointCloud.h       |  16 ++-
 CgSceneGraph/CgQuad.cpp           |   1 -
 CgSceneGraph/CgQuad.h             |   1 +
 CgSceneGraph/CgSceneControl.cpp   | 189 ++++++++++++++----------------
 CgSceneGraph/CgSceneControl.h     |   3 +-
 CgShader/simple.vert              |   2 +-
 11 files changed, 288 insertions(+), 137 deletions(-)

diff --git a/CgQtViewer/CGQtGLRenderWidget.cpp b/CgQtViewer/CGQtGLRenderWidget.cpp
index 86c828b..e2e5300 100644
--- a/CgQtViewer/CGQtGLRenderWidget.cpp
+++ b/CgQtViewer/CGQtGLRenderWidget.cpp
@@ -290,6 +290,7 @@ void CgQtGLRenderWidget::paintGL()
 
 
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    glEnable(GL_PROGRAM_POINT_SIZE);
     glEnable(GL_DEPTH_TEST);
     glEnable(GL_LIGHTING);
     if(m_backface_culling)
diff --git a/CgQtViewer/CgQtGui.cpp b/CgQtViewer/CgQtGui.cpp
index 4cc6089..5ea3956 100644
--- a/CgQtViewer/CgQtGui.cpp
+++ b/CgQtViewer/CgQtGui.cpp
@@ -101,7 +101,7 @@ CgQtGui::CgQtGui(CgQtMainApplication *mw)
 
     QAction* show_pickray=settings_menu->addAction("&show pick ray", this, SLOT(slotPickRay()));
     show_pickray->setCheckable(true);
-    show_pickray->setChecked(false);
+    show_pickray->setChecked(true);
 
     QActionGroup* polygonmode_group = new QActionGroup(this);
     polygonmode_group->setExclusive(true);
@@ -279,14 +279,7 @@ void CgQtGui::slotTrackballChanged()
 void CgQtGui::mouseEvent(QMouseEvent* event)
 {
 
-   // std::cout << QApplication::keyboardModifiers() << std::endl;
-
-  //  if(QApplication::keyboardModifiers().testFlag(Qt::ControlModifier)==true)
-    //    std::cout << Cg::ControlModifier << endl;
-
-
-   if(event->type()==QEvent::MouseButtonPress)
-   {
+   if(event->type()==QEvent::MouseButtonPress) {
 
 
         CgBaseEvent* e = new CgMouseEvent(Cg::CgMouseButtonPress,
@@ -296,8 +289,7 @@ void CgQtGui::mouseEvent(QMouseEvent* event)
         notifyObserver(e);
    }
 
-   if(event->type() == QEvent::MouseButtonRelease)
-   {
+   if(event->type() == QEvent::MouseButtonRelease) {
         CgBaseEvent* e = new CgMouseEvent(Cg::CgMouseButtonRelease,
                                           glm::vec2(event->localPos().x() ,event->localPos().y()),
                                           (Cg::MouseButtons)event->button());
@@ -305,8 +297,7 @@ void CgQtGui::mouseEvent(QMouseEvent* event)
         notifyObserver(e);
    }
 
-   if(event->type()==QEvent::MouseMove)
-   {
+   if(event->type()==QEvent::MouseMove) {
        CgBaseEvent* e= new CgMouseEvent(Cg::CgMouseMove,
                                         glm::vec2(event->localPos().x() ,event->localPos().y()),
                                         (Cg::MouseButtons)event->button());
@@ -317,21 +308,18 @@ void CgQtGui::mouseEvent(QMouseEvent* event)
 
 }
 
-void CgQtGui::wheelEvent(QWheelEvent* event)
-{
+void CgQtGui::wheelEvent(QWheelEvent* event) {
     CgBaseEvent* e = new CgWheelEvent(Cg::CgMouseWheel, event->angleDelta().y() / 8.0);
     notifyObserver(e);
 }
 
-void CgQtGui::keyPressEvent(QKeyEvent *event)
-{
+void CgQtGui::keyPressEvent(QKeyEvent *event) {
    CgBaseEvent* e= new CgKeyEvent(Cg::CgKeyPressEvent,(Cg::Key)event->key(),(Cg::KeyboardModifiers)event->nativeModifiers(),event->text().toStdString());
    notifyObserver(e);
 }
 
 
-void CgQtGui::viewportChanged(int w, int h)
-{
+void CgQtGui::viewportChanged(int w, int h) {
      CgBaseEvent* e = new CgWindowResizeEvent(Cg::WindowResizeEvent,w,h);
      notifyObserver(e);
 }
diff --git a/CgSceneGraph/CgAABB.cpp b/CgSceneGraph/CgAABB.cpp
index 4d8354e..b2e3ad0 100644
--- a/CgSceneGraph/CgAABB.cpp
+++ b/CgSceneGraph/CgAABB.cpp
@@ -24,4 +24,91 @@ std::pair<CgAABB, CgAABB> CgAABB::split(int axis, double value) {
     new_extent1[axis] *= .5;
     new_extent2[axis] *= .5;
     return {CgAABB(new_position1, new_extent1), CgAABB(new_position2, new_extent2)};
+}
+
+// idea stolen from https://tavianator.com/2011/ray_box.html
+bool CgAABB::intersectsRay(glm::vec3 origin, glm::vec3 inv_direction) {
+    glm::vec3 bmin = position - extent;
+    glm::vec3 bmax = position + extent;
+
+    // t-val for intersection with each plane
+    glm::vec3 tlo = (bmin - origin) * inv_direction;
+    glm::vec3 thi = (bmax - origin) * inv_direction;
+
+    // t-vals that intersect first for each dimension
+    glm::vec3 tmin = glm::vec3(
+        std::min(tlo.x, thi.x),
+        std::min(tlo.y, thi.y),
+        std::min(tlo.z, thi.z)
+    );
+    // t-vals that intersect last for each dimension
+    glm::vec3 tmax = glm::vec3(
+        std::max(tlo.x, thi.x),
+        std::max(tlo.y, thi.y),
+        std::max(tlo.z, thi.z)
+    );
+
+    return glm::compMax(tmin) >= glm::compMin(tmax);
+}
+
+float CgAABB::sqrDistanceToRay(glm::vec3 origin, glm::vec3 direction, glm::vec3 inv_direction) {
+    glm::vec3 bmin = position - extent;
+    glm::vec3 bmax = position + extent;
+
+    // calculate distance along the ray for intersection with each
+    // of the boxes planes
+    float tx1 = (bmin.x - origin.y) * inv_direction.x;
+    float tx2 = (bmax.x - origin.y) * inv_direction.x;
+    float ty1 = (bmin.y - origin.y) * inv_direction.y;
+    float ty2 = (bmax.y - origin.y) * inv_direction.y;
+    float tz1 = (bmin.z - origin.z) * inv_direction.z;
+    float tz2 = (bmax.z - origin.z) * inv_direction.z;
+
+    // figure out which of the planes is closest
+    float minX = std::min(tx1, tx2);
+    float maxX = std::max(tx1, tx2);
+    float minY = std::min(ty1, ty2);
+    float maxY = std::max(ty1, ty2);
+    float minZ = std::min(tz1, tz2);
+    float maxZ = std::max(tz1, tz2);
+
+    float p1 = std::max(minX, std::max(minY, minZ));
+    float p2 = std::min(maxX, std::min(maxY, maxZ));
+
+    // clamp in ray direction
+    p1 = std::max(0.0f, p1);
+    p2 = std::max(0.0f, p2);
+
+    // closest point on box
+    float boxX = (origin.x + direction.x * p1 + origin.x + direction.x * p2) / 2;
+    float boxY = (origin.y + direction.y * p1 + origin.y + direction.y * p2) / 2;
+    float boxZ = (origin.z + direction.z * p1 + origin.z + direction.z * p2) / 2;
+
+    boxX = std::max(std::min(boxX, bmax.x), bmin.x);
+    boxY = std::max(std::min(boxY, bmax.y), bmin.y);
+    boxZ = std::max(std::min(boxZ, bmax.z), bmin.z);
+
+    // closest point on ray
+    float t = (boxX - origin.x) * direction.x + (boxY - origin.y) * direction.y + (boxZ - origin.z) * direction.z;
+    t =  t / (direction.x * direction.x + direction.y * direction.y + direction.z * direction.z);
+    t = std::max(0.0f, t);
+    
+    float rayX = origin.x + direction.x * t;
+    float rayY = origin.y + direction.y * t;
+    float rayZ = origin.z + direction.z * t;
+
+    // deltas between ray point and box point
+    float dx = rayX - boxX;
+    float dy = rayY - boxY;
+    float dz = rayZ - boxZ;
+
+    // squared dist
+    return dx * dx + dy * dy + dz * dz;
+}
+
+bool CgAABB::intersectsPoint(glm::vec3 point) {
+    return true 
+    && (point.x <= position.x + extent.x && point.x >= position.x + extent.x)
+    && (point.y <= position.y + extent.y && point.y >= position.y + extent.y)
+    && (point.z <= position.z + extent.z && point.z >= position.z + extent.z);
 }
\ No newline at end of file
diff --git a/CgSceneGraph/CgAABB.h b/CgSceneGraph/CgAABB.h
index d2026bf..7737180 100644
--- a/CgSceneGraph/CgAABB.h
+++ b/CgSceneGraph/CgAABB.h
@@ -2,8 +2,11 @@
 #define CGAABB_H
 
 #include <utility>
+#include <algorithm>
 #include <glm/glm.hpp>
+#include <glm/gtx/component_wise.hpp>
 
+// a class representing an axis-aligned bounding box
 class CgAABB {
 public:
     glm::vec3 position;
@@ -12,6 +15,12 @@ public:
     CgAABB(glm::vec3 pos, glm::vec3 ext);
     // split a aabb on an axis into two aabb whose union occupies the same space
     std::pair<CgAABB, CgAABB> split(int axis, double value);
+    // ray-box collision
+    bool intersectsRay(glm::vec3 origin, glm::vec3 inv_direction);
+    // point-box collision
+    bool intersectsPoint(glm::vec3 point);
+    // return the squared distance between the box and a ray
+    float sqrDistanceToRay(glm::vec3 origin, glm::vec3 direction, glm::vec3 inv_direction);
 };
 
 
diff --git a/CgSceneGraph/CgPointCloud.cpp b/CgSceneGraph/CgPointCloud.cpp
index 6e5f60d..d640829 100644
--- a/CgSceneGraph/CgPointCloud.cpp
+++ b/CgSceneGraph/CgPointCloud.cpp
@@ -1,11 +1,15 @@
 #include "CgPointCloud.h"
 
-CgPointCloud::CgPointCloud():
-CgPointCloud::CgPointCloud(51)
-{
 
+// squared distance between a point and a ray
+float sqrPointRayDist(glm::vec3 point, glm::vec3 origin, glm::vec3 direction) {
+  glm::vec3 delta = point - origin;
+  float t = std::max(0.0f, glm::dot(direction, delta) / glm::length2(direction));
+  return glm::length2(direction * t - delta);
 }
 
+CgPointCloud::CgPointCloud(): CgPointCloud::CgPointCloud(51) {}
+
 CgPointCloud::CgPointCloud(int id):
 m_type(Cg::PointCloud),
 m_id(id) {
@@ -45,8 +49,7 @@ void CgPointCloud::calculateSplatOrientations() {
 
 }
 
-void CgPointCloud::init( std::string filename, bool cheat_normals)
-{
+void CgPointCloud::init( std::string filename, bool cheat_normals) {
     m_vertices.clear();
     m_vertex_normals.clear();
     m_vertex_colors.clear();
@@ -82,10 +85,7 @@ void CgPointCloud::init( std::string filename, bool cheat_normals)
     // "fast" if k is relatively small, if k is on the order 
     // of m_vertices.size(), this is slower than brute force.
     std::vector<unsigned int> neighbors = getNearestNeighborsFast(0, k);
-
-    for(unsigned int i = 0;i < k; i++) {
-      m_vertex_colors[neighbors[i]] = glm::vec3(0.0,0.0,1.0);
-    }
+    setColors(neighbors, glm::vec3(0.0, 0.0, .75));
 }
 
 void CgPointCloud::buildKdTree(glm::vec3* begin, glm::vec3* end, int depth) {
@@ -199,7 +199,7 @@ std::vector<unsigned int> CgPointCloud::getNearestNeighborsFast(unsigned int cur
 
     glm::vec3 curr_point = *(begin + half_size);
     // insert current point into set
-    set.insert({glm::distance(query, curr_point), begin + half_size - first});
+    set.insert({glm::distance2(query, curr_point), begin + half_size - first});
     // make sure our set only contains k elements at most
     while(set.size() > k) set.erase(std::prev(set.end()));
 
@@ -236,6 +236,71 @@ std::vector<unsigned int> CgPointCloud::getNearestNeighborsFast(unsigned int cur
   return erg;
 }
 
+int CgPointCloud::getClosestPointToRay(glm::vec3 origin, glm::vec3 direction) {
+  if(m_vertices.size() == 0) return -1;
+
+  glm::vec3 inv_direction = glm::vec3(1.0 / direction.x, 1.0 / direction.y, 1.0 / direction.z);
+  glm::vec3* first = &m_vertices.front();
+  glm::vec3* last = &(*(m_vertices.end() - 1));
+  // sensible starting point
+  float erg_sqr_dist = std::numeric_limits<float>::infinity();
+  int erg = -1;
+
+  // stack of traversed nodes (start index, end index, aabb, depth)
+  std::vector<std::tuple<glm::vec3*, glm::vec3*, CgAABB, unsigned int>> nodes;
+  nodes.push_back({first, last, m_aabb, 0});
+  
+  // traverse the tree as long as there's something to do.
+  while(!nodes.empty()) {
+    // unpack current node & pop it
+    glm::vec3* begin;
+    glm::vec3* end;
+    CgAABB aabb;
+    unsigned int depth;
+    std::tie(begin, end, aabb, depth) = nodes.back();
+    auto half_size = (end - begin) / 2;
+    nodes.pop_back();
+
+    glm::vec3 curr_point = *(begin + half_size);
+    // insert current point into set if it's closer to ray than last one
+    float cand_dist = sqrPointRayDist(curr_point, origin, direction);
+    if(cand_dist < erg_sqr_dist) {
+      erg_sqr_dist = cand_dist;
+      erg = begin + half_size - first;
+    }
+
+    // the aabb of the sub-nodes
+    unsigned int axis = depth % 3;
+    CgAABB lower_aabb;
+    CgAABB higher_aabb;
+    std::tie(lower_aabb, higher_aabb) = aabb.split(axis, curr_point[axis]);
+    // only push children if their aabb is closer to ray than 
+    // the closest point we found until now and there are
+    // points in them
+    if (
+      begin != begin + half_size
+      && lower_aabb.sqrDistanceToRay(origin, direction, inv_direction) < erg_sqr_dist
+    ) {
+      nodes.push_back({begin, begin + half_size, lower_aabb, depth + 1});
+    }
+    if (
+      end != begin + half_size
+      && higher_aabb.sqrDistanceToRay(origin, direction, inv_direction) < erg_sqr_dist
+    ) {
+      nodes.push_back({begin + half_size + 1, end, higher_aabb, depth + 1});
+    }
+  }
+
+  return erg;
+}
+
+void CgPointCloud::setColors(std::vector<unsigned int> indices, glm::vec3 color) {
+  for(unsigned int i = 0; i < indices.size(); i++) m_vertex_colors[indices[i]] = color;
+}
+
+void CgPointCloud::resetColors(glm::vec3 color) {
+  for(unsigned int i = 0; i < m_vertices.size(); i++) m_vertex_colors[i] = color;
+}
 
 // calculates an arbitrary verctor perpendicular to the given one
 glm::vec3 CgPointCloud::getPerpendicularVector(glm::vec3 arg) {
@@ -285,7 +350,6 @@ const CgAABB CgPointCloud::getAABB() const {
   return m_aabb;
 }
 
-const std::vector<glm::vec2>& CgPointCloud::getSplatScalings() const
-{
+const std::vector<glm::vec2>& CgPointCloud::getSplatScalings() const {
   return m_splat_scaling;
-}
+}
\ No newline at end of file
diff --git a/CgSceneGraph/CgPointCloud.h b/CgSceneGraph/CgPointCloud.h
index 251ec4f..80938b8 100644
--- a/CgSceneGraph/CgPointCloud.h
+++ b/CgSceneGraph/CgPointCloud.h
@@ -17,6 +17,7 @@
 #include "CgBase/CgEnums.h"
 #include "CgUtils/ObjLoader.h"
 #include <glm/glm.hpp>
+#include <glm/gtx/norm.hpp>
 #include <glm/gtc/matrix_transform.hpp>
 
 class CgPointCloud : public CgBasePointCloud {
@@ -53,6 +54,17 @@ public:
   // read a dataset from file, can cheat the normals, i.e read the mormals from file
   void init( std::string filename, bool cheat_normals=false);
 
+  // nearest-neighbour search in the point cloud
+  std::vector<unsigned int> getNearestNeighborsSlow(unsigned int current_point, unsigned int k);
+  std::vector<unsigned int> getNearestNeighborsFast(unsigned int current_point, unsigned int k);
+
+  // get the point that's closest to the ray
+  int getClosestPointToRay(glm::vec3 origin, glm::vec3 direction);
+
+  // change colors of a list of points
+  void setColors(std::vector<unsigned int> indices, glm::vec3 color);
+  void resetColors(glm::vec3 color);
+
   // the center of gravity of the object, for rendering
   const glm::vec3 getCenter() const;
 
@@ -73,10 +85,6 @@ private:
     // for demonstration: for a given normal direction find an arbitrary vector to span the tangent plane
     glm::vec3 getPerpendicularVector(glm::vec3 arg);
 
-    // for demonstration purposes, very inefficient
-    std::vector<unsigned int> getNearestNeighborsSlow(unsigned int current_point, unsigned int k);
-    std::vector<unsigned int> getNearestNeighborsFast(unsigned int current_point, unsigned int k);
-
     // rearrange the vec3 between begin and end so they form a kd-tree
     void buildKdTree(glm::vec3* begin, glm::vec3* end, int depth);
 
diff --git a/CgSceneGraph/CgQuad.cpp b/CgSceneGraph/CgQuad.cpp
index 575c4c4..54f74d9 100644
--- a/CgSceneGraph/CgQuad.cpp
+++ b/CgSceneGraph/CgQuad.cpp
@@ -1,5 +1,4 @@
 #include "CgQuad.h"
-#include "CgBase/CgEnums.h"
 
 CgQuad::CgQuad():
 m_type(Cg::Polyline),
diff --git a/CgSceneGraph/CgQuad.h b/CgSceneGraph/CgQuad.h
index 25f3179..a3daaf5 100644
--- a/CgSceneGraph/CgQuad.h
+++ b/CgSceneGraph/CgQuad.h
@@ -5,6 +5,7 @@
 #include <glm/glm.hpp>
 #include <string>
 #include "CgBase/CgBasePolyline.h"
+#include "CgBase/CgEnums.h"
 
 // four vertices arranged in a rectangle, 
 //rendered as a wireframe
diff --git a/CgSceneGraph/CgSceneControl.cpp b/CgSceneGraph/CgSceneControl.cpp
index a27161f..e4ae4fb 100644
--- a/CgSceneGraph/CgSceneControl.cpp
+++ b/CgSceneGraph/CgSceneControl.cpp
@@ -29,24 +29,36 @@ using namespace Eigen;
 
 CgSceneControl::CgSceneControl()
 {
-    m_pointcloud=nullptr;
+    m_pointcloud = nullptr;
     resetTransform();
-    m_lookAt_matrix= glm::lookAt(glm::vec3(0.0,0.0,1.0),glm::vec3(0.0,0.0,0.0),glm::vec3(0.0,1.0,0.0));
-    m_proj_matrix= glm::mat4x4(glm::vec4(1.792591, 0.0, 0.0, 0.0), glm::vec4(0.0, 1.792591, 0.0, 0.0), glm::vec4(0.0, 0.0, -1.0002, -1.0), glm::vec4(0.0, 0.0, -0.020002, 0.0));
 
-    m_select_ray=NULL;
+    m_lookAt_matrix= glm::lookAt(
+      glm::vec3(0.0,0.0,1.0),
+      glm::vec3(0.0,0.0,0.0),
+      glm::vec3(0.0,1.0,0.0)
+    );
+
+    m_proj_matrix= glm::mat4x4(
+      glm::vec4(1.792591, 0.0, 0.0, 0.0), 
+      glm::vec4(0.0, 1.792591, 0.0, 0.0), 
+      glm::vec4(0.0, 0.0, -1.0002, -1.0), 
+      glm::vec4(0.0, 0.0, -0.020002, 0.0)
+    );
+
+    m_select_ray = nullptr;
     m_disc = new CgTriangleMesh(78);
-    m_pointcloud = NULL;
-    m_center=glm::vec3(0.);
-    m_triangle_mesh= NULL;
-    m_show_splats=false;
-    m_show_pickray=false;
-    m_is_dragging=false;
-    m_drag_center=glm::vec2(0.0,0.0);
+    m_pointcloud = nullptr;
+    m_center = glm::vec3(0.);
+    m_triangle_mesh= nullptr;
+    m_show_splats = false;
+    m_show_pickray = true;
+    m_is_dragging = false;
+    m_drag_center = glm::vec2(0.0,0.0);
 }
 
 void CgSceneControl::resetTransform() {
     m_current_translation = glm::mat4(1.0);
+    m_current_translation = glm::translate(m_current_translation, glm::vec3(0.0, 0.0, -2.0));
     m_current_scale = glm::mat4(1.0);
     m_current_rotation = glm::mat4(1.0);
     m_current_transformation = glm::mat4(1.0);
@@ -66,41 +78,34 @@ CgSceneControl::~CgSceneControl()
     m_kd_tree_mesh.clear();
 }
 
-
-
-void CgSceneControl::calculatePickRay(double x, double y)
-{
-  double w = (double) m_renderer->getViewportWidth();
-  double h = (double) m_renderer->getViewportHeight();
+void CgSceneControl::calculatePickRay(float x, float y) {
+  float w = (float) m_renderer->getViewportWidth();
+  float h = (float) m_renderer->getViewportHeight();
 
   // normalize into [-1;1]
-  x=2.0*x / w -1.0;
-  if (x<-1.0) x=-1.0;
-  if (x>1.0) x=1.0;
-
-  y=2.0*y / h -1.0;
-  if (y<-1.0) y=-1.0;
-  if (y>1.0) y=1.0;
+  x = std::max(-1.0f, std::min(1.0f, 2.0f * x / w - 1.0f));
+  y = std::max(-1.0f, std::min(1.0f, 2.0f * y / h - 1.0f));
 
   // change to right handed coordinate system
-  y=-y;
+  y = -y;
 
-  glm::mat4 inverse_proj=glm::inverse(m_proj_matrix);
+  glm::mat4 inverse_proj = glm::inverse(m_proj_matrix);
 
   //unproject point on front clipping plane
-  glm::vec4 p(x,y,-0.01,1);
-  glm::vec4 q= inverse_proj*p;
-  q/= q.w;
+  glm::vec4 p(x, y, -0.01, 1);
+  glm::vec4 q = inverse_proj * p;
+  q /= q.w;
 
   //unproject point on back clipping plane
-  glm::vec4 r(x,y,1.0,1);
-  glm::vec4 s= inverse_proj*r;
-  s/= s.w;
+  glm::vec4 r(x, y, 1.0, 1);
+  glm::vec4 s = inverse_proj * r;
+  s /= s.w;
 
   //construct current modelview matrix by hand, since there is no scenegraph
-  m_current_transformation = glm::translate(m_current_transformation,-m_center);
-  glm::mat4 mv_matrix = m_lookAt_matrix * m_current_rotation* m_current_transformation ;
-  m_current_transformation = glm::translate(m_current_transformation,m_center);
+  m_current_transformation = m_current_scale * m_current_translation * m_current_rotation;
+  m_current_transformation = glm::translate(m_current_transformation, -m_center);
+  glm::mat4 mv_matrix = m_lookAt_matrix * m_current_transformation;
+  m_current_transformation = glm::translate(m_current_transformation, m_center);
 
 
   // convert pick ray into local "bunny coordinates"
@@ -109,15 +114,9 @@ void CgSceneControl::calculatePickRay(double x, double y)
   glm::vec4 rayend = mv_matrix_inv * s;
 
   // init new CgPolyline to draw the pick ray
-  if(m_select_ray!=NULL)
-    delete m_select_ray;
-  std::vector<glm::vec3> pointlist;
-  pointlist.push_back(raystart);
-  pointlist.push_back(rayend);
-  m_select_ray = new CgPolyLine(33,pointlist);
+  if(m_select_ray != nullptr) delete m_select_ray;
+  m_select_ray = new CgPolyLine(33, {raystart, rayend});
   m_renderer->init(m_select_ray);
-
-  m_renderer->redraw();
 }
 
 
@@ -223,10 +222,23 @@ void CgSceneControl::handleEvent(CgBaseEvent* e)
     if(e->getType() & Cg::CgMouseButtonPress)
     {
         CgMouseEvent* ev = (CgMouseEvent*)e;
-        if(ev->button()== Cg::RightButton)
-            calculatePickRay((double)ev->x(),(double)ev->y());
-        else if(ev->button() == Cg::MiddleButton)
-        {
+        if(ev->button()== Cg::RightButton) {
+          calculatePickRay((double)ev->x(),(double)ev->y());
+          if(m_pointcloud != nullptr) {
+            std::vector<glm::vec3> points = m_select_ray->getVertices();
+            glm::vec3 start = points[0];
+            glm::vec3 direction = points[1] - start;
+            int picked_index = m_pointcloud->getClosestPointToRay(start, direction);
+            if(picked_index != -1) { // -1 => point found
+              std::vector<unsigned int> neighbors = m_pointcloud->getNearestNeighborsFast(picked_index, 50);
+              m_pointcloud->resetColors(glm::vec3(0.0, 0.75, 0.0));
+              m_pointcloud->setColors(neighbors, glm::vec3(0.75, 0.0, 0.0));
+              m_pointcloud->setColors({picked_index}, glm::vec3(1.0, 1.0, 0.0));
+              m_renderer->init(m_pointcloud);
+              m_renderer->redraw();
+            }
+          }
+        } else if(ev->button() == Cg::MiddleButton) {
           m_drag_center = glm::vec2((double)ev->x(), (double)ev->y());
           m_is_dragging = true;
         }
@@ -282,20 +294,17 @@ void CgSceneControl::handleEvent(CgBaseEvent* e)
     {
         CgKeyEvent* ev = (CgKeyEvent*)e;
 
-        if(ev->text()=="e")
-        {
-            // example usage of eigen library to compute the eigen-decomposition of a matrix
-            calculateEigenDecomposition3x3();
+        if(ev->text()=="e") {
+          // example usage of eigen library to compute the eigen-decomposition of a matrix
+          calculateEigenDecomposition3x3();
         }
 
-        if(ev->text()=="s")
-        {
+        if(ev->text()=="s") {
           // example usage of eigen library to compute the eigen-decomposition of a matrix
           calculateSingularValueDecomposition();
         }
 
-        if(ev->text() == "c")
-        {
+        if(ev->text() == "c") {
           resetTransform();
           m_renderer->redraw();
         }
@@ -424,62 +433,47 @@ void CgSceneControl::handleEvent(CgBaseEvent* e)
 // rendering stuff
 /*******************************************************************************************************/
 
-void CgSceneControl::setRenderer(CgBaseRenderer* r)
-{
-    m_renderer=r;
+void CgSceneControl::setRenderer(CgBaseRenderer* r) {
+    m_renderer = r;
     m_renderer->setSceneControl(this);
 
-    if(m_pointcloud!=NULL)
-    m_renderer->init(m_pointcloud);
-
-    if(m_disc!=NULL)
-    m_renderer->init(m_disc);
-
-    if(m_select_ray!=NULL)
-    m_renderer->init(m_select_ray);
-
-    if(m_triangle_mesh!=NULL)
-    m_renderer->init(m_triangle_mesh);
+    if(m_pointcloud != nullptr) m_renderer->init(m_pointcloud);
+    if(m_disc != nullptr) m_renderer->init(m_disc);
+    if(m_select_ray != nullptr) m_renderer->init(m_select_ray);
+    if(m_triangle_mesh != nullptr) m_renderer->init(m_triangle_mesh);
 }
 
 
-void CgSceneControl::renderObjects()
-{
+void CgSceneControl::renderObjects() {
 
     // Materialeigenschaften setzen
-
-
-    m_renderer->setUniformValue("mycolor",glm::vec4(0.0,1.0,0.0,1.0));
-
-    m_renderer->setUniformValue("matDiffuseColor",glm::vec4(0.780392f, 0.568627f, 0.113725f, 1.0f ));
-    m_renderer->setUniformValue("lightDiffuseColor",glm::vec4(1.0,1.0,1.0,1.0));
-
-    m_renderer->setUniformValue("matAmbientColor",glm::vec4(0.329412f, 0.223529f, 0.027451f,1.0f ));
-    m_renderer->setUniformValue("lightAmbientColor",glm::vec4(1.0,1.0,1.0,1.0));
-
-    m_renderer->setUniformValue("matSpecularColor",glm::vec4(0.992157f, 0.941176f, 0.807843f, 1.0f));
-    m_renderer->setUniformValue("lightSpecularColor",glm::vec4(1.0,1.0,1.0,1.0));
-
-    m_renderer->setUniformValue("shininess",27.8974f);
-
-    m_current_transformation = m_current_scale * m_current_translation * m_current_rotation ;
-
-    if(m_triangle_mesh!=NULL) {
-      m_current_transformation = glm::translate(m_current_transformation,-m_center);
+    m_renderer->setUniformValue("mycolor", glm::vec4(0.0,1.0,0.0,1.0));
+    m_renderer->setUniformValue("matDiffuseColor", glm::vec4(0.780392f, 0.568627f, 0.113725f, 1.0f ));
+    m_renderer->setUniformValue("lightDiffuseColor", glm::vec4(1.0,1.0,1.0,1.0));
+    m_renderer->setUniformValue("matAmbientColor", glm::vec4(0.329412f, 0.223529f, 0.027451f,1.0f ));
+    m_renderer->setUniformValue("lightAmbientColor", glm::vec4(1.0,1.0,1.0,1.0));
+    m_renderer->setUniformValue("matSpecularColor", glm::vec4(0.992157f, 0.941176f, 0.807843f, 1.0f));
+    m_renderer->setUniformValue("lightSpecularColor", glm::vec4(1.0,1.0,1.0,1.0));
+    m_renderer->setUniformValue("shininess", 27.8974f);
+
+    m_current_transformation = m_current_scale * m_current_translation * m_current_rotation;
+
+    if(m_triangle_mesh != nullptr) {
+      m_current_transformation = glm::translate(m_current_transformation, -m_center);
       glm::mat4 mv_matrix = m_lookAt_matrix * m_current_transformation ;
       m_renderer->render(m_triangle_mesh, mv_matrix, m_proj_matrix);
-      m_current_transformation = glm::translate(m_current_transformation,m_center);
+      m_current_transformation = glm::translate(m_current_transformation, m_center);
     }
 
-    if((m_pointcloud!=NULL)&&(!m_show_splats)) {
+    if((m_pointcloud != nullptr)&& !m_show_splats) {
       m_current_transformation = glm::translate(m_current_transformation,-m_center);
-      glm::mat4 mv_matrix = m_lookAt_matrix * m_current_transformation ;
+      glm::mat4 mv_matrix = m_lookAt_matrix * m_current_transformation;
       m_renderer->render(m_pointcloud, mv_matrix, m_proj_matrix);
       m_current_transformation = glm::translate(m_current_transformation,m_center);
     }
 
-    if((m_select_ray!=NULL)&&(m_show_pickray)) {
-      m_current_transformation = glm::translate(m_current_transformation,-m_center);
+    if((m_select_ray != nullptr) && m_show_pickray) {
+      m_current_transformation = glm::translate(m_current_transformation, -m_center);
       glm::mat4 mv_matrix = m_lookAt_matrix * m_current_transformation;
       m_renderer->render(m_select_ray, mv_matrix, m_proj_matrix);
       m_current_transformation = glm::translate(m_current_transformation, m_center);
@@ -496,7 +490,7 @@ void CgSceneControl::renderObjects()
 
     // you would never ever render splats like this, a splat is never a scenegraph object
     // just to have not much effort to show the shape, inefficient rendering!
-    if((m_pointcloud!=NULL)&&(m_show_splats)) {
+    if((m_pointcloud != nullptr) && m_show_splats) {
 
       m_renderer->setUniformValue("rendersplats",1);
 
@@ -518,8 +512,7 @@ void CgSceneControl::renderObjects()
 
         mv_matrix = m_lookAt_matrix * m_current_rotation* m_current_transformation ;
 
-        if(m_disc!=NULL)
-              m_renderer->render(m_disc,mv_matrix,m_proj_matrix);
+        if(m_disc != nullptr) m_renderer->render(m_disc,mv_matrix,m_proj_matrix);
 
         m_current_transformation=glm::scale(m_current_transformation,glm::vec3(1.0/scalings[splat_indices[i]][0],1.0/scalings[splat_indices[i]][1],1.0));
 
diff --git a/CgSceneGraph/CgSceneControl.h b/CgSceneGraph/CgSceneControl.h
index 2b6c36a..d919d4b 100644
--- a/CgSceneGraph/CgSceneControl.h
+++ b/CgSceneGraph/CgSceneControl.h
@@ -8,6 +8,7 @@
 #include <vector>
 #include <queue>
 #include <iostream>
+#include <numeric>
 
 class CgBaseEvent;
 class CgBaseRenderer;
@@ -30,7 +31,7 @@ public:
 private:
 
     // since there is only one object, the select ray is in "bunny"-coordinates
-    void calculatePickRay(double x, double y);
+    void calculatePickRay(float x, float y);
     void resetTransform();
     bool m_show_pickray;
     bool m_is_dragging;
diff --git a/CgShader/simple.vert b/CgShader/simple.vert
index ae48699..463e0fa 100644
--- a/CgShader/simple.vert
+++ b/CgShader/simple.vert
@@ -21,7 +21,7 @@ varying vec3 viewEye,lightEye, normalEye;
 
     void main() {
       vert = vertex.xyz;
-      
+      gl_PointSize = 3;
       vec4 vert4=vec4(vertex,1.0);  
       col=color.xyz;
       
-- 
GitLab