mirror of https://github.com/CGAL/cgal
added missing ref + better syntaxis
This commit is contained in:
parent
0c5d8ed8ed
commit
61499c6fff
|
|
@ -264,6 +264,16 @@ Boissonnat}
|
||||||
update = "09.12 penarand"
|
update = "09.12 penarand"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@inproceedings{cgal:bl-kippi-18,
|
||||||
|
author = {Bauchet, Jean-Philippe and Lafarge, Florent},
|
||||||
|
title = {{KIPPI: KInetic Polygonal Partitioning of Images}},
|
||||||
|
booktitle = {{IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}},
|
||||||
|
address = {Salt Lake City, United States},
|
||||||
|
year = {2018},
|
||||||
|
month = {Jun},
|
||||||
|
pages = {3146--3154}
|
||||||
|
}
|
||||||
|
|
||||||
@book{ cgal:bn-sec++-94
|
@book{ cgal:bn-sec++-94
|
||||||
,author = "J. J. Barton and L. R. Nackman"
|
,author = "J. J. Barton and L. R. Nackman"
|
||||||
,title = "Scientific and Engineering {C{\tt ++}}"
|
,title = "Scientific and Engineering {C{\tt ++}}"
|
||||||
|
|
|
||||||
|
|
@ -161,11 +161,12 @@ void benchmark_contours(
|
||||||
open_time /= static_cast<double>(num_iters);
|
open_time /= static_cast<double>(num_iters);
|
||||||
|
|
||||||
std::cout.precision(10);
|
std::cout.precision(10);
|
||||||
if (simple_output)
|
if (simple_output) {
|
||||||
std::cout << contour.size() << " " << closed_time << " " << open_time << std::endl;
|
std::cout << contour.size() << " " << closed_time << " " << open_time << std::endl;
|
||||||
else
|
} else {
|
||||||
std::cout << "benchmark_contours " << contour.size() << " (CPU time " <<
|
std::cout << "benchmark_contours " << contour.size() << " (CPU time " <<
|
||||||
"closed/open): " << closed_time << "/" << open_time << " seconds" << std::endl;
|
"closed/open): " << closed_time << "/" << open_time << " seconds" << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
@ -174,8 +175,9 @@ int main() {
|
||||||
const std::vector<std::size_t> ns = {
|
const std::vector<std::size_t> ns = {
|
||||||
10, 100, 1000, 10000, 100000, 1000000, 10000000
|
10, 100, 1000, 10000, 100000, 1000000, 10000000
|
||||||
};
|
};
|
||||||
for (const std::size_t n : ns)
|
for (const std::size_t n : ns) {
|
||||||
benchmark_contours(n, false, num_iters);
|
benchmark_contours(n, false, num_iters);
|
||||||
|
}
|
||||||
|
|
||||||
// Dense results for plotting.
|
// Dense results for plotting.
|
||||||
// std::vector<std::size_t> ns;
|
// std::vector<std::size_t> ns;
|
||||||
|
|
|
||||||
|
|
@ -138,8 +138,9 @@ void benchmark_qp_segments(
|
||||||
group.reserve(m);
|
group.reserve(m);
|
||||||
for (std::size_t i = 0; i < n;) {
|
for (std::size_t i = 0; i < n;) {
|
||||||
group.clear();
|
group.clear();
|
||||||
for (std::size_t j = 0; j < m; ++j)
|
for (std::size_t j = 0; j < m; ++j) {
|
||||||
group.push_back(i + j);
|
group.push_back(i + j);
|
||||||
|
}
|
||||||
neighbor_query.add_group(group);
|
neighbor_query.add_group(group);
|
||||||
i += m;
|
i += m;
|
||||||
}
|
}
|
||||||
|
|
@ -157,8 +158,9 @@ void benchmark_qp_segments(
|
||||||
group.reserve(m);
|
group.reserve(m);
|
||||||
for (std::size_t i = 0; i < n;) {
|
for (std::size_t i = 0; i < n;) {
|
||||||
group.clear();
|
group.clear();
|
||||||
for (std::size_t j = 0; j < m; ++j)
|
for (std::size_t j = 0; j < m; ++j) {
|
||||||
group.push_back(i + j);
|
group.push_back(i + j);
|
||||||
|
}
|
||||||
angle_regularization.add_group(group);
|
angle_regularization.add_group(group);
|
||||||
i += m;
|
i += m;
|
||||||
}
|
}
|
||||||
|
|
@ -218,8 +220,9 @@ void benchmark_qp_segments(
|
||||||
offset_time /= static_cast<double>(num_iters);
|
offset_time /= static_cast<double>(num_iters);
|
||||||
|
|
||||||
std::cout.precision(10);
|
std::cout.precision(10);
|
||||||
if (regroup && !simple_output)
|
if (regroup && !simple_output) {
|
||||||
std::cout << "grouped: " ;
|
std::cout << "grouped: " ;
|
||||||
|
}
|
||||||
|
|
||||||
// std::cout << "benchmark_qp_segments " << segments.size() << " (CPU time " <<
|
// std::cout << "benchmark_qp_segments " << segments.size() << " (CPU time " <<
|
||||||
// "delaunay/setup_angles/angles/setup_offsets/offsets): " <<
|
// "delaunay/setup_angles/angles/setup_offsets/offsets): " <<
|
||||||
|
|
@ -228,14 +231,15 @@ void benchmark_qp_segments(
|
||||||
// setup_offset_time << "/" << offset_time <<
|
// setup_offset_time << "/" << offset_time <<
|
||||||
// " seconds" << std::endl;
|
// " seconds" << std::endl;
|
||||||
|
|
||||||
if (!simple_output)
|
if (!simple_output) {
|
||||||
std::cout << "benchmark_qp_segments " << segments.size() << " (CPU time " <<
|
std::cout << "benchmark_qp_segments " << segments.size() << " (CPU time " <<
|
||||||
"angles/offsets): " << angle_time << "/" << offset_time << " seconds" << std::endl;
|
"angles/offsets): " << angle_time << "/" << offset_time << " seconds" << std::endl;
|
||||||
else {
|
} else {
|
||||||
if (!regroup)
|
if (!regroup) {
|
||||||
std::cout << segments.size() << " " << angle_time << " " << offset_time << " ";
|
std::cout << segments.size() << " " << angle_time << " " << offset_time << " ";
|
||||||
else
|
} else {
|
||||||
std::cout << angle_time << " " << offset_time << std::endl;
|
std::cout << angle_time << " " << offset_time << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,9 @@ public:
|
||||||
|
|
||||||
const FT stub = FT(0);
|
const FT stub = FT(0);
|
||||||
std::vector<Segment_2> edges;
|
std::vector<Segment_2> edges;
|
||||||
for (const std::size_t seg_index : group)
|
for (const std::size_t seg_index : group) {
|
||||||
edges.push_back(segments[seg_index]);
|
edges.push_back(segments[seg_index]);
|
||||||
|
}
|
||||||
export_segments(edges, path, stub);
|
export_segments(edges, path, stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,8 +66,9 @@ public:
|
||||||
const std::string path,
|
const std::string path,
|
||||||
const FT) {
|
const FT) {
|
||||||
|
|
||||||
if (contour.size() == 0)
|
if (contour.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const FT stub = FT(0);
|
const FT stub = FT(0);
|
||||||
std::vector<Segment_2> segments;
|
std::vector<Segment_2> segments;
|
||||||
|
|
@ -88,8 +90,9 @@ public:
|
||||||
const std::string path,
|
const std::string path,
|
||||||
const FT) {
|
const FT) {
|
||||||
|
|
||||||
if (contour.size() == 0)
|
if (contour.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const FT stub = FT(0);
|
const FT stub = FT(0);
|
||||||
std::vector<Segment_2> segments;
|
std::vector<Segment_2> segments;
|
||||||
|
|
@ -111,8 +114,7 @@ public:
|
||||||
const std::string path,
|
const std::string path,
|
||||||
FT scale) {
|
FT scale) {
|
||||||
|
|
||||||
if (input.size() == 0)
|
if (input.size() == 0) return;
|
||||||
return;
|
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
// Compute barycenter.
|
// Compute barycenter.
|
||||||
|
|
@ -165,8 +167,9 @@ public:
|
||||||
|
|
||||||
const FT stub = FT(0);
|
const FT stub = FT(0);
|
||||||
std::vector<Segment_2> edges;
|
std::vector<Segment_2> edges;
|
||||||
for (const std::size_t seg_index : group)
|
for (const std::size_t seg_index : group) {
|
||||||
edges.push_back(segments[seg_index]);
|
edges.push_back(segments[seg_index]);
|
||||||
|
}
|
||||||
export_eps_segments(edges, path, stub);
|
export_eps_segments(edges, path, stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,8 +178,9 @@ public:
|
||||||
const std::string path,
|
const std::string path,
|
||||||
FT scale) {
|
FT scale) {
|
||||||
|
|
||||||
if (contour.size() == 0)
|
if (contour.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Segment_2> segments;
|
std::vector<Segment_2> segments;
|
||||||
const std::size_t n = contour.size();
|
const std::size_t n = contour.size();
|
||||||
|
|
@ -197,8 +201,9 @@ public:
|
||||||
const std::string path,
|
const std::string path,
|
||||||
FT scale) {
|
FT scale) {
|
||||||
|
|
||||||
if (contour.size() == 0)
|
if (contour.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Segment_2> segments;
|
std::vector<Segment_2> segments;
|
||||||
const std::size_t n = contour.size();
|
const std::size_t n = contour.size();
|
||||||
|
|
@ -240,8 +245,9 @@ private:
|
||||||
const std::vector<Polyline>& polylines,
|
const std::vector<Polyline>& polylines,
|
||||||
const std::string path) {
|
const std::string path) {
|
||||||
|
|
||||||
if (polylines.size() == 0)
|
if (polylines.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
for (std::size_t i = 0; i < polylines.size(); ++i) {
|
for (std::size_t i = 0; i < polylines.size(); ++i) {
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,9 @@ double get_coefficient_value(
|
||||||
(theta > 3.0 * CGAL_PI / 2.0 && theta < 7.0 * CGAL_PI / 4.0)) {
|
(theta > 3.0 * CGAL_PI / 2.0 && theta < 7.0 * CGAL_PI / 4.0)) {
|
||||||
|
|
||||||
iterator += 0.02;
|
iterator += 0.02;
|
||||||
} else
|
} else {
|
||||||
iterator -= 0.02;
|
iterator -= 0.02;
|
||||||
|
}
|
||||||
|
|
||||||
if (theta < CGAL_PI) return -1.0 * iterator;
|
if (theta < CGAL_PI) return -1.0 * iterator;
|
||||||
return iterator;
|
return iterator;
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,9 @@ public:
|
||||||
bool solve(OutputIterator solution) {
|
bool solve(OutputIterator solution) {
|
||||||
|
|
||||||
// 3 = 2 objects + 1 edge between them
|
// 3 = 2 objects + 1 edge between them
|
||||||
for (std::size_t i = 0; i < 3; ++i)
|
for (std::size_t i = 0; i < 3; ++i) {
|
||||||
*(++solution) = NT(0);
|
*(++solution) = NT(0);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -92,15 +92,17 @@ namespace Contours {
|
||||||
|
|
||||||
CGAL_precondition(m_input_range.size() >= 2);
|
CGAL_precondition(m_input_range.size() >= 2);
|
||||||
|
|
||||||
if (is_closed)
|
if (is_closed) {
|
||||||
estimate_closed(m_bounds, m_directions, m_assigned);
|
estimate_closed(m_bounds, m_directions, m_assigned);
|
||||||
else
|
} else {
|
||||||
estimate_open(m_bounds, m_directions, m_assigned);
|
estimate_open(m_bounds, m_directions, m_assigned);
|
||||||
|
}
|
||||||
|
|
||||||
if (verbose()) {
|
if (verbose()) {
|
||||||
std::cout << "* assigned directions: ";
|
std::cout << "* assigned directions: ";
|
||||||
for (std::size_t direction_index : m_assigned)
|
for (std::size_t direction_index : m_assigned) {
|
||||||
std::cout << direction_index << " ";
|
std::cout << direction_index << " ";
|
||||||
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -143,15 +143,17 @@ namespace Contours {
|
||||||
CGAL_precondition(
|
CGAL_precondition(
|
||||||
m_min_length_2 >= FT(0));
|
m_min_length_2 >= FT(0));
|
||||||
|
|
||||||
if (is_closed)
|
if (is_closed) {
|
||||||
estimate_closed(m_bounds, m_directions, m_assigned);
|
estimate_closed(m_bounds, m_directions, m_assigned);
|
||||||
else
|
} else {
|
||||||
estimate_open(m_bounds, m_directions, m_assigned);
|
estimate_open(m_bounds, m_directions, m_assigned);
|
||||||
|
}
|
||||||
|
|
||||||
if (verbose()) {
|
if (verbose()) {
|
||||||
std::cout << "* assigned directions: ";
|
std::cout << "* assigned directions: ";
|
||||||
for (std::size_t direction_index : m_assigned)
|
for (std::size_t direction_index : m_assigned) {
|
||||||
std::cout << direction_index << " ";
|
std::cout << direction_index << " ";
|
||||||
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -257,8 +259,9 @@ namespace Contours {
|
||||||
} else {
|
} else {
|
||||||
m_base.unify_along_contours_closed(wraps, assigned);
|
m_base.unify_along_contours_closed(wraps, assigned);
|
||||||
m_base.correct_directions_closed(wraps, assigned);
|
m_base.correct_directions_closed(wraps, assigned);
|
||||||
if (m_adjust_directions)
|
if (m_adjust_directions) {
|
||||||
m_base.readjust_directions(wraps, assigned, directions);
|
m_base.readjust_directions(wraps, assigned, directions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,17 +290,19 @@ namespace Contours {
|
||||||
} else {
|
} else {
|
||||||
m_base.unify_along_contours_open(wraps, assigned);
|
m_base.unify_along_contours_open(wraps, assigned);
|
||||||
m_base.correct_directions_open(wraps, assigned);
|
m_base.correct_directions_open(wraps, assigned);
|
||||||
if (m_adjust_directions)
|
if (m_adjust_directions) {
|
||||||
m_base.readjust_directions(wraps, assigned, directions);
|
m_base.readjust_directions(wraps, assigned, directions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_valid_directions(
|
void set_valid_directions(
|
||||||
std::vector<Segment_wrapper_2>& wraps) const {
|
std::vector<Segment_wrapper_2>& wraps) const {
|
||||||
|
|
||||||
for (auto& wrap : wraps)
|
for (auto& wrap : wraps) {
|
||||||
wrap.is_valid_direction =
|
wrap.is_valid_direction =
|
||||||
is_valid_principal_direction(wrap.segment);
|
is_valid_principal_direction(wrap.segment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_valid_principal_direction(
|
bool is_valid_principal_direction(
|
||||||
|
|
@ -327,10 +332,11 @@ namespace Contours {
|
||||||
do {
|
do {
|
||||||
query_index = find_next_longest_segment(
|
query_index = find_next_longest_segment(
|
||||||
wraps, longest_to_short);
|
wraps, longest_to_short);
|
||||||
if (query_index != std::size_t(-1))
|
if (query_index != std::size_t(-1)) {
|
||||||
set_next_longest_direction(
|
set_next_longest_direction(
|
||||||
wraps, query_index, group_index,
|
wraps, query_index, group_index,
|
||||||
bounds, directions, assigned);
|
bounds, directions, assigned);
|
||||||
|
}
|
||||||
++group_index;
|
++group_index;
|
||||||
} while (query_index != std::size_t(-1));
|
} while (query_index != std::size_t(-1));
|
||||||
}
|
}
|
||||||
|
|
@ -372,8 +378,9 @@ namespace Contours {
|
||||||
longest.is_used = true;
|
longest.is_used = true;
|
||||||
|
|
||||||
for (auto& wrap : wraps) {
|
for (auto& wrap : wraps) {
|
||||||
if (wrap.index == query_index) // skip longest
|
if (wrap.index == query_index) { // skip longest
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if another wrap satisifes the conditions.
|
// Check if another wrap satisifes the conditions.
|
||||||
if (is_valid_wrap(wrap)) {
|
if (is_valid_wrap(wrap)) {
|
||||||
|
|
|
||||||
|
|
@ -107,17 +107,19 @@ namespace Contours {
|
||||||
CGAL_precondition(input_range.size() >= 2);
|
CGAL_precondition(input_range.size() >= 2);
|
||||||
CGAL_precondition(direction_range.size() >= 1);
|
CGAL_precondition(direction_range.size() >= 1);
|
||||||
|
|
||||||
if (is_closed)
|
if (is_closed) {
|
||||||
estimate_closed(
|
estimate_closed(
|
||||||
direction_range, m_bounds, m_directions, m_assigned);
|
direction_range, m_bounds, m_directions, m_assigned);
|
||||||
else
|
} else {
|
||||||
estimate_open(
|
estimate_open(
|
||||||
direction_range, m_bounds, m_directions, m_assigned);
|
direction_range, m_bounds, m_directions, m_assigned);
|
||||||
|
}
|
||||||
|
|
||||||
if (verbose()) {
|
if (verbose()) {
|
||||||
std::cout << "* assigned directions: ";
|
std::cout << "* assigned directions: ";
|
||||||
for (std::size_t direction_index : m_assigned)
|
for (std::size_t direction_index : m_assigned) {
|
||||||
std::cout << direction_index << " ";
|
std::cout << direction_index << " ";
|
||||||
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -203,8 +205,9 @@ namespace Contours {
|
||||||
initialize_directions(direction_range, directions);
|
initialize_directions(direction_range, directions);
|
||||||
|
|
||||||
bounds.clear(); bounds.reserve(directions.size());
|
bounds.clear(); bounds.reserve(directions.size());
|
||||||
for (std::size_t i = 0; i < directions.size(); ++i)
|
for (std::size_t i = 0; i < directions.size(); ++i) {
|
||||||
bounds.push_back(std::make_pair(FT(45), FT(45)));
|
bounds.push_back(std::make_pair(FT(45), FT(45)));
|
||||||
|
}
|
||||||
|
|
||||||
assigned.clear(); assigned.resize(wraps.size());
|
assigned.clear(); assigned.resize(wraps.size());
|
||||||
set_directions(directions, wraps, assigned);
|
set_directions(directions, wraps, assigned);
|
||||||
|
|
@ -229,8 +232,9 @@ namespace Contours {
|
||||||
initialize_directions(direction_range, directions);
|
initialize_directions(direction_range, directions);
|
||||||
|
|
||||||
bounds.clear(); bounds.reserve(directions.size());
|
bounds.clear(); bounds.reserve(directions.size());
|
||||||
for (std::size_t i = 0; i < directions.size(); ++i)
|
for (std::size_t i = 0; i < directions.size(); ++i) {
|
||||||
bounds.push_back(std::make_pair(FT(45), FT(45)));
|
bounds.push_back(std::make_pair(FT(45), FT(45)));
|
||||||
|
}
|
||||||
|
|
||||||
assigned.clear(); assigned.resize(wraps.size());
|
assigned.clear(); assigned.resize(wraps.size());
|
||||||
set_directions(directions, wraps, assigned);
|
set_directions(directions, wraps, assigned);
|
||||||
|
|
|
||||||
|
|
@ -382,8 +382,9 @@ namespace Shape_regularization {
|
||||||
|
|
||||||
// Used to set bounds for each variable li <= xi <= ui.
|
// Used to set bounds for each variable li <= xi <= ui.
|
||||||
const std::size_t s = m_targets.size() * 2;
|
const std::size_t s = m_targets.size() * 2;
|
||||||
for (std::size_t i = 0; i < n; ++i)
|
for (std::size_t i = 0; i < n; ++i) {
|
||||||
qp.set_A(s + i, i, FT(1));
|
qp.set_A(s + i, i, FT(1));
|
||||||
|
}
|
||||||
|
|
||||||
// CGAL_assertion(qp.A_size() == A_nnz);
|
// CGAL_assertion(qp.A_size() == A_nnz);
|
||||||
}
|
}
|
||||||
|
|
@ -433,8 +434,9 @@ namespace Shape_regularization {
|
||||||
|
|
||||||
const auto success = qp.solve(
|
const auto success = qp.solve(
|
||||||
std::back_inserter(solution));
|
std::back_inserter(solution));
|
||||||
if (!success)
|
if (!success) {
|
||||||
std::cerr << "WARNING: The solver has not converged!" << std::endl;
|
std::cerr << "WARNING: The solver has not converged!" << std::endl;
|
||||||
|
}
|
||||||
CGAL_assertion(solution.size() == n);
|
CGAL_assertion(solution.size() == n);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -348,8 +348,9 @@ namespace Segments {
|
||||||
m_num_groups = 0;
|
m_num_groups = 0;
|
||||||
m_is_first_call = true;
|
m_is_first_call = true;
|
||||||
m_num_modified_segments = 0;
|
m_num_modified_segments = 0;
|
||||||
for (auto& wrap : m_wraps)
|
for (auto& wrap : m_wraps) {
|
||||||
wrap.is_used = false;
|
wrap.is_used = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
@ -428,7 +429,7 @@ namespace Segments {
|
||||||
x2 = wrap.barycenter.x() + wrap.length * direction.dx() / FT(2);
|
x2 = wrap.barycenter.x() + wrap.length * direction.dx() / FT(2);
|
||||||
y1 = (-c - a * x1) / b;
|
y1 = (-c - a * x1) / b;
|
||||||
y2 = (-c - a * x2) / b;
|
y2 = (-c - a * x2) / b;
|
||||||
} else {
|
} else {
|
||||||
y1 = wrap.barycenter.y() - wrap.length * direction.dy() / FT(2);
|
y1 = wrap.barycenter.y() - wrap.length * direction.dy() / FT(2);
|
||||||
y2 = wrap.barycenter.y() + wrap.length * direction.dy() / FT(2);
|
y2 = wrap.barycenter.y() + wrap.length * direction.dy() / FT(2);
|
||||||
x1 = (-c - b * y1) / a;
|
x1 = (-c - b * y1) / a;
|
||||||
|
|
|
||||||
|
|
@ -180,8 +180,9 @@ namespace Segments {
|
||||||
void clear() {
|
void clear() {
|
||||||
m_num_groups = 0;
|
m_num_groups = 0;
|
||||||
m_is_first_call = true;
|
m_is_first_call = true;
|
||||||
for (auto& group : m_groups)
|
for (auto& group : m_groups) {
|
||||||
group.clear();
|
group.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
@ -194,8 +195,9 @@ namespace Segments {
|
||||||
|
|
||||||
std::size_t number_of_neighbors() const {
|
std::size_t number_of_neighbors() const {
|
||||||
std::size_t num_neighbors = 0;
|
std::size_t num_neighbors = 0;
|
||||||
for (const auto& group : m_groups)
|
for (const auto& group : m_groups) {
|
||||||
num_neighbors += group.size();
|
num_neighbors += group.size();
|
||||||
|
}
|
||||||
return num_neighbors;
|
return num_neighbors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -352,8 +352,9 @@ namespace Segments {
|
||||||
m_num_groups = 0;
|
m_num_groups = 0;
|
||||||
m_is_first_call = true;
|
m_is_first_call = true;
|
||||||
m_num_modified_segments = 0;
|
m_num_modified_segments = 0;
|
||||||
for (auto& wrap : m_wraps)
|
for (auto& wrap : m_wraps) {
|
||||||
wrap.is_used = false;
|
wrap.is_used = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
|
||||||
|
|
@ -62,21 +62,24 @@ namespace internal {
|
||||||
if (m_wraps.size() < 3) return contour;
|
if (m_wraps.size() < 3) return contour;
|
||||||
|
|
||||||
rotate_contour(m_wraps);
|
rotate_contour(m_wraps);
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
m_base.export_polylines(
|
m_base.export_polylines(
|
||||||
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/rotated");
|
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/rotated");
|
||||||
|
}
|
||||||
|
|
||||||
bool success = optimize_contour(m_wraps);
|
bool success = optimize_contour(m_wraps);
|
||||||
if (!success) return contour;
|
if (!success) return contour;
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
m_base.export_polylines(
|
m_base.export_polylines(
|
||||||
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/optimized");
|
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/optimized");
|
||||||
|
}
|
||||||
|
|
||||||
success = connect_contour(m_wraps);
|
success = connect_contour(m_wraps);
|
||||||
if (!success) return contour;
|
if (!success) return contour;
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
m_base.export_polylines(
|
m_base.export_polylines(
|
||||||
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/connected");
|
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/connected");
|
||||||
|
}
|
||||||
|
|
||||||
return update_input(m_wraps, contour);
|
return update_input(m_wraps, contour);
|
||||||
}
|
}
|
||||||
|
|
@ -112,16 +115,18 @@ namespace internal {
|
||||||
// Merge parallel/collinear segments.
|
// Merge parallel/collinear segments.
|
||||||
std::vector<Segment_2> segments;
|
std::vector<Segment_2> segments;
|
||||||
m_base.create_unique_segments(m_max_offset_2, wraps, segments);
|
m_base.create_unique_segments(m_max_offset_2, wraps, segments);
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
std::cout <<
|
std::cout <<
|
||||||
"* number of segments (merging) = " << segments.size() << std::endl;
|
"* number of segments (merging) = " << segments.size() << std::endl;
|
||||||
|
}
|
||||||
if (wraps.size() < 4) return false;
|
if (wraps.size() < 4) return false;
|
||||||
|
|
||||||
// Add orthogonal segments.
|
// Add orthogonal segments.
|
||||||
create_orthogonal_segments(segments, wraps);
|
create_orthogonal_segments(segments, wraps);
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
std::cout <<
|
std::cout <<
|
||||||
"* number of segments (orthogonal) = " << wraps.size() << std::endl;
|
"* number of segments (orthogonal) = " << wraps.size() << std::endl;
|
||||||
|
}
|
||||||
if (wraps.size() < 4) return false;
|
if (wraps.size() < 4) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -219,9 +224,10 @@ namespace internal {
|
||||||
const std::size_t after = wraps.size();
|
const std::size_t after = wraps.size();
|
||||||
CGAL_assertion(after <= before);
|
CGAL_assertion(after <= before);
|
||||||
|
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
std::cout <<
|
std::cout <<
|
||||||
"* segments before/after = " << before << "/" << after << std::endl;
|
"* segments before/after = " << before << "/" << after << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename OutputIterator>
|
template<typename OutputIterator>
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,9 @@ namespace internal {
|
||||||
const auto& polyline = polylines[i];
|
const auto& polyline = polylines[i];
|
||||||
|
|
||||||
out << polyline.size() << " ";
|
out << polyline.size() << " ";
|
||||||
for (std::size_t j = 0; j < polyline.size(); ++j)
|
for (std::size_t j = 0; j < polyline.size(); ++j) {
|
||||||
out << polyline[j] << " ";
|
out << polyline[j] << " ";
|
||||||
|
}
|
||||||
out << std::endl;
|
out << std::endl;
|
||||||
}
|
}
|
||||||
save(out, path + ".polylines");
|
save(out, path + ".polylines");
|
||||||
|
|
@ -133,8 +134,9 @@ namespace internal {
|
||||||
|
|
||||||
sorted.clear();
|
sorted.clear();
|
||||||
sorted.reserve(wraps.size());
|
sorted.reserve(wraps.size());
|
||||||
for (std::size_t i = 0; i < wraps.size(); ++i)
|
for (std::size_t i = 0; i < wraps.size(); ++i) {
|
||||||
sorted.push_back(i);
|
sorted.push_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
std::sort(sorted.begin(), sorted.end(),
|
std::sort(sorted.begin(), sorted.end(),
|
||||||
[&wraps](const std::size_t i, const std::size_t j) -> bool {
|
[&wraps](const std::size_t i, const std::size_t j) -> bool {
|
||||||
|
|
@ -243,8 +245,9 @@ namespace internal {
|
||||||
im = (im + n - 1) % n;
|
im = (im + n - 1) % n;
|
||||||
ip = (ip + 1) % n;
|
ip = (ip + 1) % n;
|
||||||
|
|
||||||
if (im == i || ip == i)
|
if (im == i || ip == i) {
|
||||||
stop = true;
|
stop = true;
|
||||||
|
}
|
||||||
++max_count;
|
++max_count;
|
||||||
|
|
||||||
} while (!stop && max_count < n);
|
} while (!stop && max_count < n);
|
||||||
|
|
@ -273,10 +276,11 @@ namespace internal {
|
||||||
const std::size_t di = assigned[i];
|
const std::size_t di = assigned[i];
|
||||||
const std::size_t dp = assigned[ip];
|
const std::size_t dp = assigned[ip];
|
||||||
|
|
||||||
if (dm != std::size_t(-1) && dm == dp && di != dm)
|
if (dm != std::size_t(-1) && dm == dp && di != dm) {
|
||||||
clean.push_back(dm);
|
clean.push_back(dm);
|
||||||
else
|
} else {
|
||||||
clean.push_back(di);
|
clean.push_back(di);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
assigned = clean;
|
assigned = clean;
|
||||||
}
|
}
|
||||||
|
|
@ -317,13 +321,16 @@ namespace internal {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stop) break;
|
if (stop) break;
|
||||||
if (im != std::size_t(-1) && im > 0)
|
if (im != std::size_t(-1) && im > 0) {
|
||||||
im = im - 1;
|
im = im - 1;
|
||||||
if (ip != std::size_t(-1) && ip < n - 1)
|
}
|
||||||
|
if (ip != std::size_t(-1) && ip < n - 1) {
|
||||||
ip = ip + 1;
|
ip = ip + 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (im == 0 || ip == n - 1)
|
if (im == 0 || ip == n - 1) {
|
||||||
stop = true;
|
stop = true;
|
||||||
|
}
|
||||||
++max_count;
|
++max_count;
|
||||||
|
|
||||||
} while (max_count < n);
|
} while (max_count < n);
|
||||||
|
|
@ -351,10 +358,11 @@ namespace internal {
|
||||||
CGAL_assertion(ip >= 0 && ip < n);
|
CGAL_assertion(ip >= 0 && ip < n);
|
||||||
const std::size_t di = assigned[i];
|
const std::size_t di = assigned[i];
|
||||||
const std::size_t dp = assigned[ip];
|
const std::size_t dp = assigned[ip];
|
||||||
if (di != dp)
|
if (di != dp) {
|
||||||
clean.push_back(dp);
|
clean.push_back(dp);
|
||||||
else
|
} else {
|
||||||
clean.push_back(di);
|
clean.push_back(di);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,10 +371,11 @@ namespace internal {
|
||||||
CGAL_assertion(im >= 0 && im < n);
|
CGAL_assertion(im >= 0 && im < n);
|
||||||
const std::size_t dm = assigned[im];
|
const std::size_t dm = assigned[im];
|
||||||
const std::size_t di = assigned[i];
|
const std::size_t di = assigned[i];
|
||||||
if (di != dm)
|
if (di != dm) {
|
||||||
clean.push_back(dm);
|
clean.push_back(dm);
|
||||||
else
|
} else {
|
||||||
clean.push_back(di);
|
clean.push_back(di);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,10 +388,11 @@ namespace internal {
|
||||||
CGAL_assertion(ip >= 0 && ip < n);
|
CGAL_assertion(ip >= 0 && ip < n);
|
||||||
const std::size_t dp = assigned[ip];
|
const std::size_t dp = assigned[ip];
|
||||||
|
|
||||||
if (dm != std::size_t(-1) && dm == dp && di != dm)
|
if (dm != std::size_t(-1) && dm == dp && di != dm) {
|
||||||
clean.push_back(dm);
|
clean.push_back(dm);
|
||||||
else
|
} else {
|
||||||
clean.push_back(di);
|
clean.push_back(di);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
assigned = clean;
|
assigned = clean;
|
||||||
}
|
}
|
||||||
|
|
@ -457,8 +467,9 @@ namespace internal {
|
||||||
query_index < assigned.size());
|
query_index < assigned.size());
|
||||||
|
|
||||||
const std::size_t direction_index = assigned[query_index];
|
const std::size_t direction_index = assigned[query_index];
|
||||||
if (direction_index == std::size_t(-1))
|
if (direction_index == std::size_t(-1)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CGAL_assertion(
|
CGAL_assertion(
|
||||||
direction_index >= 0 &&
|
direction_index >= 0 &&
|
||||||
|
|
@ -505,9 +516,11 @@ namespace internal {
|
||||||
std::vector<Segment_wrapper_2>& wraps) const {
|
std::vector<Segment_wrapper_2>& wraps) const {
|
||||||
|
|
||||||
std::vector<Segment_wrapper_2> clean;
|
std::vector<Segment_wrapper_2> clean;
|
||||||
for (const auto& wrap : wraps)
|
for (const auto& wrap : wraps) {
|
||||||
if (wrap.segment.squared_length() > internal::tolerance<FT>())
|
if (wrap.segment.squared_length() > internal::tolerance<FT>()) {
|
||||||
clean.push_back(wrap);
|
clean.push_back(wrap);
|
||||||
|
}
|
||||||
|
}
|
||||||
wraps = clean;
|
wraps = clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -586,8 +599,9 @@ namespace internal {
|
||||||
find_longest_segment(wraps);
|
find_longest_segment(wraps);
|
||||||
const Segment_2 weighted =
|
const Segment_2 weighted =
|
||||||
compute_weighted_segment(wraps, weights, ref_segment);
|
compute_weighted_segment(wraps, weights, ref_segment);
|
||||||
if (weighted.source() == weighted.target())
|
if (weighted.source() == weighted.target()) {
|
||||||
return ref_segment;
|
return ref_segment;
|
||||||
|
}
|
||||||
return weighted;
|
return weighted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -607,8 +621,9 @@ namespace internal {
|
||||||
}
|
}
|
||||||
|
|
||||||
CGAL_assertion(sum_distance > FT(0));
|
CGAL_assertion(sum_distance > FT(0));
|
||||||
for (auto& weight : weights)
|
for (auto& weight : weights) {
|
||||||
weight /= sum_distance;
|
weight /= sum_distance;
|
||||||
|
}
|
||||||
CGAL_assertion(weights.size() == wraps.size());
|
CGAL_assertion(weights.size() == wraps.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -744,9 +759,9 @@ namespace internal {
|
||||||
typename std::result_of<Intersect_2(Line_2, Line_2)>::type result
|
typename std::result_of<Intersect_2(Line_2, Line_2)>::type result
|
||||||
= CGAL::intersection(line_1, line_2);
|
= CGAL::intersection(line_1, line_2);
|
||||||
if (result) {
|
if (result) {
|
||||||
if (const Line_2* line = boost::get<Line_2>(&*result))
|
if (const Line_2* line = boost::get<Line_2>(&*result)) {
|
||||||
return false;
|
return false;
|
||||||
else {
|
} else {
|
||||||
const Point_2* point = boost::get<Point_2>(&*result);
|
const Point_2* point = boost::get<Point_2>(&*result);
|
||||||
in_point = *point; return true;
|
in_point = *point; return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,21 +62,24 @@ namespace internal {
|
||||||
if (m_wraps.size() < 2) return contour;
|
if (m_wraps.size() < 2) return contour;
|
||||||
|
|
||||||
rotate_contour(m_wraps);
|
rotate_contour(m_wraps);
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
m_base.export_polylines(
|
m_base.export_polylines(
|
||||||
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/rotated");
|
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/rotated");
|
||||||
|
}
|
||||||
|
|
||||||
bool success = optimize_contour(m_wraps);
|
bool success = optimize_contour(m_wraps);
|
||||||
if (!success) return contour;
|
if (!success) return contour;
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
m_base.export_polylines(
|
m_base.export_polylines(
|
||||||
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/optimized");
|
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/optimized");
|
||||||
|
}
|
||||||
|
|
||||||
success = connect_contour(m_wraps);
|
success = connect_contour(m_wraps);
|
||||||
if (!success) return contour;
|
if (!success) return contour;
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
m_base.export_polylines(
|
m_base.export_polylines(
|
||||||
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/connected");
|
m_wraps, "/Users/monet/Documents/gsoc/ggr/logs/connected");
|
||||||
|
}
|
||||||
|
|
||||||
return update_input(m_wraps, contour);
|
return update_input(m_wraps, contour);
|
||||||
}
|
}
|
||||||
|
|
@ -112,16 +115,18 @@ namespace internal {
|
||||||
// Merge parallel/collinear segments.
|
// Merge parallel/collinear segments.
|
||||||
std::vector<Segment_2> segments;
|
std::vector<Segment_2> segments;
|
||||||
m_base.create_unique_segments(m_max_offset_2, wraps, segments);
|
m_base.create_unique_segments(m_max_offset_2, wraps, segments);
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
std::cout <<
|
std::cout <<
|
||||||
"* number of segments (merging) = " << segments.size() << std::endl;
|
"* number of segments (merging) = " << segments.size() << std::endl;
|
||||||
|
}
|
||||||
if (wraps.size() < 1) return false;
|
if (wraps.size() < 1) return false;
|
||||||
|
|
||||||
// Add orthogonal segments.
|
// Add orthogonal segments.
|
||||||
create_orthogonal_segments(segments, wraps);
|
create_orthogonal_segments(segments, wraps);
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
std::cout <<
|
std::cout <<
|
||||||
"* number of segments (orthogonal) = " << wraps.size() << std::endl;
|
"* number of segments (orthogonal) = " << wraps.size() << std::endl;
|
||||||
|
}
|
||||||
if (wraps.size() < 1) return false;
|
if (wraps.size() < 1) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -189,8 +194,9 @@ namespace internal {
|
||||||
|
|
||||||
auto& si = wraps[i].segment;
|
auto& si = wraps[i].segment;
|
||||||
|
|
||||||
if (im == std::size_t(-1) && ip == std::size_t(-1))
|
if (im == std::size_t(-1) && ip == std::size_t(-1)) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (im == std::size_t(-1) && ip != std::size_t(-1)) {
|
if (im == std::size_t(-1) && ip != std::size_t(-1)) {
|
||||||
const auto& sp = wraps[ip].segment;
|
const auto& sp = wraps[ip].segment;
|
||||||
|
|
@ -238,9 +244,10 @@ namespace internal {
|
||||||
const std::size_t after = wraps.size();
|
const std::size_t after = wraps.size();
|
||||||
CGAL_assertion(after <= before);
|
CGAL_assertion(after <= before);
|
||||||
|
|
||||||
if (verbose())
|
if (verbose()) {
|
||||||
std::cout <<
|
std::cout <<
|
||||||
"* segments before/after = " << before << "/" << after << std::endl;
|
"* segments before/after = " << before << "/" << after << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename OutputIterator>
|
template<typename OutputIterator>
|
||||||
|
|
|
||||||
|
|
@ -101,8 +101,9 @@ namespace internal {
|
||||||
|
|
||||||
states[i] = true;
|
states[i] = true;
|
||||||
orthogonal_group.clear();
|
orthogonal_group.clear();
|
||||||
for (const std::size_t seg_index : parallel_groups[i])
|
for (const std::size_t seg_index : parallel_groups[i]) {
|
||||||
orthogonal_group.push_back(seg_index);
|
orthogonal_group.push_back(seg_index);
|
||||||
|
}
|
||||||
|
|
||||||
traverse_group(
|
traverse_group(
|
||||||
preserve_order, i, si, parallel_groups,
|
preserve_order, i, si, parallel_groups,
|
||||||
|
|
@ -134,8 +135,9 @@ namespace internal {
|
||||||
angle_2 >= FT(90) - m_max_angle) {
|
angle_2 >= FT(90) - m_max_angle) {
|
||||||
|
|
||||||
states[j] = true;
|
states[j] = true;
|
||||||
for (const std::size_t seg_index : parallel_groups[j])
|
for (const std::size_t seg_index : parallel_groups[j]) {
|
||||||
orthogonal_group.push_back(seg_index);
|
orthogonal_group.push_back(seg_index);
|
||||||
|
}
|
||||||
} else if (preserve_order) return;
|
} else if (preserve_order) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,9 @@ namespace internal {
|
||||||
std::back_inserter(collinear_groups));
|
std::back_inserter(collinear_groups));
|
||||||
m_segments.reserve(collinear_groups.size());
|
m_segments.reserve(collinear_groups.size());
|
||||||
|
|
||||||
for (const auto& collinear_group : collinear_groups)
|
for (const auto& collinear_group : collinear_groups) {
|
||||||
handle_collinear_group(collinear_group);
|
handle_collinear_group(collinear_group);
|
||||||
|
}
|
||||||
CGAL_assertion(m_segments.size() == collinear_groups.size());
|
CGAL_assertion(m_segments.size() == collinear_groups.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,8 +123,9 @@ namespace internal {
|
||||||
|
|
||||||
Segment_2 weighted = compute_weighted_segment(
|
Segment_2 weighted = compute_weighted_segment(
|
||||||
collinear_group, weights, ref_segment);
|
collinear_group, weights, ref_segment);
|
||||||
if (weighted.source() == weighted.target())
|
if (weighted.source() == weighted.target()) {
|
||||||
weighted = ref_segment;
|
weighted = ref_segment;
|
||||||
|
}
|
||||||
|
|
||||||
const Vector_2 ref_vector = weighted.to_vector();
|
const Vector_2 ref_vector = weighted.to_vector();
|
||||||
const Line_2 ref_line = Line_2(weighted.source(), weighted.target());
|
const Line_2 ref_line = Line_2(weighted.source(), weighted.target());
|
||||||
|
|
@ -162,8 +164,9 @@ namespace internal {
|
||||||
}
|
}
|
||||||
|
|
||||||
CGAL_assertion(sum_distance > FT(0));
|
CGAL_assertion(sum_distance > FT(0));
|
||||||
for (auto& weight : weights)
|
for (auto& weight : weights) {
|
||||||
weight /= sum_distance;
|
weight /= sum_distance;
|
||||||
|
}
|
||||||
CGAL_assertion(
|
CGAL_assertion(
|
||||||
weights.size() == collinear_group.size());
|
weights.size() == collinear_group.size());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,8 +181,9 @@ namespace internal {
|
||||||
using FT = typename Traits::FT;
|
using FT = typename Traits::FT;
|
||||||
using Direction_2 = typename Traits::Direction_2;
|
using Direction_2 = typename Traits::Direction_2;
|
||||||
|
|
||||||
if (v.y() < FT(0) || (v.y() == FT(0) && v.x() < FT(0)))
|
if (v.y() < FT(0) || (v.y() == FT(0) && v.x() < FT(0))) {
|
||||||
v = -v;
|
v = -v;
|
||||||
|
}
|
||||||
normalize_vector(v);
|
normalize_vector(v);
|
||||||
return Direction_2(v);
|
return Direction_2(v);
|
||||||
}
|
}
|
||||||
|
|
@ -280,8 +281,11 @@ namespace internal {
|
||||||
FT convert_angle_2(const FT angle_2) {
|
FT convert_angle_2(const FT angle_2) {
|
||||||
|
|
||||||
FT angle = angle_2;
|
FT angle = angle_2;
|
||||||
if (angle > FT(90)) angle = FT(180) - angle;
|
if (angle > FT(90)) {
|
||||||
else if (angle < -FT(90)) angle = FT(180) + angle;
|
angle = FT(180) - angle;
|
||||||
|
} else if (angle < -FT(90)) {
|
||||||
|
angle = FT(180) + angle;
|
||||||
|
}
|
||||||
return angle;
|
return angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -341,8 +345,11 @@ namespace internal {
|
||||||
Segment_2& segment) {
|
Segment_2& segment) {
|
||||||
|
|
||||||
FT angle_deg = angle_2_deg;
|
FT angle_deg = angle_2_deg;
|
||||||
if (angle_deg < FT(0)) angle_deg += ref_angle_2_deg;
|
if (angle_deg < FT(0)) {
|
||||||
else if (angle_deg > FT(0)) angle_deg -= ref_angle_2_deg;
|
angle_deg += ref_angle_2_deg;
|
||||||
|
} else if (angle_deg > FT(0)) {
|
||||||
|
angle_deg -= ref_angle_2_deg;
|
||||||
|
}
|
||||||
|
|
||||||
auto source = segment.source();
|
auto source = segment.source();
|
||||||
auto target = segment.target();
|
auto target = segment.target();
|
||||||
|
|
@ -431,10 +438,11 @@ namespace internal {
|
||||||
const Point pt1 = projected_origin + delta * line_vector;
|
const Point pt1 = projected_origin + delta * line_vector;
|
||||||
const Point pt2 = projected_origin - delta * line_vector;
|
const Point pt2 = projected_origin - delta * line_vector;
|
||||||
|
|
||||||
if (CGAL::squared_distance(pt_normal, pt1) <= CGAL::squared_distance(pt_normal, pt2))
|
if (CGAL::squared_distance(pt_normal, pt1) <= CGAL::squared_distance(pt_normal, pt2)) {
|
||||||
return Vector(CGAL::ORIGIN, pt1);
|
return Vector(CGAL::ORIGIN, pt1);
|
||||||
else
|
} else {
|
||||||
return Vector(CGAL::ORIGIN, pt2);
|
return Vector(CGAL::ORIGIN, pt2);
|
||||||
|
}
|
||||||
} else return n;
|
} else return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -457,8 +465,9 @@ namespace internal {
|
||||||
|
|
||||||
Line line;
|
Line line;
|
||||||
const CGAL::Object ob_1 = CGAL::intersection(plane_orthogonality, plane_symmetry);
|
const CGAL::Object ob_1 = CGAL::intersection(plane_orthogonality, plane_symmetry);
|
||||||
if (!assign(line, ob_1))
|
if (!assign(line, ob_1)) {
|
||||||
return regularize_normal<Traits>(n, symmetry_direction, cos_symmetry);
|
return regularize_normal<Traits>(n, symmetry_direction, cos_symmetry);
|
||||||
|
}
|
||||||
|
|
||||||
const Point projected_origin = line.projection(CGAL::ORIGIN);
|
const Point projected_origin = line.projection(CGAL::ORIGIN);
|
||||||
const FT R = CGAL::squared_distance(Point(CGAL::ORIGIN), projected_origin);
|
const FT R = CGAL::squared_distance(Point(CGAL::ORIGIN), projected_origin);
|
||||||
|
|
@ -471,12 +480,14 @@ namespace internal {
|
||||||
const Point pt2 = projected_origin - delta * line_vector;
|
const Point pt2 = projected_origin - delta * line_vector;
|
||||||
|
|
||||||
const Point pt_n = CGAL::ORIGIN + n;
|
const Point pt_n = CGAL::ORIGIN + n;
|
||||||
if (CGAL::squared_distance(pt_n, pt1) <= CGAL::squared_distance(pt_n, pt2))
|
if (CGAL::squared_distance(pt_n, pt1) <= CGAL::squared_distance(pt_n, pt2)) {
|
||||||
return Vector(CGAL::ORIGIN, pt1);
|
return Vector(CGAL::ORIGIN, pt1);
|
||||||
else
|
} else {
|
||||||
return Vector(CGAL::ORIGIN, pt2);
|
return Vector(CGAL::ORIGIN, pt2);
|
||||||
} else // no point intersecting the unit sphere and line
|
}
|
||||||
|
} else { // no point intersecting the unit sphere and line
|
||||||
return regularize_normal<Traits>(n, symmetry_direction, cos_symmetry);
|
return regularize_normal<Traits>(n, symmetry_direction, cos_symmetry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<
|
template<
|
||||||
|
|
@ -498,9 +509,10 @@ namespace internal {
|
||||||
std::vector< std::vector<Point> > listp(nb_planes);
|
std::vector< std::vector<Point> > listp(nb_planes);
|
||||||
for (std::size_t i = 0; i < points.size(); ++i) {
|
for (std::size_t i = 0; i < points.size(); ++i) {
|
||||||
const int idx = get(index_map, i);
|
const int idx = get(index_map, i);
|
||||||
if (idx != -1)
|
if (idx != -1) {
|
||||||
listp[std::size_t(idx)].push_back(
|
listp[std::size_t(idx)].push_back(
|
||||||
get(point_map, *(points.begin() + i)));
|
get(point_map, *(points.begin() + i)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
centroids.reserve(nb_planes);
|
centroids.reserve(nb_planes);
|
||||||
|
|
@ -539,8 +551,9 @@ namespace internal {
|
||||||
const auto it2 = planes.begin() + j;
|
const auto it2 = planes.begin() + j;
|
||||||
const Vector v2 = get(plane_map, *it2).orthogonal_vector();
|
const Vector v2 = get(plane_map, *it2).orthogonal_vector();
|
||||||
|
|
||||||
if (CGAL::abs(v1 * v2) > FT(1) - tolerance_cosangle)
|
if (CGAL::abs(v1 * v2) > FT(1) - tolerance_cosangle) {
|
||||||
parallel_planes[i].push_back(j);
|
parallel_planes[i].push_back(j);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -581,8 +594,9 @@ namespace internal {
|
||||||
index_container_current_ring_parallel.push_back(it);
|
index_container_current_ring_parallel.push_back(it);
|
||||||
is_available[it] = false;
|
is_available[it] = false;
|
||||||
|
|
||||||
if (clu.normal * normal_it < FT(0))
|
if (clu.normal * normal_it < FT(0)) {
|
||||||
normal_it = -normal_it;
|
normal_it = -normal_it;
|
||||||
|
}
|
||||||
|
|
||||||
clu.normal = FT(clu.area) * clu.normal + FT(areas[it]) * normal_it;
|
clu.normal = FT(clu.area) * clu.normal + FT(areas[it]) * normal_it;
|
||||||
const FT norm = FT(1) / CGAL::sqrt(clu.normal.squared_length());
|
const FT norm = FT(1) / CGAL::sqrt(clu.normal.squared_length());
|
||||||
|
|
@ -625,8 +639,9 @@ namespace internal {
|
||||||
|
|
||||||
std::vector<FT> cosangle_centroids;
|
std::vector<FT> cosangle_centroids;
|
||||||
std::vector<std::size_t> list_cluster_index;
|
std::vector<std::size_t> list_cluster_index;
|
||||||
for (std::size_t i = 0; i < clusters.size(); ++i)
|
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
||||||
list_cluster_index.push_back(static_cast<std::size_t>(-1));
|
list_cluster_index.push_back(static_cast<std::size_t>(-1));
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t mean_index = 0;
|
std::size_t mean_index = 0;
|
||||||
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
||||||
|
|
@ -651,14 +666,16 @@ namespace internal {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::size_t i = 0; i < cosangle_centroids.size(); ++i) {
|
for (std::size_t i = 0; i < cosangle_centroids.size(); ++i) {
|
||||||
if (cosangle_centroids[i] < tolerance_cosangle_ortho)
|
if (cosangle_centroids[i] < tolerance_cosangle_ortho) {
|
||||||
cosangle_centroids[i] = FT(0);
|
cosangle_centroids[i] = FT(0);
|
||||||
else if (cosangle_centroids[i] > FT(1) - tolerance_cosangle)
|
} else if (cosangle_centroids[i] > FT(1) - tolerance_cosangle) {
|
||||||
cosangle_centroids[i] = FT(1);
|
cosangle_centroids[i] = FT(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::size_t i = 0; i < clusters.size(); ++i)
|
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
||||||
clusters[i].cosangle_symmetry = cosangle_centroids[list_cluster_index[i]];
|
clusters[i].cosangle_symmetry = cosangle_centroids[list_cluster_index[i]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Traits>
|
template<typename Traits>
|
||||||
|
|
@ -672,8 +689,9 @@ namespace internal {
|
||||||
std::vector< std::vector<std::size_t> > subgraph_clusters;
|
std::vector< std::vector<std::size_t> > subgraph_clusters;
|
||||||
std::vector<std::size_t> subgraph_clusters_max_area_index;
|
std::vector<std::size_t> subgraph_clusters_max_area_index;
|
||||||
|
|
||||||
for (std::size_t i = 0; i < clusters.size(); ++i)
|
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
||||||
clusters[i].is_free = true;
|
clusters[i].is_free = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
||||||
if (clusters[i].is_free) {
|
if (clusters[i].is_free) {
|
||||||
|
|
@ -736,9 +754,11 @@ namespace internal {
|
||||||
|
|
||||||
const std::size_t index = subgraph_clusters_max_area_index[i];
|
const std::size_t index = subgraph_clusters_max_area_index[i];
|
||||||
std::vector<std::size_t> subgraph_clusters_prop_temp;
|
std::vector<std::size_t> subgraph_clusters_prop_temp;
|
||||||
for (std::size_t j = 0; j < subgraph_clusters[i].size(); ++j)
|
for (std::size_t j = 0; j < subgraph_clusters[i].size(); ++j) {
|
||||||
if (subgraph_clusters[i][j] != index)
|
if (subgraph_clusters[i][j] != index) {
|
||||||
subgraph_clusters_prop_temp.push_back(subgraph_clusters[i][j]);
|
subgraph_clusters_prop_temp.push_back(subgraph_clusters[i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
subgraph_clusters_prop.push_back(subgraph_clusters_prop_temp);
|
subgraph_clusters_prop.push_back(subgraph_clusters_prop_temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -746,8 +766,9 @@ namespace internal {
|
||||||
// from the largest area cluster and we propagate over the subgraph
|
// from the largest area cluster and we propagate over the subgraph
|
||||||
// by regularizing the normals of the clusters according to the
|
// by regularizing the normals of the clusters according to the
|
||||||
// orthogonality and cos angle to symmetry direction.
|
// orthogonality and cos angle to symmetry direction.
|
||||||
for (std::size_t i = 0; i < clusters.size(); ++i)
|
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
||||||
clusters[i].is_free = true;
|
clusters[i].is_free = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (std::size_t i = 0; i < subgraph_clusters_prop.size(); ++i) {
|
for (std::size_t i = 0; i < subgraph_clusters_prop.size(); ++i) {
|
||||||
const std::size_t index_current = subgraph_clusters_max_area_index[i];
|
const std::size_t index_current = subgraph_clusters_max_area_index[i];
|
||||||
|
|
|
||||||
|
|
@ -109,9 +109,10 @@ namespace Planes {
|
||||||
|
|
||||||
// Find subgraphs of mutually orthogonal clusters (store indices of
|
// Find subgraphs of mutually orthogonal clusters (store indices of
|
||||||
// clusters in subgraph_clusters), and select the cluster of the largest area.
|
// clusters in subgraph_clusters), and select the cluster of the largest area.
|
||||||
if (regularize_orthogonality || regularize_axis_symmetry)
|
if (regularize_orthogonality || regularize_axis_symmetry) {
|
||||||
internal::subgraph_mutually_orthogonal_clusters<Kernel>(
|
internal::subgraph_mutually_orthogonal_clusters<Kernel>(
|
||||||
clusters, (regularize_axis_symmetry ? symmetry_direction : CGAL::NULL_VECTOR));
|
clusters, (regularize_axis_symmetry ? symmetry_direction : CGAL::NULL_VECTOR));
|
||||||
|
}
|
||||||
|
|
||||||
// Recompute optimal plane for each primitive after the normal regularization.
|
// Recompute optimal plane for each primitive after the normal regularization.
|
||||||
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
||||||
|
|
@ -121,12 +122,14 @@ namespace Planes {
|
||||||
const Plane& plane = get(plane_map, *(planes.begin() + index_prim));
|
const Plane& plane = get(plane_map, *(planes.begin() + index_prim));
|
||||||
|
|
||||||
const Point pt_reg = plane.projection(centroids[index_prim]);
|
const Point pt_reg = plane.projection(centroids[index_prim]);
|
||||||
if (plane.orthogonal_vector() * vec_reg < FT(0))
|
if (plane.orthogonal_vector() * vec_reg < FT(0)) {
|
||||||
vec_reg = -vec_reg;
|
vec_reg = -vec_reg;
|
||||||
|
}
|
||||||
const Plane plane_reg(pt_reg, vec_reg);
|
const Plane plane_reg(pt_reg, vec_reg);
|
||||||
|
|
||||||
if (CGAL::abs(plane.orthogonal_vector() * vec_reg) > FT(1) - tolerance_cosangle)
|
if (CGAL::abs(plane.orthogonal_vector() * vec_reg) > FT(1) - tolerance_cosangle) {
|
||||||
put(plane_map, *(planes.begin() + index_prim), plane_reg);
|
put(plane_map, *(planes.begin() + index_prim), plane_reg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,8 +137,9 @@ namespace Planes {
|
||||||
// Detect. co-planarity and use list_coplanar_prim to store the results.
|
// Detect. co-planarity and use list_coplanar_prim to store the results.
|
||||||
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
for (std::size_t i = 0; i < clusters.size(); ++i) {
|
||||||
Vector vec_reg = clusters[i].normal;
|
Vector vec_reg = clusters[i].normal;
|
||||||
for (std::size_t ip = 0; ip < clusters[i].planes.size(); ++ip)
|
for (std::size_t ip = 0; ip < clusters[i].planes.size(); ++ip) {
|
||||||
clusters[i].coplanar_group.push_back(static_cast<std::size_t>(-1));
|
clusters[i].coplanar_group.push_back(static_cast<std::size_t>(-1));
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t cop_index = 0;
|
std::size_t cop_index = 0;
|
||||||
for (std::size_t j = 0; j < clusters[i].planes.size(); ++j) {
|
for (std::size_t j = 0; j < clusters[i].planes.size(); ++j) {
|
||||||
|
|
@ -156,8 +160,9 @@ namespace Planes {
|
||||||
const Point pt_proj = plan_reg.projection(pt_reg_next);
|
const Point pt_proj = plan_reg.projection(pt_reg_next);
|
||||||
const FT distance = CGAL::sqrt(CGAL::squared_distance(pt_reg_next, pt_proj));
|
const FT distance = CGAL::sqrt(CGAL::squared_distance(pt_reg_next, pt_proj));
|
||||||
|
|
||||||
if (distance < tolerance_coplanarity)
|
if (distance < tolerance_coplanarity) {
|
||||||
clusters[i].coplanar_group[k] = cop_index;
|
clusters[i].coplanar_group[k] = cop_index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++cop_index;
|
++cop_index;
|
||||||
|
|
@ -186,10 +191,11 @@ namespace Planes {
|
||||||
|
|
||||||
if (get(plane_map,
|
if (get(plane_map,
|
||||||
*(planes.begin() + index_prim)).orthogonal_vector()
|
*(planes.begin() + index_prim)).orthogonal_vector()
|
||||||
* plane_reg.orthogonal_vector() < 0)
|
* plane_reg.orthogonal_vector() < 0) {
|
||||||
put(plane_map, *(planes.begin() + index_prim), plane_reg.opposite());
|
put(plane_map, *(planes.begin() + index_prim), plane_reg.opposite());
|
||||||
else
|
} else {
|
||||||
put(plane_map, *(planes.begin() + index_prim), plane_reg);
|
put(plane_map, *(planes.begin() + index_prim), plane_reg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,9 @@ namespace Tests {
|
||||||
|
|
||||||
const FT stub = FT(0);
|
const FT stub = FT(0);
|
||||||
std::vector<Segment_2> edges;
|
std::vector<Segment_2> edges;
|
||||||
for (const std::size_t seg_index : group)
|
for (const std::size_t seg_index : group) {
|
||||||
edges.push_back(segments[seg_index]);
|
edges.push_back(segments[seg_index]);
|
||||||
|
}
|
||||||
export_segments(edges, path, stub);
|
export_segments(edges, path, stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,8 +70,9 @@ namespace Tests {
|
||||||
const std::string path,
|
const std::string path,
|
||||||
const FT) {
|
const FT) {
|
||||||
|
|
||||||
if (contour.size() == 0)
|
if (contour.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const FT stub = FT(0);
|
const FT stub = FT(0);
|
||||||
std::vector<Segment_2> segments;
|
std::vector<Segment_2> segments;
|
||||||
|
|
@ -92,8 +94,9 @@ namespace Tests {
|
||||||
const std::string path,
|
const std::string path,
|
||||||
const FT) {
|
const FT) {
|
||||||
|
|
||||||
if (contour.size() == 0)
|
if (contour.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const FT stub = FT(0);
|
const FT stub = FT(0);
|
||||||
std::vector<Segment_2> segments;
|
std::vector<Segment_2> segments;
|
||||||
|
|
@ -115,8 +118,7 @@ namespace Tests {
|
||||||
const std::string path,
|
const std::string path,
|
||||||
FT scale) {
|
FT scale) {
|
||||||
|
|
||||||
if (input.size() == 0)
|
if (input.size() == 0) return;
|
||||||
return;
|
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
// Compute barycenter.
|
// Compute barycenter.
|
||||||
|
|
@ -169,8 +171,9 @@ namespace Tests {
|
||||||
const FT scale) {
|
const FT scale) {
|
||||||
|
|
||||||
std::vector<Segment_2> edges;
|
std::vector<Segment_2> edges;
|
||||||
for (const std::size_t seg_index : group)
|
for (const std::size_t seg_index : group) {
|
||||||
edges.push_back(segments[seg_index]);
|
edges.push_back(segments[seg_index]);
|
||||||
|
}
|
||||||
export_eps_segments(edges, path, scale);
|
export_eps_segments(edges, path, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,8 +182,9 @@ namespace Tests {
|
||||||
const std::string path,
|
const std::string path,
|
||||||
FT scale) {
|
FT scale) {
|
||||||
|
|
||||||
if (contour.size() == 0)
|
if (contour.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Segment_2> segments;
|
std::vector<Segment_2> segments;
|
||||||
const std::size_t n = contour.size();
|
const std::size_t n = contour.size();
|
||||||
|
|
@ -201,8 +205,9 @@ namespace Tests {
|
||||||
const std::string path,
|
const std::string path,
|
||||||
FT scale) {
|
FT scale) {
|
||||||
|
|
||||||
if (contour.size() == 0)
|
if (contour.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Segment_2> segments;
|
std::vector<Segment_2> segments;
|
||||||
const std::size_t n = contour.size();
|
const std::size_t n = contour.size();
|
||||||
|
|
@ -244,16 +249,18 @@ namespace Tests {
|
||||||
const std::vector<Polyline>& polylines,
|
const std::vector<Polyline>& polylines,
|
||||||
const std::string path) {
|
const std::string path) {
|
||||||
|
|
||||||
if (polylines.size() == 0)
|
if (polylines.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
for (std::size_t i = 0; i < polylines.size(); ++i) {
|
for (std::size_t i = 0; i < polylines.size(); ++i) {
|
||||||
const auto& polyline = polylines[i];
|
const auto& polyline = polylines[i];
|
||||||
|
|
||||||
out << polyline.size() << " ";
|
out << polyline.size() << " ";
|
||||||
for (std::size_t j = 0; j < polyline.size(); ++j)
|
for (std::size_t j = 0; j < polyline.size(); ++j) {
|
||||||
out << polyline[j] << " ";
|
out << polyline[j] << " ";
|
||||||
|
}
|
||||||
out << std::endl;
|
out << std::endl;
|
||||||
}
|
}
|
||||||
save(path + ".polylines");
|
save(path + ".polylines");
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,9 @@ double get_coefficient_value(
|
||||||
(theta > 3.0 * CGAL_PI / 2.0 && theta < 7.0 * CGAL_PI / 4.0)) {
|
(theta > 3.0 * CGAL_PI / 2.0 && theta < 7.0 * CGAL_PI / 4.0)) {
|
||||||
|
|
||||||
iterator += 0.02;
|
iterator += 0.02;
|
||||||
} else
|
} else {
|
||||||
iterator -= 0.02;
|
iterator -= 0.02;
|
||||||
|
}
|
||||||
|
|
||||||
if (theta < CGAL_PI) return -1.0 * iterator;
|
if (theta < CGAL_PI) return -1.0 * iterator;
|
||||||
return iterator;
|
return iterator;
|
||||||
|
|
|
||||||
|
|
@ -122,9 +122,11 @@ void check_planes_changed(
|
||||||
const std::vector<Plane>& before,
|
const std::vector<Plane>& before,
|
||||||
const std::vector<Plane>& after) {
|
const std::vector<Plane>& after) {
|
||||||
|
|
||||||
for (std::size_t i = 0; i < before.size(); ++i)
|
for (std::size_t i = 0; i < before.size(); ++i) {
|
||||||
if (planes_difference(before[i], after[i]))
|
if (planes_difference(before[i], after[i])) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
std::cerr << "Error: no plane has been altered by regularization" <<
|
std::cerr << "Error: no plane has been altered by regularization" <<
|
||||||
" while at least one should have." << std::endl;
|
" while at least one should have." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue