{"action":"created","comment":{"url":"https://api.github.com/repos/CGAL/cgal/pulls/comments/85700585","pull_request_review_id":6394704,"id":85700585,"diff_hunk":"@@ -0,0 +1,1483 @@\n+// Copyright (c) 2015 INRIA Sophia-Antipolis (France).\n+// All rights reserved.\n+//\n+// This file is part of CGAL (www.cgal.org).\n+// You can redistribute it and/or modify it under the terms of the GNU\n+// General Public License as published by the Free Software Foundation,\n+// either version 3 of the License, or (at your option) any later version.\n+//\n+// Licensees holding a valid commercial license may use this file in\n+// accordance with the commercial license agreement provided with the software.\n+//\n+// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE\n+// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.\n+//\n+// $URL$\n+// $Id$\n+//\n+//\n+// Author(s)     : Florent Lafarge, Simon Giraudot\n+//\n+\n+#ifndef CGAL_STRUCTURE_POINT_SET_3_H\n+#define CGAL_STRUCTURE_POINT_SET_3_H\n+\n+#include <CGAL/property_map.h>\n+#include <CGAL/point_set_processing_assertions.h>\n+#include <CGAL/assertions.h>\n+\n+#include <CGAL/centroid.h>\n+\n+#include <CGAL/Kd_tree.h>\n+#include <CGAL/Fuzzy_sphere.h>\n+#include <CGAL/Fuzzy_iso_box.h>\n+#include <CGAL/Search_traits_d.h>\n+\n+#include <CGAL/Delaunay_triangulation_3.h>\n+#include <CGAL/Triangulation_vertex_base_with_info_3.h>\n+\n+#include <iterator>\n+#include <list>\n+#include <limits>\n+\n+// The following lines only for MS Visual C++\n+#ifdef _MSC_VER\n+#pragma warning(push)\n+#pragma warning(disable:4244) // boost converts signed to std::size_t\n+#endif\n+\n+\n+\n+namespace CGAL {\n+\n+/*!\n+\\ingroup PkgPointSetProcessing\n+\n+\\brief A 3D point set with structure information based on a set of\n+detected planes.\n+\n+Given a point set in 3D space along with a set of fitted planes, this\n+class stores a simplified and structured version of the point\n+set. Each output point is assigned to one, two or more primitives\n+(depending wether it belongs to a planar section, an edge or a if it\n+is a vertex). The implementation follow \\cgalCite{cgal:la-srpss-13}.\n+\n+\\tparam Traits a model of `EfficientRANSACTraits`\n+\n+*/\n+template <typename Traits>\n+class Point_set_with_structure\n+{\n+public:\n+\n+  /// \\cond SKIP_IN_MANUAL\n+  typedef Point_set_with_structure<Traits> Self;\n+\n+  typedef typename Traits::FT FT;\n+  typedef typename Traits::Segment_3 Segment;\n+  typedef typename Traits::Line_3 Line;\n+  typedef typename Traits::Plane_3 Plane;\n+\n+  typedef typename Traits::Point_2 Point_2;\n+\n+  typedef typename Traits::Point_map Point_map;\n+  typedef typename Traits::Normal_map Normal_map;\n+  typedef typename Traits::Input_range Input_range;\n+\n+  typedef typename Input_range::iterator Input_iterator;\n+\n+  typedef Shape_detection_3::Shape_base<Traits> Shape;\n+\n+  enum Point_status { POINT, RESIDUS, PLANE, EDGE, CORNER, SKIPPED };\n+  /// \\endcond\n+\n+  typedef typename Traits::Point_3 Point;\n+  typedef typename Traits::Vector_3 Vector;\n+  typedef Shape_detection_3::Plane<Traits> Plane_shape;\n+\n+  /// \\endcond\n+\n+  /// Tag classifying the coherence of a triplet of points with\n+  /// respect to an inferred surface\n+  enum Coherence_type\n+    {\n+      INCOHERENT = -1, ///< Incoherent (facet violates the underlying structure)\n+      FREEFORM = 0,    ///< Free-form coherent (facet is between 3 free-form points)\n+      VERTEX = 1,      ///< Structure coherent, facet adjacent to a vertex\n+      CREASE = 2,      ///< Structure coherent, facet adjacent to an edge\n+      PLANAR = 3       ///< Structure coherent, facet inside a planar section\n+    };\n+  \n+  /// \\cond SKIP_IN_MANUAL\n+private:\n+\n+  class My_point_property_map{\n+    const std::vector<Point>& points;\n+  public:\n+    typedef Point value_type;\n+    typedef const value_type& reference;\n+    typedef std::size_t key_type;\n+    typedef boost::lvalue_property_map_tag category;  \n+    My_point_property_map (const std::vector<Point>& pts) : points (pts) {}\n+    reference operator[] (key_type k) const { return points[k]; }\n+    friend inline reference get (const My_point_property_map& ppmap, key_type i) \n+    { return ppmap[i]; }\n+  };\n+\n+  struct On_the_fly_pair{\n+    const std::vector<Point>& points;\n+    typedef std::pair<Point, std::size_t> result_type;\n+\n+    On_the_fly_pair(const std::vector<Point>& points) : points(points) {}\n+  \n+    result_type\n+    operator()(std::size_t i) const\n+    {\n+      return result_type(points[i],i);\n+    }\n+  };\n+    \n+  struct Edge\n+  {\n+    CGAL::cpp11::array<std::size_t, 2> planes;\n+    std::vector<std::size_t> indices; // Points belonging to intersection\n+    Line support;\n+    bool active;\n+\n+    Edge (std::size_t a, std::size_t b)\n+    { planes[0] = a; planes[1] = b; active = true; }\n+  };\n+  struct Corner\n+  {\n+    std::vector<std::size_t> planes;\n+    std::vector<std::size_t> edges;\n+    std::vector<Vector> directions;\n+    Point support;\n+    bool active;\n+\n+    Corner (std::size_t p1, std::size_t p2, std::size_t p3,\n+            std::size_t e1, std::size_t e2, std::size_t e3)\n+    {\n+      planes.resize (3); planes[0] = p1; planes[1] = p2; planes[2] = p3;\n+      edges.resize (3); edges[0] = e1; edges[1] = e2; edges[2] = e3;\n+      active = true;\n+    }\n+  };\n+      \n+\n+  Traits m_traits;\n+\n+  std::vector<Point> m_points;\n+  std::vector<Vector> m_normals;\n+  std::vector<std::size_t> m_indices;\n+  std::vector<Point_status> m_status;\n+  Point_map m_point_map;\n+  Normal_map m_normal_map;\n+    \n+  std::vector<boost::shared_ptr<Plane_shape> > m_planes;\n+  std::vector<Edge> m_edges;\n+  std::vector<Corner> m_corners;\n+    \n+public:\n+  /// \\endcond\n+\n+  /*!\n+    Constructs a structured point set based on the input points and the\n+    associated shape detection object.\n+\n+    \\note Both property maps can be omitted if the default constructors of these property maps can be safely used.\n+\n+  */\n+  Point_set_with_structure (Input_iterator begin, ///< iterator over the first input point.\n+                            Input_iterator end, ///< past-the-end iterator over the input points.\n+                            Point_map point_map, ///< property map: value_type of InputIterator -> Point_3. \n+                            Normal_map normal_map, ///< property map: value_type of InputIterator -> Vector_3. \n+                            const Shape_detection_3::Efficient_RANSAC<Traits>&\n+                            shape_detection, ///< shape detection object\n+                            double epsilon, ///< size parameter\n+                            double attraction_factor = 3.) ///< attraction factor\n+    : m_traits (shape_detection.traits()),\n+      m_point_map(point_map), m_normal_map (normal_map)\n+  {\n+    for (Input_iterator it = begin; it != end; ++ it)\n+      {\n+        m_points.push_back (get(m_point_map, *it));\n+        m_normals.push_back (get(m_normal_map, *it));\n+      }\n+      \n+    m_indices = std::vector<std::size_t> (m_points.size (), (std::numeric_limits<std::size_t>::max)());\n+    m_status = std::vector<Point_status> (m_points.size (), POINT);\n+\n+    BOOST_FOREACH (boost::shared_ptr<Shape> shape, shape_detection.shapes())\n+      {\n+        boost::shared_ptr<Plane_shape> pshape\n+          = boost::dynamic_pointer_cast<Plane_shape>(shape);\n+        \n+        // Ignore all shapes other than plane\n+        if (pshape == boost::shared_ptr<Plane_shape>())\n+          continue;\n+        m_planes.push_back (pshape);\n+\n+        for (std::size_t i = 0; i < pshape->indices_of_assigned_points().size (); ++ i)\n+          {\n+            m_indices[pshape->indices_of_assigned_points()[i]] = m_planes.size () - 1;\n+            m_status[pshape->indices_of_assigned_points()[i]] = PLANE;\n+          }\n+      }\n+\n+    run (epsilon, attraction_factor);\n+    clean ();\n+  }\n+\n+  /// \\cond SKIP_IN_MANUAL\n+  Point_set_with_structure (Input_iterator begin, ///< iterator over the first input point.\n+                            Input_iterator end, ///< past-the-end iterator over the input points.\n+                            const Shape_detection_3::Efficient_RANSAC<Traits>&\n+                            shape_detection, ///< shape detection object\n+                            double epsilon, ///< size parameter\n+                            double attraction_factor = 3.) ///< attraction factor\n+    : Point_set_with_structure (begin, end, Point_map(), Normal_map(), shape_detection, epsilon, attraction_factor)\n+  {\n+  }\n+  /// \\endcond\n+\n+  /// \\cond SKIP_IN_MANUAL    \n+  virtual ~Point_set_with_structure ()\n+  {\n+  }\n+  /// \\endcond\n+\n+  \n+  std::size_t size () const { return m_points.size (); }\n+  std::pair<Point, Vector> operator[] (std::size_t i) const\n+  { return std::make_pair (m_points[i], m_normals[i]); }\n+  const Point& point (std::size_t i) const { return m_points[i]; }\n+  const Vector& normal (std::size_t i) const { return m_normals[i]; }\n+\n+  /*!\n+\n+    Returns all `Plane_shape` objects that are adjacent to the point\n+    with index `i`.\n+\n+    \\note Points not adjacent to any plane are free-form points,\n+    points adjacent to 1 plane are planar points, points adjacent to 2\n+    planes are edge points and points adjacent to 3 or more planes are\n+    vertices.\n+\n+   */\n+  std::vector<boost::shared_ptr<Plane_shape> > adjacency (std::size_t i) const\n+  {\n+    std::vector<boost::shared_ptr<Plane_shape> > out;\n+\n+    if (m_status[i] == PLANE || m_status[i] == RESIDUS)\n+      out.push_back (m_planes[m_indices[i]]);\n+    else if (m_status[i] == EDGE)\n+      {\n+        out.push_back (m_planes[m_edges[m_indices[i]].planes[0]]);\n+        out.push_back (m_planes[m_edges[m_indices[i]].planes[1]]);\n+      }\n+    else if (m_status[i] == CORNER)\n+      {\n+        for (std::size_t j = 0; j < m_corners[m_indices[i]].planes.size(); ++ j)\n+          out.push_back (m_planes[m_corners[m_indices[i]].planes[j]]);\n+      }\n+    return out;\n+  }\n+\n+  /*!\n+\n+    Computes the coherence of a facet between the 3 points indexed by\n+    `f` with respect to the underlying structure.\n+\n+   */\n+  Coherence_type facet_coherence (CGAL::cpp11::array<std::size_t, 3>& f) const\n+  {\n+    // O- FREEFORM CASE\n+    if (m_status[f[0]] == POINT &&\n+        m_status[f[1]] == POINT &&\n+        m_status[f[2]] == POINT)\n+      return FREEFORM;\n+      \n+    // 1- PLANAR CASE\n+    if (m_status[f[0]] == PLANE &&\n+        m_status[f[1]] == PLANE &&\n+        m_status[f[2]] == PLANE)\n+      {\n+        if (m_indices[f[0]] == m_indices[f[1]] &&\n+            m_indices[f[0]] == m_indices[f[2]])\n+          return PLANAR;\n+        else\n+          return INCOHERENT;\n+      }\n+\n+    for (std::size_t i = 0; i < 3; ++ i)\n+      {\n+        Point_status sa = m_status[f[(i+1)%3]];\n+        Point_status sb = m_status[f[(i+2)%3]];\n+        Point_status sc = m_status[f[(i+3)%3]];\n+        std::size_t a = m_indices[f[(i+1)%3]];\n+        std::size_t b = m_indices[f[(i+2)%3]];\n+        std::size_t c = m_indices[f[(i+3)%3]];\n+\n+        // O- FREEFORM CASE\n+        if (sa == POINT && sb == POINT && sc == PLANE)\n+          return FREEFORM;\n+        if (sa == POINT && sb == PLANE && sc == PLANE)\n+          {\n+            if (b == c)\n+              return FREEFORM;\n+            else\n+              return INCOHERENT;\n+          }\n+          \n+        // 2- CREASE CASES\n+        if (sa == EDGE && sb == EDGE && sc == PLANE)\n+          {\n+            if ((c == m_edges[a].planes[0] ||\n+                 c == m_edges[a].planes[1]) &&\n+                (c == m_edges[b].planes[0] ||\n+                 c == m_edges[b].planes[1]))\n+              return CREASE;\n+            else\n+              return INCOHERENT;\n+          }\n+\n+        if (sa == EDGE && sb == PLANE && sc == PLANE)\n+          {\n+            if (b == c &&\n+                (b == m_edges[a].planes[0] ||\n+                 b == m_edges[a].planes[1]))\n+              return CREASE;\n+            else\n+              return INCOHERENT;\n+          }\n+\n+\n+        // 3- CORNER CASES\n+        if (sc == CORNER)\n+          {\n+            if (sa == EDGE && sb == EDGE)\n+              {\n+                bool a0 = false, a1 = false, b0 = false, b1 = false;\n+\n+                if ((m_edges[a].planes[0] != m_edges[b].planes[0] &&\n+                     m_edges[a].planes[0] != m_edges[b].planes[1] &&\n+                     m_edges[a].planes[1] != m_edges[b].planes[0] &&\n+                     m_edges[a].planes[1] != m_edges[b].planes[1]))\n+                  return INCOHERENT;\n+                  \n+                for (std::size_t j = 0; j < m_corners[c].planes.size (); ++ j)\n+                  {\n+                    if (m_corners[c].planes[j] == m_edges[a].planes[0])\n+                      a0 = true;\n+                    else if (m_corners[c].planes[j] == m_edges[a].planes[1])\n+                      a1 = true;\n+                    if (m_corners[c].planes[j] == m_edges[b].planes[0])\n+                      b0 = true;\n+                    else if (m_corners[c].planes[j] == m_edges[b].planes[1])\n+                      b1 = true;\n+                  }\n+                if (a0 && a1 && b0 && b1)\n+                  return VERTEX;\n+                else\n+                  return INCOHERENT;\n+              }\n+            else if (sa == PLANE && sb == PLANE)\n+              {\n+                if (a != b)\n+                  return INCOHERENT;\n+\n+                for (std::size_t j = 0; j < m_corners[c].planes.size (); ++ j)\n+                  if (m_corners[c].planes[j] == a)\n+                    return VERTEX;\n+                  \n+                return INCOHERENT;\n+              }\n+            else if (sa == PLANE && sb == EDGE)\n+              {\n+                bool pa = false, b0 = false, b1 = false;\n+                if (a != m_edges[b].planes[0] && a != m_edges[b].planes[1])\n+                  return INCOHERENT;\n+                  \n+                for (std::size_t j = 0; j < m_corners[c].planes.size (); ++ j)\n+                  {\n+                    if (m_corners[c].planes[j] == a)\n+                      pa = true;\n+                    if (m_corners[c].planes[j] == m_edges[b].planes[0])\n+                      b0 = true;\n+                    else if (m_corners[c].planes[j] == m_edges[b].planes[1])\n+                      b1 = true;\n+                  }\n+                if (pa && b0 && b1)\n+                  return VERTEX;\n+                else\n+                  return INCOHERENT;\n+              }\n+            else if (sa == EDGE && sb == PLANE)\n+              {\n+                bool a0 = false, a1 = false, pb = false;\n+                if (b != m_edges[a].planes[0] && b != m_edges[a].planes[1])\n+                  return INCOHERENT;\n+                  \n+                for (std::size_t j = 0; j < m_corners[c].planes.size (); ++ j)\n+                  {\n+                    if (m_corners[c].planes[j] == b)\n+                      pb = true;\n+                    if (m_corners[c].planes[j] == m_edges[a].planes[0])\n+                      a0 = true;\n+                    else if (m_corners[c].planes[j] == m_edges[a].planes[1])\n+                      a1 = true;\n+                  }\n+                if (a0 && a1 && pb)\n+                  return VERTEX;\n+                else\n+                  return INCOHERENT;\n+              }\n+            else\n+              return INCOHERENT;\n+          }\n+      }\n+\n+\n+    return INCOHERENT;\n+  }\n+\n+\n+  /// \\cond SKIP_IN_MANUAL  \n+private:\n+\n+\n+  void clean ()\n+  {\n+    std::vector<Point> points;\n+    std::vector<Vector> normals;\n+    std::vector<std::size_t> indices;\n+    std::vector<Point_status> status;\n+      \n+    for (std::size_t i = 0; i < m_points.size (); ++ i)\n+      if (m_status[i] != SKIPPED)\n+        {\n+          points.push_back (m_points[i]);\n+          normals.push_back (m_normals[i]);\n+          status.push_back (m_status[i]);\n+          if (m_status[i] == RESIDUS)\n+            status.back () = PLANE;\n+          indices.push_back (m_indices[i]);\n+        }\n+      \n+    m_points.swap (points);\n+    m_normals.swap (normals);\n+    m_indices.swap (indices);\n+    m_status.swap (status);\n+  }\n+\n+\n+  void run (double epsilon, double attraction_factor = 3.)\n+  {\n+    if (m_planes.empty ())\n+      return;\n+      \n+    double radius = epsilon * attraction_factor;\n+\n+#ifdef CGAL_PSP3_VERBOSE\n+    std::cerr << \"Computing planar points... \" << std::endl;\n+#endif\n+      \n+    project_inliers ();\n+    resample_planes (epsilon);\n+      \n+#ifdef CGAL_PSP3_VERBOSE\n+    std::cerr << \" -> Done\" << std::endl;\n+    std::cerr << \"Finding adjacent primitives... \" << std::endl;\n+#endif\n+      \n+    find_pairs_of_adjacent_primitives (radius);\n+\n+#ifdef CGAL_PSP3_VERBOSE\n+    std::cerr << \" -> Found \" << m_edges.size () << \" pair(s) of adjacent primitives.\" << std::endl;\n+    std::cerr << \"Computing edges... \" << std::endl;\n+#endif\n+      \n+    compute_edges (epsilon);\n+\n+#ifdef CGAL_PSP3_VERBOSE\n+    std::cerr << \" -> Done\" << std::endl;\n+    std::cerr << \"Creating edge-anchor points... \" << std::endl;\n+    {\n+      std::size_t size_before = m_points.size ();\n+#endif\n+\n+      create_edge_anchor_points (radius, epsilon);\n+\n+#ifdef CGAL_PSP3_VERBOSE\n+      std::cerr << \" -> \" << m_points.size () - size_before << \" anchor point(s) created.\" << std::endl;\n+    }\n+\n+    std::cerr << \"Computating first set of corners... \" << std::endl;\n+#endif\n+      \n+    compute_corners (radius);\n+\n+#ifdef CGAL_PSP3_VERBOSE\n+    std::cerr << \" -> Found \" << m_corners.size () << \" triple(s) of adjacent primitives/edges.\" << std::endl;\n+    std::cerr << \"Merging corners... \" << std::endl;\n+    {\n+      std::size_t size_before = m_points.size ();\n+#endif\n+        \n+      merge_corners (radius);\n+\n+#ifdef CGAL_PSP3_VERBOSE\n+      std::cerr << \" -> \" << m_points.size () - size_before << \" corner point(s) created.\" << std::endl;\n+    }\n+\n+    std::cerr << \"Computing corner directions... \" << std::endl;\n+#endif\n+      \n+    compute_corner_directions (epsilon);\n+\n+#ifdef CGAL_PSP3_VERBOSE\n+    std::cerr << \" -> Done\" << std::endl;\n+    std::cerr << \"Refining sampling... \" << std::endl;\n+#endif\n+      \n+    refine_sampling (epsilon);\n+\n+#ifdef CGAL_PSP3_VERBOSE\n+    std::cerr << \" -> Done\" << std::endl;\n+\n+    std::cerr << \"Cleaning data set... \" << std::endl;\n+#endif\n+      \n+    clean ();\n+\n+#ifdef CGAL_PSP3_VERBOSE\n+    std::cerr << \" -> Done\" << std::endl;\n+#endif\n+  }\n+\n+  void project_inliers ()\n+  {\n+    for(std::size_t i = 0; i < m_planes.size (); ++ i)\n+      for (std::size_t j = 0; j < m_planes[i]->indices_of_assigned_points ().size(); ++ j)\n+        {\n+          std::size_t ind = m_planes[i]->indices_of_assigned_points ()[j];\n+          m_points[ind] = static_cast<Plane> (*(m_planes[i])).projection (m_points[ind]);\n+        }\n+  }\n+\n+  void resample_planes (double epsilon)\n+  {\n+    double grid_length = epsilon * (std::sqrt(2.) - 1e-3);\n+\n+    for (std::size_t c = 0; c < m_planes.size (); ++ c)\n+      {\n+        //plane attributes and 2D projection vectors\n+        Plane plane = static_cast<Plane> (*(m_planes[c]));\n+        Vector vortho = plane.orthogonal_vector();\n+        Vector b1 = plane.base1();\n+        Vector b2 = plane.base2();\n+\t\t\t\n+        b1 = b1 / std::sqrt (b1 * b1);\n+        b2 = b2 / std::sqrt (b2 * b2);\n+\n+        std::vector<Point_2> points_2d;\n+\n+        //storage of the 2D points in \"pt_2d\"\n+        for (std::size_t j = 0; j < m_planes[c]->indices_of_assigned_points ().size(); ++ j)\n+          {\n+            std::size_t ind = m_planes[c]->indices_of_assigned_points ()[j];\n+            const Point& pt = m_points[ind];\n+            points_2d.push_back (Point_2 (b1.x() * pt.x() + b1.y() * pt.y() + b1.z() * pt.z(),\n+                                          b2.x() * pt.x() + b2.y() * pt.y() + b2.z() * pt.z()));\n+          }\n+\n+\n+        //creation of a 2D-grid with cell width = grid_length, and image structures\n+        CGAL::Bbox_2 box_2d = CGAL::bbox_2 (points_2d.begin(), points_2d.end());\n+        std::size_t Nx = (std::size_t)((box_2d.xmax() - box_2d.xmin()) / grid_length) + 1;\n+        std::size_t Ny = (std::size_t)((box_2d.ymax() - box_2d.ymin()) / grid_length) + 1;\n+          \n+        std::vector<std::vector<bool> > Mask (Nx, std::vector<bool> (Ny, false));\n+        std::vector<std::vector<bool> > Mask_border (Nx, std::vector<bool> (Ny, false));\n+        std::vector<std::vector<std::vector<std::size_t> > >\n+          point_map (Nx, std::vector<std::vector<std::size_t> > (Ny, std::vector<std::size_t>()));\n+\n+        //storage of the points in the 2D-grid \"point_map\"\n+        for (std::size_t i = 0; i < points_2d.size(); ++ i)\n+          {\n+            std::size_t ind_x = (std::size_t)((points_2d[i].x() - box_2d.xmin()) / grid_length);\n+            std::size_t ind_y = (std::size_t)((points_2d[i].y() - box_2d.ymin()) / grid_length);\n+            Mask[ind_x][ind_y] = true;\n+            point_map[ind_x][ind_y].push_back (m_planes[c]->indices_of_assigned_points ()[i]);\n+          }\n+\n+        //hole filing in Mask in 4-connexity\n+        for (std::size_t j = 1; j < Ny - 1; ++ j)\n+          for (std::size_t i = 1; i < Nx - 1; ++ i)\n+            if( !Mask[i][j]\n+                && Mask[i-1][j] && Mask[i][j-1]\n+                && Mask[i][j+1] && Mask[i+1][j] )\n+              Mask[i][j]=true;\n+\t\t\t\t\t\n+        //finding mask border in 8-connexity\t\n+        for (std::size_t j = 1; j < Ny - 1; ++ j)\n+          for (std::size_t i = 1; i < Nx - 1; ++ i)\n+            if( Mask[i][j] &&\n+                ( !Mask[i-1][j-1] || !Mask[i-1][j] ||\n+                  !Mask[i-1][j+1] || !Mask[i][j-1] ||\n+                  !Mask[i][j+1] || !Mask[i+1][j-1] ||\n+                  !Mask[i+1][j]|| !Mask[i+1][j+1] ) )\n+              Mask_border[i][j]=true;\n+          \n+        for (std::size_t j = 0; j < Ny; ++ j)\n+          {\n+            if (Mask[0][j])\n+              Mask_border[0][j]=true;\n+            if (Mask[Nx-1][j])\n+              Mask_border[Nx-1][j]=true;\n+          }\n+\n+        for (std::size_t i = 0; i < Nx; ++ i)\n+          {\n+            if(Mask[i][0])\n+              Mask_border[i][0]=true;\n+            if(Mask[i][Ny-1])\n+              Mask_border[i][Ny-1]=true;\n+          }\n+\n+        //saving of points to keep\n+        for (std::size_t j = 0; j < Ny; ++ j)\n+          for (std::size_t i = 0; i < Nx; ++ i)\n+            if( point_map[i][j].size()>0)\n+              {\n+                //inside: recenter (cell center) the first point of the cell and desactivate the others points \n+                if (!Mask_border[i][j] && Mask[i][j])\n+                  {\n+                    double x2pt = (i+0.5) * grid_length + box_2d.xmin();\n+                    double y2pt = (j+0.4) * grid_length + box_2d.ymin();\n+\t\t\t\t\t\t\t\n+                    if (i%2 == 1)\n+                      {\n+                        x2pt = (i+0.5) * grid_length + box_2d.xmin();\n+                        y2pt = (j+0.6) * grid_length + box_2d.ymin();\n+                      }\n+\n+                    FT X1 = x2pt * b1.x() + y2pt * b2.x() - plane.d() * vortho.x();\n+                    FT X2 = x2pt * b1.y() + y2pt * b2.y() - plane.d() * vortho.y();\n+                    FT X3 = x2pt * b1.z() + y2pt * b2.z() - plane.d() * vortho.z();\n+\n+                    std::size_t index_pt = point_map[i][j][0];\n+                    m_points[index_pt] = Point (X1, X2, X3);\n+                    m_normals[index_pt] = m_planes[c]->plane_normal();\n+                    m_status[index_pt] = PLANE;\n+\n+                    for (std::size_t np = 1; np < point_map[i][j].size(); ++ np)\n+                      m_status[point_map[i][j][np]] = SKIPPED;\n+                  }\n+\n+                //border: recenter (barycenter) the first point of the cell and desactivate the others points\n+                else if (Mask_border[i][j] && Mask[i][j])\n+                  {\n+                    std::vector<Point> pts;\n+                    for (std::size_t np = 0; np < point_map[i][j].size(); ++ np)\n+                      pts.push_back (m_points[point_map[i][j][np]]);\n+\n+                    m_points[point_map[i][j][0]] = CGAL::centroid (pts.begin (), pts.end ());\n+                    m_status[point_map[i][j][0]] = PLANE;\n+                    for (std::size_t np = 1; np < point_map[i][j].size(); ++ np)\n+                      m_status[point_map[i][j][np]] = SKIPPED;\n+                  }\n+              }\n+        // point use to filling 4-connexity holes are store in HPS_residus\n+            else if (point_map[i][j].size()==0 && !Mask_border[i][j] && Mask[i][j])\n+              {\n+                double x2pt = (i+0.5) * grid_length + box_2d.xmin();\n+                double y2pt = (j+0.49) * grid_length + box_2d.ymin();\n+                if(i%2==1)\n+                  {\n+                    x2pt = (i+0.5) * grid_length + box_2d.xmin();\n+                    y2pt = (j+0.51) * grid_length + box_2d.ymin();\n+                  }\n+                FT X1 = x2pt * b1.x() + y2pt * b2.x() - plane.d() * vortho.x();\n+                FT X2 = x2pt * b1.y() + y2pt * b2.y() - plane.d() * vortho.y();\n+                FT X3 = x2pt * b1.z() + y2pt * b2.z() - plane.d() * vortho.z();\n+\n+                m_points.push_back (Point (X1, X2, X3));\n+                m_normals.push_back (m_planes[c]->plane_normal());\n+                m_indices.push_back (c);\n+                m_status.push_back (RESIDUS);\n+              }\n+      }\n+\n+  }\n+\n+  void find_pairs_of_adjacent_primitives (double radius)\n+  {\n+    typedef typename Traits::Search_traits Search_traits_base;\n+    typedef Search_traits_adapter <std::size_t, My_point_property_map, Search_traits_base> Search_traits;\n+    typedef CGAL::Kd_tree<Search_traits> Tree;\n+    typedef CGAL::Fuzzy_sphere<Search_traits> Fuzzy_sphere;\n+\n+    My_point_property_map pmap (m_points);\n+\n+    Tree tree (boost::counting_iterator<std::size_t> (0),\n+               boost::counting_iterator<std::size_t> (m_points.size()),\n+               typename Tree::Splitter(),\n+               Search_traits (pmap));\n+\n+    std::vector<std::vector<bool> > adjacency_table (m_planes.size (),\n+                                                     std::vector<bool> (m_planes.size (), false));\n+\n+    //compute a basic adjacency relation (two primitives are neighbors\n+    //if at least one point of the primitive 1 is a k-nearest neighbor\n+    //of a point of the primitive 2 and vice versa)\n+    for (std::size_t i = 0; i < m_points.size (); ++ i)\n+      {\n+        std::size_t ind_i = m_indices[i];\n+\n+        if (ind_i == (std::numeric_limits<std::size_t>::max)())\n+          continue;\n+\n+        Fuzzy_sphere query (i, radius, 0., tree.traits());\n+          \n+        std::vector<std::size_t> neighbors;\n+        tree.search (std::back_inserter (neighbors), query);\n+\n+          \n+        for (std::size_t k = 0; k < neighbors.size(); ++ k)\n+          {\n+            std::size_t ind_k = m_indices[neighbors[k]];\n+            if (ind_k != (std::numeric_limits<std::size_t>::max)() && ind_k != ind_i)\n+              adjacency_table[ind_i][ind_k] = true;\n+          }\n+      }\n+\n+    //verify the symmetry and store the pairs of primitives in\n+    //m_edges\n+    for (std::size_t i = 0; i < adjacency_table.size() - 1; ++ i)\n+      for (std::size_t j = i + 1; j < adjacency_table[i].size(); ++ j)\n+        if ((adjacency_table[i][j]) && (adjacency_table[j][i]))\n+          m_edges.push_back (Edge (i, j));\n+\n+  }\n+\n+  void compute_edges (double epsilon)\n+  {\n+    for (std::size_t i = 0; i < m_edges.size(); ++ i)\n+      {\n+        boost::shared_ptr<Plane_shape> plane1 = m_planes[m_edges[i].planes[0]];\n+        boost::shared_ptr<Plane_shape> plane2 = m_planes[m_edges[i].planes[1]];       \n+\n+        double angle_A = std::acos (std::abs (plane1->plane_normal() * plane2->plane_normal()));\n+        double angle_B = CGAL_PI - angle_A;\n+\n+        CGAL::Object ob_temp = CGAL::intersection (static_cast<Plane>(*plane1),\n+                                                   static_cast<Plane>(*plane2));\n+        if (!assign (m_edges[i].support, ob_temp))\n+          {\n+#ifdef CGAL_PSP3_VERBOSE\n+            std::cerr << \"Warning: bad plane/plane intersection\" << std::endl;\n+#endif\n+            continue;\n+          }\n+\n+        Vector direction_p1 (0., 0., 0.);\n+        for (std::size_t k = 0; k < plane1->indices_of_assigned_points ().size(); ++ k)\n+          {\n+            std::size_t index_point = plane1->indices_of_assigned_points ()[k];\n+              \n+            const Point& point = m_points[index_point];\n+            Point projected = m_edges[i].support.projection (point);\n+            if (std::sqrt (CGAL::squared_distance (point, projected))\n+                < 2 * (std::min) (4., 1 / std::sin (angle_A)) * epsilon\n+                && m_status[index_point] != SKIPPED)\n+              direction_p1 = direction_p1 + Vector (projected, point);\n+          }\n+        if (direction_p1.squared_length() > 0)\n+          direction_p1 = direction_p1 / std::sqrt (direction_p1 * direction_p1);\n+\n+        Vector direction_p2 (0., 0., 0.);\n+        for (std::size_t k = 0; k < plane2->indices_of_assigned_points ().size(); ++ k)\n+          {\n+            std::size_t index_point = plane2->indices_of_assigned_points ()[k];\n+              \n+            const Point& point = m_points[index_point];\n+            Point projected = m_edges[i].support.projection (point);\n+            if (std::sqrt (CGAL::squared_distance (point, projected))\n+                < 2 * std::min (4., 1 / std::sin (angle_A)) * epsilon\n+                && m_status[index_point] != SKIPPED)\n+              direction_p2 = direction_p2 + Vector (projected, point);\n+          }\n+        if (direction_p2.squared_length() > 0)\n+          direction_p2 = direction_p2 / std::sqrt (direction_p2 * direction_p2);\n+\n+        double angle = std::acos (direction_p1 * direction_p2);\n+      \n+        if (direction_p1.squared_length() == 0\n+            || direction_p2.squared_length() == 0\n+            || (std::fabs (angle - angle_A) > 1e-2\n+                && std::fabs (angle - angle_B) > 1e-2 ))\n+          {\n+            m_edges[i].active = false;\n+          }\n+      }\n+  }\n+\n+  void create_edge_anchor_points (double radius, double epsilon)\n+  {\n+    double d_DeltaEdge = std::sqrt (2.) * epsilon;\n+    double r_edge = d_DeltaEdge / 2.;\n+      \n+    for (std::size_t i = 0; i < m_edges.size(); ++ i)\n+      {\n+        boost::shared_ptr<Plane_shape> plane1 = m_planes[m_edges[i].planes[0]];\n+        boost::shared_ptr<Plane_shape> plane2 = m_planes[m_edges[i].planes[1]];\n+\n+        const Line& line = m_edges[i].support;\n+\n+        if (!(m_edges[i].active))\n+          {\n+            continue;\n+          }\n+\n+        Vector normal = 0.5 * plane1->plane_normal () + 0.5 * plane2->plane_normal();\n+\t\t\t\t\t\t\t\n+        //find set of points close (<attraction_radius) to the edge and store in intersection_points\n+        std::vector<std::size_t> intersection_points;\n+        for (std::size_t k = 0; k < plane1->indices_of_assigned_points().size(); ++ k)\n+          {\n+            std::size_t index_point = plane1->indices_of_assigned_points()[k];\n+            const Point& point = m_points[index_point];\n+            Point projected = line.projection (point);\n+            if (CGAL::squared_distance (point, projected) < radius * radius)\n+              intersection_points.push_back (index_point);\n+          }\n+        for (std::size_t k = 0; k < plane2->indices_of_assigned_points().size(); ++ k)\n+          {\n+            std::size_t index_point = plane2->indices_of_assigned_points()[k];\n+            const Point& point = m_points[index_point];\n+            Point projected = line.projection (point);\n+            if (CGAL::squared_distance (point, projected) < radius * radius)\n+              intersection_points.push_back (index_point);\n+          }\n+\n+        if (intersection_points.empty ())\n+          {\n+            continue;\n+          }\n+\n+        const Point& t0 = m_points[intersection_points[0]];\n+        Point t0p = line.projection (t0);\n+        double dmin = 0.;\n+        double dmax = 0.;\n+        Point Pmin = t0p;\n+        Point Pmax = t0p;\n+        Vector dir = line.to_vector ();\n+          \n+        //compute the segment of the edge\n+        for (std::size_t k = 0; k < intersection_points.size(); ++ k)\n+          {\n+            std::size_t ind = intersection_points[k];\n+            const Point& point = m_points[ind];\n+            Point projected = line.projection (point);\n+            double d = Vector (t0p, projected) * dir;\n+                  \n+            if (d < dmin)\n+              {\n+                dmin = d;\n+                Pmin = projected;\n+              }\n+            else if (d > dmax)\n+              {\n+                dmax = d;\n+                Pmax = projected;\n+              }\n+          }\n+\n+        //faire un partitionnement ds une image 1D en votant si\n+        //a la fois au moins un point de plan1 et aussi de plan\n+        //2 tombent dans une case (meme pas que pour les plans).\n+        Segment seg (Pmin,Pmax);\n+        std::size_t number_of_division = (std::size_t)(std::sqrt (seg.squared_length ()) / d_DeltaEdge) + 1;\n+        std::vector<std::vector<std::size_t> > division_tab (number_of_division);\n+\n+        for (std::size_t k = 0; k < intersection_points.size(); ++ k)\n+          {\n+            std::size_t ind = intersection_points[k];\n+            const Point& point = m_points[ind];\n+            Point projected = line.projection (point);\n+\n+            std::size_t tab_index = static_cast<std::size_t>(std::sqrt (CGAL::squared_distance (seg[0], projected))\n+                                                             / d_DeltaEdge);\n+\n+            division_tab[tab_index].push_back (ind);\n+          }\n+\n+        //C1-CREATE the EDGE\n+        std::vector<int> index_of_edge_points;\n+        for (std::size_t j = 0; j < division_tab.size(); ++ j)\n+          {\n+            bool p1found = false, p2found = false;\n+            for (std::size_t k = 0; k < division_tab[j].size () && !(p1found && p2found); ++ k)\n+              {\n+                if (m_indices[division_tab[j][k]] == m_edges[i].planes[0])\n+                  p1found = true;\n+                if (m_indices[division_tab[j][k]] == m_edges[i].planes[1])\n+                  p2found = true;\n+              }\n+\n+            if (!(p1found && p2found))\n+              {\n+                division_tab[j].clear();\n+                continue;\n+              }\n+\n+            Point perfect (seg[0].x() + (seg[1].x() - seg[0].x()) * (j + 0.5) / (double)number_of_division,\n+                           seg[0].y() + (seg[1].y() - seg[0].y()) * (j + 0.5) / (double)number_of_division,\n+                           seg[0].z() + (seg[1].z() - seg[0].z()) * (j + 0.5) / (double)number_of_division);\n+\n+            // keep closest point, replace it by perfect one and skip the others\n+            double dist_min = (std::numeric_limits<double>::max)();\n+            std::size_t index_best = 0;\n+\n+            for (std::size_t k = 0; k < division_tab[j].size(); ++ k)\n+              {\n+                std::size_t inde = division_tab[j][k];\n+\n+                if (CGAL::squared_distance (line, m_points[inde]) < d_DeltaEdge * d_DeltaEdge)\n+                  m_status[inde] = SKIPPED; // Deactive points too close (except best, see below)\n+                  \n+                double distance = CGAL::squared_distance (perfect, m_points[inde]);\n+                if (distance < dist_min)\n+                  {\n+                    dist_min = distance;\n+                    index_best = inde;\n+                  }\n+              }\n+\n+            m_points[index_best] = perfect;\n+            m_normals[index_best] = normal;\n+            m_status[index_best] = EDGE;\n+            m_indices[index_best] = i;\n+            m_edges[i].indices.push_back (index_best);\n+\n+          }\n+\n+        //C2-CREATE the ANCHOR\n+        Vector direction_p1(0,0,0);\n+        Vector direction_p2(0,0,0);\n+\n+        for (std::size_t j = 0; j < division_tab.size() - 1; ++ j)\n+          {\n+            if (division_tab[j].empty () || division_tab[j+1].empty ())\n+              continue;\n+            Point anchor (seg[0].x() + (seg[1].x() - seg[0].x()) * (j + 1) / (double)number_of_division,\n+                          seg[0].y() + (seg[1].y() - seg[0].y()) * (j + 1) / (double)number_of_division,\n+                          seg[0].z() + (seg[1].z() - seg[0].z()) * (j + 1) / (double)number_of_division);\n+              \n+            Plane ortho = seg.supporting_line().perpendicular_plane(anchor); \n+\n+            std::vector<Point> pts1, pts2;\n+            //Computation of the permanent angle and directions\n+            for (std::size_t k = 0; k < division_tab[j].size(); ++ k)\n+              { \n+                std::size_t inde = division_tab[j][k];\n+                std::size_t plane = m_indices[inde];\n+                if (plane == m_edges[i].planes[0])\n+                  pts1.push_back (m_points[inde]);\n+                else if (plane == m_edges[i].planes[1])\n+                  pts2.push_back (m_points[inde]);\n+              }\n+\n+            Line line_p1;\n+            CGAL::Object ob_temp1 = CGAL::intersection (static_cast<Plane> (*plane1), ortho);\n+            if (!assign(line_p1, ob_temp1))\n+              {\n+#ifdef CGAL_PSP3_VERBOSE\n+                std::cerr<<\"Warning: bad plane/plane intersection\"<<std::endl;\n+#endif\n+              }\n+            else if (!(pts1.empty()))\n+              {\n+                Vector vecp1 = line_p1.to_vector();\n+                vecp1 = vecp1/ std::sqrt (vecp1 * vecp1);\n+                Vector vtest1 (anchor, CGAL::centroid (pts1.begin (), pts1.end ()));\n+                if (vtest1 * vecp1<0)\n+                  vecp1 = -vecp1;\n+\n+                direction_p1 = direction_p1+vecp1;\n+\n+                Point anchor1 = anchor + vecp1 * r_edge;\n+                m_points.push_back (anchor1);\n+                m_normals.push_back (m_planes[m_edges[i].planes[0]]->plane_normal());\n+                m_indices.push_back (m_edges[i].planes[0]);\n+                m_status.push_back (PLANE);\n+              }\n+\n+            Line line_p2;\n+            CGAL::Object ob_temp2 = CGAL::intersection (static_cast<Plane> (*plane2),ortho);\n+            if (!assign(line_p2, ob_temp2))\n+              {\n+#ifdef CGAL_PSP3_VERBOSE\n+                std::cerr<<\"Warning: bad plane/plane intersection\"<<std::endl;\n+#endif\n+              }\n+            else if (!(pts2.empty()))\n+              {\n+                Vector vecp2 = line_p2.to_vector();\n+                vecp2 = vecp2 / std::sqrt (vecp2 * vecp2);\n+                Vector vtest2 (anchor, CGAL::centroid (pts2.begin (), pts2.end ()));\n+                if (vtest2 * vecp2 < 0)\n+                  vecp2 =- vecp2;\n+\n+                direction_p2 = direction_p2+vecp2;\n+\n+                Point anchor2 = anchor + vecp2 * r_edge;\n+                m_points.push_back (anchor2);\n+                m_normals.push_back (m_planes[m_edges[i].planes[1]]->plane_normal());\n+                m_indices.push_back (m_edges[i].planes[1]);\n+                m_status.push_back (PLANE);\n+              }\n+\n+          }\n+\n+        //if not information enough (not enough edges to create\n+        //anchor) we unactivate the edge, else we update the angle\n+        //and directions\n+        if ( !(direction_p1.squared_length()>0 || direction_p2.squared_length()>0) )\n+          {\n+            m_edges[i].active = false;\n+            for (std::size_t j = 0; j < m_edges[i].indices.size (); ++ j)\n+              m_status[m_edges[i].indices[j]] = SKIPPED;\n+          }\n+      }\n+  }\n+\n+  void compute_corners (double radius)\n+  {\n+    if (m_edges.size () < 3)\n+      return;\n+\n+    std::vector<std::vector<std::size_t> > plane_edge_adj (m_planes.size());\n+    for (std::size_t i = 0; i < m_edges.size (); ++ i)\n+      if (m_edges[i].active)\n+        {\n+          plane_edge_adj[m_edges[i].planes[0]].push_back (i);\n+          plane_edge_adj[m_edges[i].planes[1]].push_back (i);\n+        }\n+\n+    std::vector<std::set<std::size_t> > edge_adj (m_edges.size ());\n+\n+    for (std::size_t i = 0; i < plane_edge_adj.size (); ++ i)\n+      {\n+        if (plane_edge_adj[i].size () < 2)\n+          continue;\n+          \n+        for (std::size_t j = 0; j < plane_edge_adj[i].size ()- 1; ++ j)\n+          for (std::size_t k = j + 1; k < plane_edge_adj[i].size (); ++ k)\n+            {\n+              edge_adj[plane_edge_adj[i][j]].insert (plane_edge_adj[i][k]);\n+              edge_adj[plane_edge_adj[i][k]].insert (plane_edge_adj[i][j]);\n+            }\n+      }\n+\n+    for (std::size_t i = 0; i < edge_adj.size (); ++ i)\n+      {\n+        if (edge_adj[i].size () < 2)\n+          continue;\n+\n+        std::set<std::size_t>::iterator end = edge_adj[i].end();\n+        end --;\n+          \n+        for (std::set<std::size_t>::iterator jit = edge_adj[i].begin ();\n+             jit != end; ++ jit)\n+          {\n+            std::size_t j = *jit;\n+            if (j < i)\n+              continue;\n+\n+            std::set<std::size_t>::iterator begin = jit;\n+            begin ++;\n+            for (std::set<std::size_t>::iterator kit = begin;\n+                 kit != edge_adj[i].end (); ++ kit)\n+              {\n+                std::size_t k = *kit;\n+                if (k < j)\n+                  continue;\n+                  \n+                std::set<std::size_t> planes;\n+                planes.insert (m_edges[i].planes[0]);\n+                planes.insert (m_edges[i].planes[1]);\n+                planes.insert (m_edges[j].planes[0]);\n+                planes.insert (m_edges[j].planes[1]);\n+                planes.insert (m_edges[k].planes[0]);\n+                planes.insert (m_edges[k].planes[1]);\n+\n+                if (planes.size () == 3) // Triple found\n+                  {\n+                    std::vector<std::size_t> vecplanes (planes.begin (), planes.end ());\n+                    m_corners.push_back (Corner (vecplanes[0], vecplanes[1], vecplanes[2],\n+                                                 i, j, k));\n+                  }\n+              }\n+          }\n+      }\n+\n+\n+    for (std::size_t i = 0; i < m_corners.size (); ++ i)\n+      {\n+        //calcul pt d'intersection des 3 plans\n+        Plane plane1 = static_cast<Plane> (*(m_planes[m_corners[i].planes[0]]));\n+        Plane plane2 = static_cast<Plane> (*(m_planes[m_corners[i].planes[1]]));\n+        Plane plane3 = static_cast<Plane> (*(m_planes[m_corners[i].planes[2]]));\n+        Line line;\n+\n+        CGAL::Object ob_temp = CGAL::intersection(plane1, plane2);","path":"Point_set_processing_3/include/CGAL/structure_point_set.h","position":1136,"original_position":1136,"commit_id":"3dd3ac7529392f6295e1ec3fcb76495559b76ca8","original_commit_id":"3dd3ac7529392f6295e1ec3fcb76495559b76ca8","user":{"login":"sloriot","id":1224632,"avatar_url":"https://avatars.githubusercontent.com/u/1224632?v=3","gravatar_id":"","url":"https://api.github.com/users/sloriot","html_url":"https://github.com/sloriot","followers_url":"https://api.github.com/users/sloriot/followers","following_url":"https://api.github.com/users/sloriot/following{/other_user}","gists_url":"https://api.github.com/users/sloriot/gists{/gist_id}","starred_url":"https://api.github.com/users/sloriot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sloriot/subscriptions","organizations_url":"https://api.github.com/users/sloriot/orgs","repos_url":"https://api.github.com/users/sloriot/repos","events_url":"https://api.github.com/users/sloriot/events{/privacy}","received_events_url":"https://api.github.com/users/sloriot/received_events","type":"User","site_admin":false},"body":"btw,  you should be aware that most of the time the kernel used is inexact. You should check whether this is an issue or not.","created_at":"2016-10-31T08:46:43Z","updated_at":"2016-10-31T08:46:43Z","html_url":"https://github.com/CGAL/cgal/pull/1633#discussion_r85700585","pull_request_url":"https://api.github.com/repos/CGAL/cgal/pulls/1633","_links":{"self":{"href":"https://api.github.com/repos/CGAL/cgal/pulls/comments/85700585"},"html":{"href":"https://github.com/CGAL/cgal/pull/1633#discussion_r85700585"},"pull_request":{"href":"https://api.github.com/repos/CGAL/cgal/pulls/1633"}}},"pull_request":{"url":"https://api.github.com/repos/CGAL/cgal/pulls/1633","id":91564745,"html_url":"https://github.com/CGAL/cgal/pull/1633","diff_url":"https://github.com/CGAL/cgal/pull/1633.diff","patch_url":"https://github.com/CGAL/cgal/pull/1633.patch","issue_url":"https://api.github.com/repos/CGAL/cgal/issues/1633","number":1633,"state":"open","locked":false,"title":"Point Set Structuring","user":{"login":"sgiraudot","id":12890639,"avatar_url":"https://avatars.githubusercontent.com/u/12890639?v=3","gravatar_id":"","url":"https://api.github.com/users/sgiraudot","html_url":"https://github.com/sgiraudot","followers_url":"https://api.github.com/users/sgiraudot/followers","following_url":"https://api.github.com/users/sgiraudot/following{/other_user}","gists_url":"https://api.github.com/users/sgiraudot/gists{/gist_id}","starred_url":"https://api.github.com/users/sgiraudot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sgiraudot/subscriptions","organizations_url":"https://api.github.com/users/sgiraudot/orgs","repos_url":"https://api.github.com/users/sgiraudot/repos","events_url":"https://api.github.com/users/sgiraudot/events{/privacy}","received_events_url":"https://api.github.com/users/sgiraudot/received_events","type":"User","site_admin":false},"body":"Pre-approved [small feature](https://cgal.geometryfactory.com/CGAL/Members/wiki/Features/Small_Features/Point_set_structuring).\r\n\r\n(Note: PR https://github.com/CGAL/cgal/pull/1630 was merged into this branch as it was necessary to avoid some bugs.)","created_at":"2016-10-31T07:05:26Z","updated_at":"2016-10-31T08:54:55Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1e1179528c476532fac97e2f97f0f0c62b450e51","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/CGAL/cgal/pulls/1633/commits","review_comments_url":"https://api.github.com/repos/CGAL/cgal/pulls/1633/comments","review_comment_url":"https://api.github.com/repos/CGAL/cgal/pulls/comments{/number}","comments_url":"https://api.github.com/repos/CGAL/cgal/issues/1633/comments","statuses_url":"https://api.github.com/repos/CGAL/cgal/statuses/3dd3ac7529392f6295e1ec3fcb76495559b76ca8","head":{"label":"sgiraudot:Point_set_processing_3-Point_set_structuring-GF","ref":"Point_set_processing_3-Point_set_structuring-GF","sha":"3dd3ac7529392f6295e1ec3fcb76495559b76ca8","user":{"login":"sgiraudot","id":12890639,"avatar_url":"https://avatars.githubusercontent.com/u/12890639?v=3","gravatar_id":"","url":"https://api.github.com/users/sgiraudot","html_url":"https://github.com/sgiraudot","followers_url":"https://api.github.com/users/sgiraudot/followers","following_url":"https://api.github.com/users/sgiraudot/following{/other_user}","gists_url":"https://api.github.com/users/sgiraudot/gists{/gist_id}","starred_url":"https://api.github.com/users/sgiraudot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sgiraudot/subscriptions","organizations_url":"https://api.github.com/users/sgiraudot/orgs","repos_url":"https://api.github.com/users/sgiraudot/repos","events_url":"https://api.github.com/users/sgiraudot/events{/privacy}","received_events_url":"https://api.github.com/users/sgiraudot/received_events","type":"User","site_admin":false},"repo":{"id":37466040,"name":"cgal","full_name":"sgiraudot/cgal","owner":{"login":"sgiraudot","id":12890639,"avatar_url":"https://avatars.githubusercontent.com/u/12890639?v=3","gravatar_id":"","url":"https://api.github.com/users/sgiraudot","html_url":"https://github.com/sgiraudot","followers_url":"https://api.github.com/users/sgiraudot/followers","following_url":"https://api.github.com/users/sgiraudot/following{/other_user}","gists_url":"https://api.github.com/users/sgiraudot/gists{/gist_id}","starred_url":"https://api.github.com/users/sgiraudot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sgiraudot/subscriptions","organizations_url":"https://api.github.com/users/sgiraudot/orgs","repos_url":"https://api.github.com/users/sgiraudot/repos","events_url":"https://api.github.com/users/sgiraudot/events{/privacy}","received_events_url":"https://api.github.com/users/sgiraudot/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/sgiraudot/cgal","description":"The public CGAL repository","fork":true,"url":"https://api.github.com/repos/sgiraudot/cgal","forks_url":"https://api.github.com/repos/sgiraudot/cgal/forks","keys_url":"https://api.github.com/repos/sgiraudot/cgal/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sgiraudot/cgal/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sgiraudot/cgal/teams","hooks_url":"https://api.github.com/repos/sgiraudot/cgal/hooks","issue_events_url":"https://api.github.com/repos/sgiraudot/cgal/issues/events{/number}","events_url":"https://api.github.com/repos/sgiraudot/cgal/events","assignees_url":"https://api.github.com/repos/sgiraudot/cgal/assignees{/user}","branches_url":"https://api.github.com/repos/sgiraudot/cgal/branches{/branch}","tags_url":"https://api.github.com/repos/sgiraudot/cgal/tags","blobs_url":"https://api.github.com/repos/sgiraudot/cgal/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sgiraudot/cgal/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sgiraudot/cgal/git/refs{/sha}","trees_url":"https://api.github.com/repos/sgiraudot/cgal/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sgiraudot/cgal/statuses/{sha}","languages_url":"https://api.github.com/repos/sgiraudot/cgal/languages","stargazers_url":"https://api.github.com/repos/sgiraudot/cgal/stargazers","contributors_url":"https://api.github.com/repos/sgiraudot/cgal/contributors","subscribers_url":"https://api.github.com/repos/sgiraudot/cgal/subscribers","subscription_url":"https://api.github.com/repos/sgiraudot/cgal/subscription","commits_url":"https://api.github.com/repos/sgiraudot/cgal/commits{/sha}","git_commits_url":"https://api.github.com/repos/sgiraudot/cgal/git/commits{/sha}","comments_url":"https://api.github.com/repos/sgiraudot/cgal/comments{/number}","issue_comment_url":"https://api.github.com/repos/sgiraudot/cgal/issues/comments{/number}","contents_url":"https://api.github.com/repos/sgiraudot/cgal/contents/{+path}","compare_url":"https://api.github.com/repos/sgiraudot/cgal/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sgiraudot/cgal/merges","archive_url":"https://api.github.com/repos/sgiraudot/cgal/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sgiraudot/cgal/downloads","issues_url":"https://api.github.com/repos/sgiraudot/cgal/issues{/number}","pulls_url":"https://api.github.com/repos/sgiraudot/cgal/pulls{/number}","milestones_url":"https://api.github.com/repos/sgiraudot/cgal/milestones{/number}","notifications_url":"https://api.github.com/repos/sgiraudot/cgal/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sgiraudot/cgal/labels{/name}","releases_url":"https://api.github.com/repos/sgiraudot/cgal/releases{/id}","deployments_url":"https://api.github.com/repos/sgiraudot/cgal/deployments","created_at":"2015-06-15T13:14:30Z","updated_at":"2016-10-28T08:03:15Z","pushed_at":"2016-10-31T06:57:57Z","git_url":"git://github.com/sgiraudot/cgal.git","ssh_url":"git@github.com:sgiraudot/cgal.git","clone_url":"https://github.com/sgiraudot/cgal.git","svn_url":"https://github.com/sgiraudot/cgal","homepage":"http://www.cgal.org","size":528269,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":11,"forks":0,"open_issues":11,"watchers":0,"default_branch":"master"}},"base":{"label":"CGAL:master","ref":"master","sha":"1c400420ce6e82ade15c819ffae1025f3a5b9cf3","user":{"login":"CGAL","id":5746664,"avatar_url":"https://avatars.githubusercontent.com/u/5746664?v=3","gravatar_id":"","url":"https://api.github.com/users/CGAL","html_url":"https://github.com/CGAL","followers_url":"https://api.github.com/users/CGAL/followers","following_url":"https://api.github.com/users/CGAL/following{/other_user}","gists_url":"https://api.github.com/users/CGAL/gists{/gist_id}","starred_url":"https://api.github.com/users/CGAL/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CGAL/subscriptions","organizations_url":"https://api.github.com/users/CGAL/orgs","repos_url":"https://api.github.com/users/CGAL/repos","events_url":"https://api.github.com/users/CGAL/events{/privacy}","received_events_url":"https://api.github.com/users/CGAL/received_events","type":"Organization","site_admin":false},"repo":{"id":24677392,"name":"cgal","full_name":"CGAL/cgal","owner":{"login":"CGAL","id":5746664,"avatar_url":"https://avatars.githubusercontent.com/u/5746664?v=3","gravatar_id":"","url":"https://api.github.com/users/CGAL","html_url":"https://github.com/CGAL","followers_url":"https://api.github.com/users/CGAL/followers","following_url":"https://api.github.com/users/CGAL/following{/other_user}","gists_url":"https://api.github.com/users/CGAL/gists{/gist_id}","starred_url":"https://api.github.com/users/CGAL/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CGAL/subscriptions","organizations_url":"https://api.github.com/users/CGAL/orgs","repos_url":"https://api.github.com/users/CGAL/repos","events_url":"https://api.github.com/users/CGAL/events{/privacy}","received_events_url":"https://api.github.com/users/CGAL/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/CGAL/cgal","description":"The public CGAL repository, see the README below","fork":false,"url":"https://api.github.com/repos/CGAL/cgal","forks_url":"https://api.github.com/repos/CGAL/cgal/forks","keys_url":"https://api.github.com/repos/CGAL/cgal/keys{/key_id}","collaborators_url":"https://api.github.com/repos/CGAL/cgal/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/CGAL/cgal/teams","hooks_url":"https://api.github.com/repos/CGAL/cgal/hooks","issue_events_url":"https://api.github.com/repos/CGAL/cgal/issues/events{/number}","events_url":"https://api.github.com/repos/CGAL/cgal/events","assignees_url":"https://api.github.com/repos/CGAL/cgal/assignees{/user}","branches_url":"https://api.github.com/repos/CGAL/cgal/branches{/branch}","tags_url":"https://api.github.com/repos/CGAL/cgal/tags","blobs_url":"https://api.github.com/repos/CGAL/cgal/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/CGAL/cgal/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/CGAL/cgal/git/refs{/sha}","trees_url":"https://api.github.com/repos/CGAL/cgal/git/trees{/sha}","statuses_url":"https://api.github.com/repos/CGAL/cgal/statuses/{sha}","languages_url":"https://api.github.com/repos/CGAL/cgal/languages","stargazers_url":"https://api.github.com/repos/CGAL/cgal/stargazers","contributors_url":"https://api.github.com/repos/CGAL/cgal/contributors","subscribers_url":"https://api.github.com/repos/CGAL/cgal/subscribers","subscription_url":"https://api.github.com/repos/CGAL/cgal/subscription","commits_url":"https://api.github.com/repos/CGAL/cgal/commits{/sha}","git_commits_url":"https://api.github.com/repos/CGAL/cgal/git/commits{/sha}","comments_url":"https://api.github.com/repos/CGAL/cgal/comments{/number}","issue_comment_url":"https://api.github.com/repos/CGAL/cgal/issues/comments{/number}","contents_url":"https://api.github.com/repos/CGAL/cgal/contents/{+path}","compare_url":"https://api.github.com/repos/CGAL/cgal/compare/{base}...{head}","merges_url":"https://api.github.com/repos/CGAL/cgal/merges","archive_url":"https://api.github.com/repos/CGAL/cgal/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/CGAL/cgal/downloads","issues_url":"https://api.github.com/repos/CGAL/cgal/issues{/number}","pulls_url":"https://api.github.com/repos/CGAL/cgal/pulls{/number}","milestones_url":"https://api.github.com/repos/CGAL/cgal/milestones{/number}","notifications_url":"https://api.github.com/repos/CGAL/cgal/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/CGAL/cgal/labels{/name}","releases_url":"https://api.github.com/repos/CGAL/cgal/releases{/id}","deployments_url":"https://api.github.com/repos/CGAL/cgal/deployments","created_at":"2014-10-01T12:22:47Z","updated_at":"2016-10-30T15:24:31Z","pushed_at":"2016-10-31T07:05:26Z","git_url":"git://github.com/CGAL/cgal.git","ssh_url":"git@github.com:CGAL/cgal.git","clone_url":"https://github.com/CGAL/cgal.git","svn_url":"https://github.com/CGAL/cgal","homepage":"https://github.com/CGAL/cgal#readme","size":562149,"stargazers_count":368,"watchers_count":368,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":183,"mirror_url":null,"open_issues_count":159,"forks":183,"open_issues":159,"watchers":368,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/CGAL/cgal/pulls/1633"},"html":{"href":"https://github.com/CGAL/cgal/pull/1633"},"issue":{"href":"https://api.github.com/repos/CGAL/cgal/issues/1633"},"comments":{"href":"https://api.github.com/repos/CGAL/cgal/issues/1633/comments"},"review_comments":{"href":"https://api.github.com/repos/CGAL/cgal/pulls/1633/comments"},"review_comment":{"href":"https://api.github.com/repos/CGAL/cgal/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/CGAL/cgal/pulls/1633/commits"},"statuses":{"href":"https://api.github.com/repos/CGAL/cgal/statuses/3dd3ac7529392f6295e1ec3fcb76495559b76ca8"}}}}
{"actor":{"display_login":"sloriot"}}