mirror of https://github.com/CGAL/cgal
Merge pull request #5071 from sgiraudot/PSP-Scanline_orient_normals-GF
[Small Feature] Scanline Orient Normals
This commit is contained in:
commit
2f73dab22f
|
|
@ -152,6 +152,8 @@ CGAL_add_named_parameter(inspector_t, inspector, inspector)
|
|||
CGAL_add_named_parameter(logger_t, logger, logger)
|
||||
CGAL_add_named_parameter(pointmatcher_config_t, pointmatcher_config, pointmatcher_config)
|
||||
CGAL_add_named_parameter(adjacencies_t, adjacencies, adjacencies)
|
||||
CGAL_add_named_parameter(scan_angle_t, scan_angle_map, scan_angle_map)
|
||||
CGAL_add_named_parameter(scanline_id_t, scanline_id_map, scanline_id_map)
|
||||
|
||||
// List of named parameters used in Surface_mesh_approximation package
|
||||
CGAL_add_named_parameter(verbose_level_t, verbose_level, verbose_level)
|
||||
|
|
|
|||
|
|
@ -268,6 +268,8 @@ void test(const NamedParameters& np)
|
|||
check_same_type<9032>(get_parameter(np, CGAL::internal_np::inspector));
|
||||
check_same_type<9033>(get_parameter(np, CGAL::internal_np::logger));
|
||||
check_same_type<9034>(get_parameter(np, CGAL::internal_np::maximum_normal_deviation));
|
||||
check_same_type<9035>(get_parameter(np, CGAL::internal_np::scan_angle_map));
|
||||
check_same_type<9036>(get_parameter(np, CGAL::internal_np::scanline_id_map));
|
||||
}
|
||||
|
||||
int main()
|
||||
|
|
@ -396,6 +398,8 @@ int main()
|
|||
.inspector(A<9032>(9032))
|
||||
.logger(A<9033>(9033))
|
||||
.maximum_normal_deviation(A<9034>(9034))
|
||||
.scan_angle_map(A<9035>(9035))
|
||||
.scanline_id_map(A<9036>(9036))
|
||||
.maximum_number_of_faces(A<78910>(78910))
|
||||
);
|
||||
return EXIT_SUCCESS;
|
||||
|
|
|
|||
|
|
@ -184,9 +184,21 @@ PCA is faster but jet is more accurate in the presence of high
|
|||
curvatures. These function only estimates the _direction_ of the
|
||||
normals, not their orientation (the orientation of the vectors might
|
||||
not be locally consistent). To properly orient the normals, the
|
||||
function `mst_orient_normals()` can be used. Notice that it can also
|
||||
be used directly on input normals if their orientation is not
|
||||
consistent.
|
||||
following functions can be used:
|
||||
|
||||
- `mst_orient_normals()`
|
||||
- `scanline_orient_normals()`
|
||||
|
||||
The first one uses a _minimum spanning tree_ to consistently propagate
|
||||
the orientation of normals in an increasingly large neighborhood. In
|
||||
the case of data with many sharp features and occlusions (which are
|
||||
common in airborne LIDAR data, for example), the second algorithm may
|
||||
produce better results: it takes advantage of point clouds which are
|
||||
ordered into scanlines to estimate the line of sight of each point and
|
||||
thus to orient normals accordingly.
|
||||
|
||||
Notice that these can also be used directly on input normals if their
|
||||
orientation is not consistent.
|
||||
|
||||
\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Normal estimation
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ Release date: December 2020
|
|||
- Added an optional range parameter to `CGAL::Polygon_mesh_processing::stitch_borders()`,
|
||||
which can be used to specify which boundary cycles are eligible for stitching.
|
||||
|
||||
### [Point Set Processing](https://doc.cgal.org/5.2/Manual/packages.html#PkgPointSetProcessing3)
|
||||
- Added a function [`CGAL::scanline_orient_normals()`](https://doc.cgal.org/5.2/Point_set_processing_3/group__PkgPointSetProcessing3Algorithms.html#ga221d4efde44f42aefe153cb927138efe) that orients a point cloud by estimating a line of sight.
|
||||
|
||||
### [Classification](https://doc.cgal.org/5.2/Manual/packages.html#PkgClassification)
|
||||
|
||||
- **Breaking change**: new IO format for `ETHZ::Random_Forest` (a
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ format.
|
|||
- `CGAL::jet_estimate_normals()`
|
||||
- `CGAL::pca_estimate_normals()`
|
||||
- `CGAL::mst_orient_normals()`
|
||||
- `CGAL::scanline_orient_normals()`
|
||||
- `CGAL::edge_aware_upsample_point_set()`
|
||||
- `CGAL::compute_vcm()`
|
||||
- `CGAL::vcm_estimate_normals()`
|
||||
|
|
@ -101,4 +102,3 @@ format.
|
|||
- `CGAL::make_las_point_writer()`
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -832,6 +832,8 @@ of nearest neighbors or a fixed spherical neighborhood radius.
|
|||
|
||||
\section Point_set_processing_3NormalOrientation Normal Orientation
|
||||
|
||||
\subsection Point_set_processing_3Mst_orient_normals Minimum Spanning Tree
|
||||
|
||||
Function `mst_orient_normals()` orients the normals of a set of
|
||||
points with unoriented normals using the method described by Hoppe et
|
||||
al. in <I>Surface reconstruction from unorganized points</I> \cgalCite{cgal:hddms-srup-92}.
|
||||
|
|
@ -846,7 +848,7 @@ the normals which cannot be successfully oriented.
|
|||
Normal orientation of a sampled cube surface. Left: unoriented normals. Right: orientation of right face normals is propagated to bottom face.
|
||||
\cgalFigureEnd
|
||||
|
||||
\subsection Point_set_processing_3Example_normals Example
|
||||
\subsubsection Point_set_processing_3Example_normals Example
|
||||
|
||||
The following example reads a point set from a file, estimates the
|
||||
normals through PCA (either over the 18 nearest neighbors or using a
|
||||
|
|
@ -854,8 +856,31 @@ spherical neighborhood radius of twice the average spacing) and
|
|||
orients the normals:
|
||||
\cgalExample{Point_set_processing_3/normals_example.cpp}
|
||||
|
||||
\subsection Point_set_processing_3Scanline_orient_normals Scanline
|
||||
|
||||
The minimum spanning tree results can give suboptimal results on point
|
||||
clouds with many sharp features and occlusions, which typically
|
||||
happens on airborne acquired urban datasets.
|
||||
|
||||
`scanline_orient_normals()` is an alternative method specialized for
|
||||
point sets which are ordered in scanline aligned on the XY-plane. It
|
||||
can take advantage of LAS properties provided by some LIDAR scanner
|
||||
and is the best choice of normal orientation when dealing with 2.5D
|
||||
urban scenes.
|
||||
|
||||
\cgalFigureBegin{Point_set_processing_3figmst_scanline_orient_normals,scanline_orient_normals.png}
|
||||
Normal orientation of a LIDAR scanline. The point cloud is a typical
|
||||
airborne LIDAR input, sampling a building without normal information
|
||||
and with many occlusions (especially on vertical walls).
|
||||
\cgalFigureEnd
|
||||
|
||||
\subsubsection Point_set_processing_3Example_scanline_normals Example
|
||||
|
||||
The following example reads a point set from a LAS file, estimates the
|
||||
normals through Jet Fitting and outputs in PLY format the orientation
|
||||
results of all the variants of `scanline_orient_normals()`:
|
||||
|
||||
\cgalExample{Point_set_processing_3/orient_scanlines_example.cpp}
|
||||
|
||||
\section Point_set_processing_3Upsampling Upsampling
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
\example Point_set_processing_3/hierarchy_simplification_example.cpp
|
||||
\example Point_set_processing_3/jet_smoothing_example.cpp
|
||||
\example Point_set_processing_3/normals_example.cpp
|
||||
\example Point_set_processing_3/orient_scanlines_example.cpp
|
||||
\example Point_set_processing_3/wlop_simplify_and_regularize_point_set_example.cpp
|
||||
\example Point_set_processing_3/bilateral_smooth_point_set_example.cpp
|
||||
\example Point_set_processing_3/edge_aware_upsample_point_set_example.cpp
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
|
|
@ -59,18 +59,24 @@ if ( CGAL_FOUND )
|
|||
target_link_libraries(${target} ${CGAL_libs})
|
||||
endforeach()
|
||||
|
||||
# Use Eigen
|
||||
find_package(Eigen3 3.1.0) #(requires 3.1.0 or greater)
|
||||
include(CGAL_Eigen_support)
|
||||
|
||||
find_package(LASLIB)
|
||||
include(CGAL_LASLIB_support)
|
||||
if (TARGET CGAL::LASLIB_support)
|
||||
add_executable( read_las_example "read_las_example.cpp" )
|
||||
target_link_libraries(read_las_example ${CGAL_libs} CGAL::LASLIB_support)
|
||||
if (TARGET CGAL::Eigen_support)
|
||||
add_executable( orient_scanlines_example "orient_scanlines_example.cpp" )
|
||||
target_link_libraries(orient_scanlines_example ${CGAL_libs}
|
||||
CGAL::Eigen_support CGAL::LASLIB_support)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "NOTICE : the LAS reader test requires LASlib and will not be compiled.")
|
||||
endif()
|
||||
|
||||
# Use Eigen
|
||||
find_package(Eigen3 3.1.0) #(requires 3.1.0 or greater)
|
||||
include(CGAL_Eigen_support)
|
||||
if (TARGET CGAL::Eigen_support)
|
||||
set(CGAL_libs ${CGAL_libs} CGAL::Eigen_support)
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -0,0 +1,88 @@
|
|||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/IO/read_las_points.h>
|
||||
#include <CGAL/IO/write_ply_points.h>
|
||||
#include <CGAL/jet_estimate_normals.h>
|
||||
#include <CGAL/scanline_orient_normals.h>
|
||||
|
||||
using Kernel = CGAL::Simple_cartesian<double>;
|
||||
using Point_3 = Kernel::Point_3;
|
||||
using Vector_3 = Kernel::Vector_3;
|
||||
using Point_with_info = std::tuple<Point_3, Vector_3, float, unsigned char>;
|
||||
using Point_map = CGAL::Nth_of_tuple_property_map<0, Point_with_info>;
|
||||
using Normal_map = CGAL::Nth_of_tuple_property_map<1, Point_with_info>;
|
||||
using Scan_angle_map = CGAL::Nth_of_tuple_property_map<2, Point_with_info>;
|
||||
using Scanline_id_map = CGAL::Nth_of_tuple_property_map<3, Point_with_info>;
|
||||
|
||||
void dump (const char* filename, const std::vector<Point_with_info>& points)
|
||||
{
|
||||
std::ofstream ofile (filename, std::ios::binary);
|
||||
CGAL::set_binary_mode(ofile);
|
||||
CGAL::write_ply_points
|
||||
(ofile, points,
|
||||
CGAL::parameters::point_map (Point_map()).
|
||||
normal_map (Normal_map()));
|
||||
|
||||
}
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
std::string fname (argc > 1 ? argv[1] : "data/urban.las");
|
||||
|
||||
std::vector<Point_with_info> points;
|
||||
|
||||
std::cerr << "Reading input file " << fname << std::endl;
|
||||
std::ifstream ifile (fname, std::ios::binary);
|
||||
if (!ifile ||
|
||||
!CGAL::read_las_points_with_properties
|
||||
(ifile, std::back_inserter (points),
|
||||
CGAL::make_las_point_reader (Point_map()),
|
||||
std::make_pair (Scan_angle_map(),
|
||||
CGAL::LAS_property::Scan_angle()),
|
||||
std::make_pair (Scanline_id_map(),
|
||||
CGAL::LAS_property::Scan_direction_flag())))
|
||||
{
|
||||
std::cerr << "Can't read " << fname << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
std::cerr << "Estimating normals" << std::endl;
|
||||
CGAL::jet_estimate_normals<CGAL::Parallel_if_available_tag>
|
||||
(points, 12,
|
||||
CGAL::parameters::point_map (Point_map()).
|
||||
normal_map (Normal_map()));
|
||||
|
||||
std::cerr << "Orienting normals using scan angle and direction flag" << std::endl;
|
||||
CGAL::scanline_orient_normals
|
||||
(points,
|
||||
CGAL::parameters::point_map (Point_map()).
|
||||
normal_map (Normal_map()).
|
||||
scan_angle_map (Scan_angle_map()).
|
||||
scanline_id_map (Scanline_id_map()));
|
||||
dump("out_angle_and_flag.ply", points);
|
||||
|
||||
std::cerr << "Orienting normals using scan direction flag only" << std::endl;
|
||||
CGAL::scanline_orient_normals
|
||||
(points,
|
||||
CGAL::parameters::point_map (Point_map()).
|
||||
normal_map (Normal_map()).
|
||||
scanline_id_map (Scanline_id_map()));
|
||||
dump("out_flag.ply", points);
|
||||
|
||||
std::cerr << "Orienting normals using scan angle only" << std::endl;
|
||||
CGAL::scanline_orient_normals
|
||||
(points,
|
||||
CGAL::parameters::point_map (Point_map()).
|
||||
normal_map (Normal_map()).
|
||||
scan_angle_map (Scan_angle_map()));
|
||||
dump("out_angle.ply", points);
|
||||
|
||||
std::cerr << "Orienting normals using no additional info" << std::endl;
|
||||
CGAL::scanline_orient_normals
|
||||
(points,
|
||||
CGAL::parameters::point_map (Point_map()).
|
||||
normal_map (Normal_map()));
|
||||
dump("out_nothing.ply", points);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,564 @@
|
|||
// Copyright (c) 2020 GeometryFactory Sarl (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||
//
|
||||
// Author(s) : Simon Giraudot
|
||||
|
||||
#ifndef CGAL_SCANLINE_ORIENT_NORMALS_H
|
||||
#define CGAL_SCANLINE_ORIENT_NORMALS_H
|
||||
|
||||
#include <CGAL/license/Point_set_processing_3.h>
|
||||
|
||||
#include <CGAL/squared_distance_3.h>
|
||||
|
||||
#include <CGAL/boost/graph/Named_function_parameters.h>
|
||||
#include <CGAL/boost/graph/named_params_helper.h>
|
||||
|
||||
#include <CGAL/linear_least_squares_fitting_3.h>
|
||||
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
|
||||
#if defined(CGAL_EIGEN3_ENABLED) || defined(DOXYGEN_RUNNING)
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include <Eigen/IterativeLinearSolvers>
|
||||
|
||||
//#define CGAL_SCANLINE_ORIENT_VERBOSE
|
||||
|
||||
namespace CGAL
|
||||
{
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
namespace Point_set_processing_3
|
||||
{
|
||||
|
||||
namespace internal
|
||||
{
|
||||
|
||||
template <typename Vector_3>
|
||||
const Vector_3& vertical_vector()
|
||||
{
|
||||
static Vector_3 v(0, 0, 1);
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename Iterator, typename PointMap,
|
||||
typename ScanlineIDMap>
|
||||
bool is_end_of_scanline (Iterator scanline_begin, Iterator it,
|
||||
PointMap,
|
||||
ScanlineIDMap scanline_id_map,
|
||||
const Tag_false&) // no fallback
|
||||
{
|
||||
return (get (scanline_id_map, *scanline_begin)
|
||||
!= get (scanline_id_map, *it));
|
||||
}
|
||||
|
||||
template <typename Iterator, typename PointMap,
|
||||
typename ScanlineIDMap>
|
||||
bool is_end_of_scanline (Iterator scanline_begin, Iterator it,
|
||||
PointMap point_map,
|
||||
ScanlineIDMap,
|
||||
const Tag_true&) // fallback
|
||||
{
|
||||
using Point_3 = typename boost::property_traits<PointMap>::value_type;
|
||||
using Vector_3 = typename Kernel_traits<Point_3>::Kernel::Vector_3;
|
||||
|
||||
if (std::distance (scanline_begin, it) < 3)
|
||||
return false;
|
||||
|
||||
Iterator n_minus_1 = it; -- n_minus_1;
|
||||
Iterator n_minus_2 = n_minus_1; -- n_minus_2;
|
||||
Iterator n_minus_3 = n_minus_2; -- n_minus_3;
|
||||
|
||||
const Point_3& p_minus_1 = get(point_map, *n_minus_1);
|
||||
const Point_3& p_minus_2 = get(point_map, *n_minus_2);
|
||||
const Point_3& p_minus_3 = get(point_map, *n_minus_3);
|
||||
|
||||
// End of scanline reached if inversion of direction
|
||||
Vector_3 v32 (p_minus_3, p_minus_2);
|
||||
v32 = Vector_3 (v32.x(), v32.y(), 0);
|
||||
Vector_3 v21 (p_minus_2, p_minus_1);
|
||||
v21 = Vector_3 (v21.x(), v21.y(), 0);
|
||||
|
||||
return (v32 * v21 < 0);
|
||||
}
|
||||
|
||||
template <typename Iterator, typename PointMap>
|
||||
std::pair<typename Kernel_traits<typename boost::property_traits
|
||||
<PointMap>::value_type>::Kernel::Vector_3,
|
||||
typename Kernel_traits<typename boost::property_traits
|
||||
<PointMap>::value_type>::Kernel::Vector_3>
|
||||
scanline_base (Iterator begin, Iterator end,
|
||||
PointMap point_map)
|
||||
{
|
||||
using Point_3 = typename boost::property_traits<PointMap>::value_type;
|
||||
using Kernel = typename Kernel_traits<Point_3>::Kernel;
|
||||
using Vector_3 = typename Kernel::Vector_3;
|
||||
|
||||
using Line_3 = typename Kernel::Line_3;
|
||||
using Plane_3 = typename Kernel::Plane_3;
|
||||
|
||||
const double limit = CGAL_PI * 30. / 180.;
|
||||
|
||||
Line_3 pca2;
|
||||
linear_least_squares_fitting_3
|
||||
(boost::make_transform_iterator
|
||||
(begin, Property_map_to_unary_function<PointMap>(point_map)),
|
||||
boost::make_transform_iterator
|
||||
(end, Property_map_to_unary_function<PointMap>(point_map)),
|
||||
pca2, Dimension_tag<0>());
|
||||
|
||||
Vector_3 pca_direction = pca2.to_vector();
|
||||
pca_direction = Vector_3 (pca_direction.x(), pca_direction.y(), 0);
|
||||
pca_direction = pca_direction / CGAL::approximate_sqrt(pca_direction.squared_length());
|
||||
|
||||
Plane_3 pca3;
|
||||
linear_least_squares_fitting_3
|
||||
(boost::make_transform_iterator
|
||||
(begin, Property_map_to_unary_function<PointMap>(point_map)),
|
||||
boost::make_transform_iterator
|
||||
(end, Property_map_to_unary_function<PointMap>(point_map)),
|
||||
pca3, Dimension_tag<0>());
|
||||
|
||||
Vector_3 orthogonal = pca3.orthogonal_vector();
|
||||
Vector_3 vertical = CGAL::cross_product (pca_direction, orthogonal);
|
||||
if (vertical * vertical_vector<Vector_3>() < 0)
|
||||
vertical = -vertical;
|
||||
|
||||
vertical = vertical / CGAL::approximate_sqrt(vertical.squared_length());
|
||||
|
||||
if (std::acos(vertical * vertical_vector<Vector_3>()) < limit)
|
||||
return std::make_pair (pca_direction, vertical);
|
||||
|
||||
// if plane diverges from the vertical more than 30 degrees, then
|
||||
// fallback to 0 0 1 vertical vector
|
||||
|
||||
// Dummy begin->end vector version
|
||||
Iterator last = end; -- last;
|
||||
const Point_3& pbegin = get (point_map, *begin);
|
||||
const Point_3& plast = get (point_map, *last);
|
||||
Vector_3 direction (Point_3 (pbegin.x(), pbegin.y(), 0),
|
||||
Point_3 (plast.x(), plast.y(), 0));
|
||||
direction = direction / CGAL::approximate_sqrt (direction.squared_length());
|
||||
|
||||
return std::make_pair (direction, vertical_vector<Vector_3>());
|
||||
}
|
||||
|
||||
template <typename Vector_3>
|
||||
bool normal_along_scanline_is_inverted
|
||||
(const Vector_3& normal, const Vector_3& line_of_sight)
|
||||
{
|
||||
return (line_of_sight * normal < 0);
|
||||
}
|
||||
|
||||
template <typename Iterator, typename PointMap, typename Vector_3>
|
||||
std::pair<typename boost::property_traits<PointMap>::value_type, bool>
|
||||
estimate_scan_position (Iterator begin, Iterator end, PointMap point_map,
|
||||
const std::vector<Vector_3>& lines_of_sight)
|
||||
{
|
||||
using Point_3 = typename boost::property_traits<PointMap>::value_type;
|
||||
typedef Eigen::Matrix3d Matrix;
|
||||
typedef Eigen::Vector3d Vector;
|
||||
typedef Eigen::ConjugateGradient<Matrix> Solver;
|
||||
|
||||
Matrix R;
|
||||
R << 0, 0, 0, 0, 0, 0, 0, 0, 0;
|
||||
Vector Q;
|
||||
Q << 0, 0, 0;
|
||||
|
||||
std::size_t idx = 0;
|
||||
for (Iterator it = begin; it != end; ++ it, ++ idx)
|
||||
{
|
||||
const Point_3& p = get (point_map, *it);
|
||||
const Vector_3& v = lines_of_sight[idx];
|
||||
|
||||
Vector n;
|
||||
n << v.x(), v.y(), v.z();
|
||||
|
||||
Matrix I_nnt = Matrix::Identity() - n * n.transpose();
|
||||
Vector a;
|
||||
a << p.x(), p.y(), p.z();
|
||||
|
||||
R += I_nnt;
|
||||
Q += I_nnt * a;
|
||||
}
|
||||
|
||||
Solver solver(R);
|
||||
|
||||
if (solver.info() != Eigen::Success)
|
||||
return std::make_pair (ORIGIN, false);
|
||||
|
||||
Vector p = solver.solve(Q);
|
||||
if (solver.info() != Eigen::Success)
|
||||
return std::make_pair (ORIGIN, false);
|
||||
|
||||
return std::make_pair (Point_3(p(0), p(1), p(2)), true);
|
||||
}
|
||||
|
||||
|
||||
template <typename Iterator, typename PointMap, typename NormalMap,
|
||||
typename ScanAngleMap>
|
||||
void orient_scanline (Iterator begin, Iterator end,
|
||||
PointMap point_map,
|
||||
NormalMap normal_map,
|
||||
ScanAngleMap scan_angle_map,
|
||||
const Tag_false&) // no fallback scan angle
|
||||
{
|
||||
using Point_3 = typename boost::property_traits<PointMap>::value_type;
|
||||
using Vector_3 = typename boost::property_traits<NormalMap>::value_type;
|
||||
|
||||
Vector_3 direction;
|
||||
Vector_3 vertical;
|
||||
std::tie (direction, vertical)
|
||||
= scanline_base (begin, end, point_map);
|
||||
|
||||
std::vector<Vector_3> lines_of_sight;
|
||||
lines_of_sight.reserve (std::distance (begin, end));
|
||||
|
||||
double mean_z = 0;
|
||||
for (Iterator it = begin; it != end; ++ it)
|
||||
{
|
||||
double angle = CGAL_PI * static_cast<double>(get (scan_angle_map, *it)) / 180.;
|
||||
Vector_3 los = direction * std::sin(angle) + vertical * std::cos(angle);
|
||||
lines_of_sight.push_back (los);
|
||||
mean_z += get (point_map, *it).z();
|
||||
}
|
||||
mean_z /= std::distance (begin, end);
|
||||
|
||||
#ifdef CGAL_SCANORIENT_DUMP_RANDOM_SCANLINES
|
||||
if (rand() % 1000 == 0 && std::distance(begin, end) > 10)
|
||||
{
|
||||
std::ofstream ofile ("scanline.polylines.txt");
|
||||
ofile.precision(18);
|
||||
ofile << std::distance (begin, end);
|
||||
for (Iterator it = begin; it != end; ++ it)
|
||||
ofile << " " << get(point_map, *it);
|
||||
ofile << std::endl;
|
||||
|
||||
std::ofstream ofile2 ("base.polylines.txt");
|
||||
Point_3 orig = get (point_map, *(begin + std::distance(begin, end) / 2));
|
||||
double dist = CGAL::approximate_sqrt (CGAL::squared_distance
|
||||
(get(point_map, *begin), orig));
|
||||
|
||||
ofile2.precision(18);
|
||||
ofile2 << "2 " << orig << " " << orig + direction * dist << std::endl
|
||||
<< "2 " << orig << " " << orig + vertical * dist << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
Point_3 scan_position;
|
||||
bool solver_success;
|
||||
std::tie (scan_position, solver_success)
|
||||
= estimate_scan_position (begin, end, point_map, lines_of_sight);
|
||||
|
||||
// If solver failed OR if scan position is detected under the
|
||||
// scanline (obviously wrong)
|
||||
if (!solver_success || scan_position.z() < mean_z)
|
||||
{
|
||||
#ifdef CGAL_SCANLINE_ORIENT_VERBOSE
|
||||
if (!solver_success)
|
||||
std::cerr << "Inverting because olver failed: ";
|
||||
else
|
||||
std::cerr << "Inverting because scanner under scanline: ";
|
||||
#endif
|
||||
|
||||
direction = -direction;
|
||||
std::size_t idx = 0;
|
||||
for (Iterator it = begin; it != end; ++ it, ++ idx)
|
||||
{
|
||||
double angle = CGAL_PI * static_cast<double>(get (scan_angle_map, *it)) / 180.;
|
||||
Vector_3 los = direction * std::sin(angle) + vertical * std::cos(angle);
|
||||
lines_of_sight[idx] = los;
|
||||
}
|
||||
|
||||
std::tie (scan_position, solver_success)
|
||||
= estimate_scan_position (begin, end, point_map, lines_of_sight);
|
||||
|
||||
#ifdef CGAL_SCANLINE_ORIENT_VERBOSE
|
||||
if (solver_success && scan_position.z() > mean_z)
|
||||
std::cerr << "SOLVED" << std::endl;
|
||||
else if (!solver_success)
|
||||
std::cerr << "FAILED, solver failure" << std::endl;
|
||||
else
|
||||
std::cerr << "FAILED, scanner under scanline" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::size_t idx = 0;
|
||||
for (Iterator it = begin; it != end; ++ it, ++ idx)
|
||||
{
|
||||
const Vector_3 los = lines_of_sight[idx];
|
||||
const Vector_3& normal = get (normal_map, *it);
|
||||
if (normal_along_scanline_is_inverted (normal, los))
|
||||
put (normal_map, *it, -normal);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iterator, typename PointMap, typename NormalMap,
|
||||
typename ScanAngleMap>
|
||||
void orient_scanline (Iterator begin, Iterator end,
|
||||
PointMap point_map,
|
||||
NormalMap normal_map,
|
||||
ScanAngleMap,
|
||||
const Tag_true&) // fallback scan angle
|
||||
{
|
||||
using Point_3 = typename boost::property_traits<PointMap>::value_type;
|
||||
using Vector_3 = typename boost::property_traits<NormalMap>::value_type;
|
||||
|
||||
Vector_3 direction;
|
||||
Vector_3 vertical;
|
||||
std::tie (direction, vertical)
|
||||
= scanline_base (begin, end, point_map);
|
||||
|
||||
// Estimate scanner position:
|
||||
// average XY-projected point, located above
|
||||
double mean_x = 0.;
|
||||
double mean_y = 0.;
|
||||
double max_z = -(std::numeric_limits<double>::max)();
|
||||
std::size_t nb = 0;
|
||||
|
||||
for (Iterator it = begin; it != end; ++ it)
|
||||
{
|
||||
const Point_3& p = get (point_map, *it);
|
||||
mean_x += p.x();
|
||||
mean_y += p.y();
|
||||
max_z = (std::max)(max_z, p.z());
|
||||
++ nb;
|
||||
}
|
||||
|
||||
Iterator last = end; -- last;
|
||||
double length = CGAL::approximate_sqrt (CGAL::squared_distance
|
||||
(get (point_map, *begin),
|
||||
get (point_map, *last)));
|
||||
|
||||
Point_3 scan_position (mean_x / nb, mean_y / nb,
|
||||
max_z + length * 2);
|
||||
|
||||
for (Iterator it = begin; it != end; ++ it)
|
||||
{
|
||||
Vector_3 line_of_sight (get(point_map, *it), scan_position);
|
||||
const Vector_3& normal = get (normal_map, *it);
|
||||
if (normal_along_scanline_is_inverted (normal, line_of_sight))
|
||||
put (normal_map, *it, -normal);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace Point_set_processing_3
|
||||
/// \endcond
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Public section
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
\ingroup PkgPointSetProcessing3Algorithms
|
||||
|
||||
orients the normals of the range of `points` by estimating a line
|
||||
of sight and checking its consistency with the current normal orientation.
|
||||
|
||||
\warning This function requires the input `points` to be ordered
|
||||
along scanlines aligned on the XY-plane. It is typically designed
|
||||
for 2.5D urban datasets acquired through, for example, airborne
|
||||
LIDAR devices.
|
||||
|
||||
First, scanlines are estimated as subranges of `points` by
|
||||
iterating on `points`:
|
||||
|
||||
- if the named parameter `scanline_id_map` is provided, the range
|
||||
is cutted everytime the id changes.
|
||||
|
||||
- if no scanline ID map is provided, a fallback method simply cuts
|
||||
the range everytime 3 consecutive points form an acute angle on
|
||||
the projected XY-plane. This fallback method gives suboptimal
|
||||
results.
|
||||
|
||||
Then, the line of sight (estimated vector between a point and the
|
||||
position of the scanner at its time of acquisition) is estimated:
|
||||
|
||||
- if `scan_angle` is provided, the line of sight can be directly
|
||||
computed as a combination of the estimated scanline and of the
|
||||
scan angle.
|
||||
|
||||
- if no scan angle map is provided, then for each scanline, the
|
||||
position of the scanner is estimated as being above of the
|
||||
barycenter of the points of the scanline projected on the
|
||||
XY-plane. This fallback method gives suboptimal results.
|
||||
|
||||
Once the line of sight is estimated for each point, the normals are
|
||||
oriented by checking, for each of them, if the line of sight and the
|
||||
normal vector give a positive scalar product. If they don't, then
|
||||
the normal vector is inverted.
|
||||
|
||||
\note This method gives optimal results when `scanline_id_map`
|
||||
and `scan_angle` are provided. Correct results may still be
|
||||
produced in the absence of either one or both of these properties,
|
||||
as long as the point set is ordered in 2.5D scanlines.
|
||||
|
||||
\tparam PointRange is a model of `Range`. The value type of
|
||||
its iterator is the key type of the named parameter `point_map`.
|
||||
|
||||
\param points input point range.
|
||||
\param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||
|
||||
\cgalNamedParamsBegin
|
||||
\cgalParamNBegin{point_map}
|
||||
\cgalParamDescription{a property map associating points to the elements of the point set `points`}
|
||||
\cgalParamType{a model of `ReadablePropertyMap` whose key type is the value type
|
||||
of the iterator of `PointRange` and whose value type is `geom_traits::Point_3`}
|
||||
\cgalParamDefault{`CGAL::Identity_property_map<geom_traits::Point_3>`}
|
||||
\cgalParamNEnd
|
||||
|
||||
\cgalParamNBegin{normal_map}
|
||||
\cgalParamDescription{a property map associating normals to the
|
||||
elements of the point set `points`}
|
||||
\cgalParamType{a model of `WritablePropertyMap` whose key type
|
||||
is the value type of the iterator of
|
||||
`PointRange` and whose value type is
|
||||
`geom_traits::Vector_3`}
|
||||
\cgalParamNEnd
|
||||
|
||||
\cgalParamNBegin{scan_angle_map}
|
||||
\cgalParamDescription{a property map associating the angle of
|
||||
acquisition (in degrees) to the elements of the point set
|
||||
`points`}
|
||||
\cgalParamType{a model of `ReadablePropertyMap` whose key type
|
||||
is the value type of the iterator of
|
||||
`PointRange` and whose value type is convertible
|
||||
to `double`}
|
||||
\cgalParamNEnd
|
||||
|
||||
\cgalParamNBegin{scanline_id_map}
|
||||
\cgalParamDescription{a property map associating a scanline ID
|
||||
to the elements of the point set `points`. A scanline is
|
||||
detected as a consecutive subrange of items in the input range
|
||||
`point` whose ID are identical. IDs do not need to be unique,
|
||||
they just need to be different for two consecutive
|
||||
scanlines. The LAS property `scan_direction_flag` (whose values
|
||||
are either 0 or 1 depending on the direction of the scanner)
|
||||
can be used.}
|
||||
\cgalParamType{a model of `ReadablePropertyMap` whose key type
|
||||
is the value type of the iterator of
|
||||
`PointRange` and whose value type is a model of
|
||||
`EqualityComparable`}
|
||||
\cgalParamNEnd
|
||||
|
||||
\cgalParamNBegin{geom_traits}
|
||||
\cgalParamDescription{an instance of a geometric traits class}
|
||||
\cgalParamType{a model of `Kernel`}
|
||||
\cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`}
|
||||
\cgalParamNEnd
|
||||
\cgalNamedParamsEnd
|
||||
*/
|
||||
template <typename PointRange, typename NamedParameters>
|
||||
void scanline_orient_normals (PointRange& points, const NamedParameters& np)
|
||||
{
|
||||
using parameters::choose_parameter;
|
||||
using parameters::get_parameter;
|
||||
|
||||
using Iterator = typename PointRange::iterator;
|
||||
using Value_type = typename std::iterator_traits<Iterator>::value_type;
|
||||
using PointMap = typename CGAL::GetPointMap<PointRange, NamedParameters>::type;
|
||||
using NormalMap = typename Point_set_processing_3::GetNormalMap<PointRange, NamedParameters>::type;
|
||||
|
||||
using No_map = Constant_property_map<Value_type, int>;
|
||||
|
||||
using ScanAngleMap = typename internal_np::Lookup_named_param_def
|
||||
<internal_np::scan_angle_t, NamedParameters, No_map>::type;
|
||||
using Fallback_scan_angle = Boolean_tag<std::is_same<ScanAngleMap, No_map>::value>;
|
||||
|
||||
using ScanlineIDMap = typename internal_np::Lookup_named_param_def
|
||||
<internal_np::scanline_id_t, NamedParameters, No_map>::type;
|
||||
using Fallback_scanline_ID = Boolean_tag<std::is_same<ScanlineIDMap, No_map>::value>;
|
||||
|
||||
CGAL_static_assertion_msg(!(std::is_same<NormalMap,
|
||||
typename Point_set_processing_3::GetNormalMap
|
||||
<PointRange, NamedParameters>::NoMap>::value),
|
||||
"Error: no normal map");
|
||||
|
||||
PointMap point_map = choose_parameter<PointMap>(get_parameter(np, internal_np::point_map));
|
||||
NormalMap normal_map = choose_parameter<NormalMap>(get_parameter(np, internal_np::normal_map));
|
||||
ScanAngleMap scan_angle_map = choose_parameter<ScanAngleMap>
|
||||
(get_parameter(np, internal_np::scan_angle_map));
|
||||
ScanlineIDMap scanline_id_map = choose_parameter<ScanlineIDMap>
|
||||
(get_parameter(np, internal_np::scanline_id_map));
|
||||
|
||||
std::size_t nb_scanlines = 1;
|
||||
|
||||
#ifdef CGAL_SCANORIENT_DUMP_RANDOM_SCANLINES
|
||||
std::ofstream ofile ("scanlines.polylines.txt");
|
||||
ofile.precision(18);
|
||||
#endif
|
||||
|
||||
CGAL::Bbox_3 bbox = CGAL::bbox_3
|
||||
(boost::make_transform_iterator
|
||||
(points.begin(), Property_map_to_unary_function<PointMap>(point_map)),
|
||||
boost::make_transform_iterator
|
||||
(points.end(), Property_map_to_unary_function<PointMap>(point_map)));
|
||||
|
||||
double bbox_diagonal
|
||||
= CGAL::approximate_sqrt((bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin())
|
||||
+ (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin())
|
||||
+ (bbox.zmax() - bbox.zmin()) * (bbox.zmax() - bbox.zmin()));
|
||||
double limit = 0.05 * bbox_diagonal;
|
||||
|
||||
Iterator scanline_begin = points.begin();
|
||||
for (Iterator it = points.begin(); it != points.end(); ++ it)
|
||||
{
|
||||
bool force_cut = false;
|
||||
if (it != points.begin())
|
||||
{
|
||||
Iterator prev = it; -- prev;
|
||||
force_cut = (CGAL::squared_distance (get (point_map, *prev),
|
||||
get (point_map, *it)) > limit * limit);
|
||||
}
|
||||
|
||||
if (Point_set_processing_3::internal::is_end_of_scanline
|
||||
(scanline_begin, it, point_map, scanline_id_map,
|
||||
Fallback_scanline_ID()) || force_cut)
|
||||
{
|
||||
Point_set_processing_3::internal::orient_scanline
|
||||
(scanline_begin, it, point_map, normal_map,
|
||||
scan_angle_map, Fallback_scan_angle());
|
||||
|
||||
#ifdef CGAL_SCANORIENT_DUMP_RANDOM_SCANLINES
|
||||
ofile << std::distance (scanline_begin, it);
|
||||
for (Iterator it2 = scanline_begin; it2 != it; ++ it2)
|
||||
ofile << " " << get (point_map, *it2);
|
||||
ofile << std::endl;
|
||||
#endif
|
||||
|
||||
scanline_begin = it;
|
||||
++ nb_scanlines;
|
||||
}
|
||||
}
|
||||
|
||||
Point_set_processing_3::internal::orient_scanline
|
||||
(scanline_begin, points.end(), point_map, normal_map,
|
||||
scan_angle_map, Fallback_scan_angle());
|
||||
|
||||
#ifdef CGAL_SCANLINE_ORIENT_VERBOSE
|
||||
std::cerr << nb_scanlines << " scanline(s) identified (mean length = "
|
||||
<< std::size_t(points.size() / double(nb_scanlines))
|
||||
<< " point(s))" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename PointRange>
|
||||
void scanline_orient_normals (PointRange& points)
|
||||
{
|
||||
return scanline_orient_normals (points,
|
||||
CGAL::Point_set_processing_3::parameters::all_default(points));
|
||||
}
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_EIGEN3_ENABLED
|
||||
|
||||
#endif // CGAL_SCANLINE_ORIENT_NORMALS_H
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#include <CGAL/jet_estimate_normals.h>
|
||||
#include <CGAL/vcm_estimate_normals.h>
|
||||
#include <CGAL/mst_orient_normals.h>
|
||||
#include <CGAL/scanline_orient_normals.h>
|
||||
#include <CGAL/Timer.h>
|
||||
#include <CGAL/Memory_sizer.h>
|
||||
|
||||
|
|
@ -306,82 +307,156 @@ void Polyhedron_demo_point_set_normal_estimation_plugin::on_actionNormalOrientat
|
|||
if(points == NULL)
|
||||
return;
|
||||
|
||||
// Gets options
|
||||
QMultipleInputDialog dialog ("Normal Orientation", mw);
|
||||
QSpinBox* neighborhood = dialog.add<QSpinBox> ("Neighborhood Size: ");
|
||||
neighborhood->setRange (1, 10000000);
|
||||
neighborhood->setValue (18);
|
||||
// Chose method
|
||||
QMultipleInputDialog method ("Normal Orientation", mw);
|
||||
QRadioButton* mst = method.add<QRadioButton> ("Orient by Minimum Spanning Tree");
|
||||
mst->setChecked(true);
|
||||
QRadioButton* scanline = method.add<QRadioButton> ("Orient 2.5D airborne acquired scanlines");
|
||||
scanline->setChecked(false);
|
||||
|
||||
QRadioButton* use_seed_points = NULL;
|
||||
QRadioButton* orient_selection = NULL;
|
||||
|
||||
if (points->nb_selected_points() != 0)
|
||||
{
|
||||
use_seed_points = dialog.add<QRadioButton> ("Use selection as seed points and orient the unselected points");
|
||||
use_seed_points->setChecked(true);
|
||||
orient_selection = dialog.add<QRadioButton> ("Orient selection");
|
||||
orient_selection->setChecked(false);
|
||||
}
|
||||
|
||||
if(!dialog.exec())
|
||||
if (!method.exec())
|
||||
return;
|
||||
|
||||
QApplication::setOverrideCursor(Qt::BusyCursor);
|
||||
QApplication::processEvents();
|
||||
|
||||
// First point to delete
|
||||
Point_set::iterator first_unoriented_point = points->end();
|
||||
|
||||
//***************************************
|
||||
// normal orientation
|
||||
//***************************************
|
||||
|
||||
CGAL::Timer task_timer; task_timer.start();
|
||||
std::cerr << "Orient normals with a Minimum Spanning Tree (k=" << neighborhood->value() << ")...\n";
|
||||
|
||||
// Tries to orient normals
|
||||
if (points->nb_selected_points() != 0 && use_seed_points->isChecked())
|
||||
if (mst->isChecked())
|
||||
{
|
||||
std::vector<bool> constrained_map (points->size(), false);
|
||||
// Gets options
|
||||
QMultipleInputDialog dialog ("Normal Orientation", mw);
|
||||
QSpinBox* neighborhood = dialog.add<QSpinBox> ("Neighborhood Size: ");
|
||||
neighborhood->setRange (1, 10000000);
|
||||
neighborhood->setValue (18);
|
||||
|
||||
for (Point_set::iterator it = points->first_selected(); it != points->end(); ++ it)
|
||||
constrained_map[*it] = true;
|
||||
QRadioButton* use_seed_points = NULL;
|
||||
QRadioButton* orient_selection = NULL;
|
||||
|
||||
first_unoriented_point =
|
||||
CGAL::mst_orient_normals(*points,
|
||||
std::size_t(neighborhood->value()),
|
||||
points->parameters().
|
||||
point_is_constrained_map(Vector_to_pmap(&constrained_map)));
|
||||
if (points->nb_selected_points() != 0)
|
||||
{
|
||||
use_seed_points = dialog.add<QRadioButton> ("Use selection as seed points and orient the unselected points");
|
||||
use_seed_points->setChecked(true);
|
||||
orient_selection = dialog.add<QRadioButton> ("Orient selection");
|
||||
orient_selection->setChecked(false);
|
||||
}
|
||||
|
||||
if(!dialog.exec())
|
||||
return;
|
||||
|
||||
QApplication::setOverrideCursor(Qt::BusyCursor);
|
||||
QApplication::processEvents();
|
||||
|
||||
// First point to delete
|
||||
Point_set::iterator first_unoriented_point = points->end();
|
||||
|
||||
//***************************************
|
||||
// normal orientation
|
||||
//***************************************
|
||||
|
||||
CGAL::Timer task_timer; task_timer.start();
|
||||
std::cerr << "Orient normals with a Minimum Spanning Tree (k=" << neighborhood->value() << ")...\n";
|
||||
|
||||
// Tries to orient normals
|
||||
if (points->nb_selected_points() != 0 && use_seed_points->isChecked())
|
||||
{
|
||||
std::vector<bool> constrained_map (points->size(), false);
|
||||
|
||||
for (Point_set::iterator it = points->first_selected(); it != points->end(); ++ it)
|
||||
constrained_map[*it] = true;
|
||||
|
||||
first_unoriented_point =
|
||||
CGAL::mst_orient_normals(*points,
|
||||
std::size_t(neighborhood->value()),
|
||||
points->parameters().
|
||||
point_is_constrained_map(Vector_to_pmap(&constrained_map)));
|
||||
}
|
||||
else
|
||||
first_unoriented_point =
|
||||
CGAL::mst_orient_normals(points->all_or_selection_if_not_empty(),
|
||||
std::size_t(neighborhood->value()),
|
||||
points->parameters());
|
||||
|
||||
std::size_t nb_unoriented_normals = std::distance(first_unoriented_point, points->end());
|
||||
std::size_t memory = CGAL::Memory_sizer().virtual_size();
|
||||
std::cerr << "Orient normals: " << nb_unoriented_normals << " point(s) with an unoriented normal are selected ("
|
||||
<< task_timer.time() << " seconds, "
|
||||
<< (memory>>20) << " Mb allocated)"
|
||||
<< std::endl;
|
||||
|
||||
// Selects points with an unoriented normal
|
||||
points->set_first_selected (first_unoriented_point);
|
||||
|
||||
// Updates scene
|
||||
item->invalidateOpenGLBuffers();
|
||||
scene->itemChanged(index);
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
// Warns user
|
||||
if (nb_unoriented_normals > 0)
|
||||
{
|
||||
QMessageBox::information(NULL,
|
||||
tr("Points with an unoriented normal"),
|
||||
tr("%1 point(s) with an unoriented normal are selected.\nPlease orient them or remove them before running Poisson reconstruction.")
|
||||
.arg(nb_unoriented_normals));
|
||||
}
|
||||
}
|
||||
else
|
||||
first_unoriented_point =
|
||||
CGAL::mst_orient_normals(points->all_or_selection_if_not_empty(),
|
||||
std::size_t(neighborhood->value()),
|
||||
points->parameters());
|
||||
|
||||
std::size_t nb_unoriented_normals = std::distance(first_unoriented_point, points->end());
|
||||
std::size_t memory = CGAL::Memory_sizer().virtual_size();
|
||||
std::cerr << "Orient normals: " << nb_unoriented_normals << " point(s) with an unoriented normal are selected ("
|
||||
<< task_timer.time() << " seconds, "
|
||||
<< (memory>>20) << " Mb allocated)"
|
||||
<< std::endl;
|
||||
|
||||
// Selects points with an unoriented normal
|
||||
points->set_first_selected (first_unoriented_point);
|
||||
|
||||
// Updates scene
|
||||
item->invalidateOpenGLBuffers();
|
||||
scene->itemChanged(index);
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
// Warns user
|
||||
if (nb_unoriented_normals > 0)
|
||||
else // scanline method
|
||||
{
|
||||
QMessageBox::information(NULL,
|
||||
tr("Points with an unoriented normal"),
|
||||
tr("%1 point(s) with an unoriented normal are selected.\nPlease orient them or remove them before running Poisson reconstruction.")
|
||||
.arg(nb_unoriented_normals));
|
||||
QApplication::setOverrideCursor(Qt::BusyCursor);
|
||||
QApplication::processEvents();
|
||||
|
||||
//***************************************
|
||||
// normal orientation
|
||||
//***************************************
|
||||
|
||||
CGAL::Timer task_timer; task_timer.start();
|
||||
std::cerr << "Orient normals with along 2.5D scanlines..." << std::endl;
|
||||
|
||||
Point_set::Property_map<float> scan_angle;
|
||||
Point_set::Property_map<unsigned char> scan_direction_flag;
|
||||
bool angle_found = false, flag_found = false;
|
||||
|
||||
std::tie (scan_angle, angle_found)
|
||||
= points->property_map<float>("scan_angle");
|
||||
std::tie (scan_direction_flag, flag_found)
|
||||
= points->property_map<unsigned char>("scan_direction_flag");
|
||||
|
||||
if (!angle_found && !flag_found)
|
||||
{
|
||||
std::cerr << " using no additional properties" << std::endl;
|
||||
CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(),
|
||||
points->parameters());
|
||||
}
|
||||
else if (!angle_found && flag_found)
|
||||
{
|
||||
std::cerr << " using scan direction flag" << std::endl;
|
||||
CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(),
|
||||
points->parameters().
|
||||
scanline_id_map (scan_direction_flag));
|
||||
}
|
||||
else if (angle_found && !flag_found)
|
||||
{
|
||||
std::cerr << " using scan angle" << std::endl;
|
||||
CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(),
|
||||
points->parameters().
|
||||
scan_angle_map (scan_angle));
|
||||
}
|
||||
else // if (angle_found && flag_found)
|
||||
{
|
||||
std::cerr << " using scan angle and direction flag" << std::endl;
|
||||
CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(),
|
||||
points->parameters().
|
||||
scan_angle_map (scan_angle).
|
||||
scanline_id_map (scan_direction_flag));
|
||||
}
|
||||
std::size_t memory = CGAL::Memory_sizer().virtual_size();
|
||||
std::cerr << "Orient normals: "
|
||||
<< task_timer.time() << " seconds, "
|
||||
<< (memory>>20) << " Mb allocated)"
|
||||
<< std::endl;
|
||||
|
||||
// Updates scene
|
||||
item->invalidateOpenGLBuffers();
|
||||
scene->itemChanged(index);
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue