mirror of https://github.com/CGAL/cgal
1956 lines
63 KiB
C++
1956 lines
63 KiB
C++
// Copyright(c) 2012, 2020 Tel - Aviv University(Israel).
|
|
// All rights reserved.
|
|
//
|
|
// This file is part of CGAL (www.cgal.org).
|
|
//
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
|
|
//
|
|
// Author(s): Engin Deniz Diktas <denizdiktas@gmail.com>
|
|
|
|
#include "Aos.h"
|
|
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <map>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
#include <qmath.h>
|
|
#include <qvector3d.h>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
using json = nlohmann::ordered_json;
|
|
|
|
// Includes for Arrangements on Sphere (AOS)
|
|
#include "Aos_defs.h"
|
|
//#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
|
|
//#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
|
//#include <CGAL/Arrangement_on_surface_2.h>
|
|
//#include <CGAL/Arr_extended_dcel.h>
|
|
//#include <CGAL/Arr_geodesic_arc_on_sphere_traits_2.h>
|
|
//#include <CGAL/Arr_spherical_topology_traits_2.h>
|
|
//#include <CGAL/Vector_3.h>
|
|
#include "arr_print.h"
|
|
#include "Tools.h"
|
|
|
|
// Includes for Constrained Delaunay Triangulation
|
|
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
|
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
|
|
#include <CGAL/draw_triangulation_2.h>
|
|
#include <CGAL/mark_domain_in_triangulation.h>
|
|
#include <CGAL/Polygon_2.h>
|
|
//#include <CGAL/Projection_traits_3.h>
|
|
#include <unordered_map>
|
|
#include <boost/property_map/property_map.hpp>
|
|
|
|
// Include for point location queries
|
|
#include <CGAL/Arr_naive_point_location.h>
|
|
#include <CGAL/Arr_point_location_result.h>
|
|
|
|
|
|
namespace {
|
|
// //#define USE_EPIC
|
|
//
|
|
//#ifdef USE_EPIC
|
|
// using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
|
|
//#else
|
|
// using Kernel = CGAL::Exact_predicates_exact_constructions_kernel;
|
|
//#endif
|
|
//
|
|
// using Geom_traits = CGAL::Arr_geodesic_arc_on_sphere_traits_2<Kernel>;
|
|
// using Point = Geom_traits::Point_2;
|
|
// using Curve = Geom_traits::Curve_2;
|
|
// using Topol_traits = CGAL::Arr_spherical_topology_traits_2<Geom_traits>;
|
|
// using Arrangement = CGAL::Arrangement_on_surface_2<Geom_traits, Topol_traits>;
|
|
//
|
|
// // the following is from "arr_inexact_construction_segments.h":
|
|
// using Segment = Geom_traits::X_monotone_curve_2;
|
|
// using Vertex_handle = Arrangement::Vertex_handle;
|
|
|
|
// use this traits everytime you construct an arrangment!
|
|
static Geom_traits s_traits;
|
|
|
|
// Extended DCEL & Arrangement
|
|
struct Flag
|
|
{
|
|
bool v;
|
|
Flag() : v{ false } {}
|
|
Flag(bool init) : v{ init } {}
|
|
};
|
|
|
|
// EXTENDED AOS for analysing the arrangement
|
|
using Ext_dcel = CGAL::Arr_extended_dcel<Geom_traits, Flag, Flag, Flag>;
|
|
using Ext_topol_traits = CGAL::Arr_spherical_topology_traits_2<Geom_traits,
|
|
Ext_dcel>;
|
|
using Ext_aos = CGAL::Arrangement_on_surface_2<Geom_traits, Ext_topol_traits>;
|
|
|
|
// COUNTRIES AOS for grouping the faces by the country name
|
|
//using Countries_dcel = CGAL::Arr_face_extended_dcel<Geom_traits, std::string>;
|
|
//using Countries_topol_traits =
|
|
// CGAL::Arr_spherical_topology_traits_2<Geom_traits, Countries_dcel>;
|
|
//using Countries_arr =
|
|
// CGAL::Arrangement_on_surface_2<Geom_traits, Countries_topol_traits>;
|
|
|
|
|
|
|
|
using Dir3 = Kernel::Direction_3;
|
|
std::ostream& operator << (std::ostream& os, const Dir3& d)
|
|
{
|
|
os << d.dx() << ", " << d.dy() << ", " << d.dz();
|
|
return os;
|
|
}
|
|
|
|
using Approximate_point_2 = Geom_traits::Approximate_point_2;
|
|
std::ostream& operator << (std::ostream& os, const Approximate_point_2& d)
|
|
{
|
|
os << d.dx() << ", " << d.dy() << ", " << d.dz();
|
|
return os;
|
|
}
|
|
|
|
using Approximate_number_type = Geom_traits::Approximate_number_type;
|
|
using Approximate_kernel = Geom_traits::Approximate_kernel;
|
|
using Approximate_Vector_3 = CGAL::Vector_3<Approximate_kernel>;
|
|
using Approximate_Direction_3 = Approximate_kernel::Direction_3;
|
|
using Direction_3 = Kernel::Direction_3;
|
|
|
|
|
|
std::ostream& operator << (std::ostream& os, const Approximate_Vector_3& v)
|
|
{
|
|
os << v.x() << ", " << v.y() << ", " << v.z();
|
|
//os << v.hx() << ", " << v.hy() << ", " << v.hz() << ", " << v.hw();
|
|
return os;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// below are the helper functions used to construct the arcs from KML data
|
|
// TODO: Revisit handling of INNER & OUTER boundaries
|
|
using Curves = std::vector<Curve>;
|
|
|
|
// get curves for the given kml placemark
|
|
// NOTE: this is defined here to keep the definitions local to this cpp file
|
|
Curves get_arcs(const Kml::Placemark& placemark)
|
|
{
|
|
//Geom_traits traits;
|
|
auto ctr_p = s_traits.construct_point_2_object();
|
|
auto ctr_cv = s_traits.construct_curve_2_object();
|
|
|
|
std::vector<Curve> xcvs;
|
|
for (const auto& polygon : placemark.polygons)
|
|
{
|
|
// colect all rings into a single list (FOR NOW!!!)
|
|
// TO-DO: PROCESS OUTER & INNER BOUNDARIES SEPARATELY!!!
|
|
Kml::LinearRings linear_rings;
|
|
linear_rings.push_back(polygon.outer_boundary);
|
|
for (const auto& inner_boundary : polygon.inner_boundaries)
|
|
linear_rings.push_back(inner_boundary);
|
|
|
|
|
|
// convert the nodes to points on unit-sphere
|
|
for (const auto& lring : linear_rings)
|
|
{
|
|
std::vector<Approximate_Vector_3> sphere_points;
|
|
for (const auto& node : lring.nodes)
|
|
{
|
|
const auto p = node.get_coords_3d();
|
|
Approximate_Vector_3 v(p.x, p.y, p.z);
|
|
sphere_points.push_back(v);
|
|
}
|
|
|
|
// add geodesic arcs for the current LinearRing
|
|
int num_points = sphere_points.size();
|
|
for (int i = 0; i < num_points - 1; i++)
|
|
{
|
|
const auto p1 = sphere_points[i];
|
|
const auto p2 = sphere_points[i + 1];
|
|
xcvs.push_back(ctr_cv(ctr_p(p1.x(), p1.y(), p1.z()),
|
|
ctr_p(p2.x(), p2.y(), p2.z())));
|
|
}
|
|
}
|
|
}
|
|
|
|
return xcvs;
|
|
}
|
|
|
|
|
|
// this one is used by the Aos::check and Aos::ext_check functions
|
|
int num_counted_nodes = 0;
|
|
int num_counted_arcs = 0;
|
|
int num_counted_polygons = 0;
|
|
std::map<Ext_aos::Vertex_handle, Kml::Node> vertex_node_map;
|
|
|
|
template<typename Arr_type>
|
|
Curves get_arcs(const Kml::Placemarks& placemarks, Arr_type& arr)
|
|
{
|
|
//Geom_traits traits;
|
|
auto ctr_p = s_traits.construct_point_2_object();
|
|
auto ctr_cv = s_traits.construct_curve_2_object();
|
|
|
|
num_counted_nodes = 0;
|
|
num_counted_arcs = 0;
|
|
num_counted_polygons = 0;
|
|
std::vector<Curve> xcvs;
|
|
for (const auto& pm : placemarks)
|
|
{
|
|
for (const auto& polygon : pm.polygons)
|
|
{
|
|
num_counted_polygons++;
|
|
|
|
// colect all rings into a single list (FOR NOW!!!)
|
|
// TO-DO: PROCESS OUTER & INNER BOUNDARIES SEPARATELY!!!
|
|
Kml::LinearRings linear_rings;
|
|
linear_rings.push_back(polygon.outer_boundary);
|
|
for (const auto& inner_boundary : polygon.inner_boundaries)
|
|
linear_rings.push_back(inner_boundary);
|
|
|
|
// loop on outer and inner boundaries
|
|
for (const auto& lring : linear_rings)
|
|
{
|
|
// convert the nodes to points on unit-sphere
|
|
std::vector<Approximate_Vector_3> sphere_points;
|
|
for (const auto& node : lring.nodes)
|
|
{
|
|
num_counted_nodes++;
|
|
const auto p = node.get_coords_3d();
|
|
Approximate_Vector_3 v(p.x, p.y, p.z);
|
|
sphere_points.push_back(v);
|
|
auto vh = CGAL::insert_point(arr, ctr_p(p.x, p.y, p.z));
|
|
if constexpr (std::is_same<Arr_type, Ext_aos>::value)
|
|
{
|
|
vertex_node_map.insert(std::make_pair(vh, node));
|
|
}
|
|
}
|
|
|
|
// add curves
|
|
int num_points = sphere_points.size();
|
|
for (int i = 0; i < num_points - 1; i++)
|
|
{
|
|
num_counted_arcs++;
|
|
const auto p1 = sphere_points[i];
|
|
const auto p2 = sphere_points[i + 1];
|
|
auto xcv = ctr_cv(ctr_p(p1.x(), p1.y(), p1.z()),
|
|
ctr_p(p2.x(), p2.y(), p2.z()));
|
|
xcvs.push_back(xcv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return xcvs;
|
|
}
|
|
|
|
|
|
Aos::Approx_arc get_approx_curve(Curve xcv, double error)
|
|
{
|
|
//Geom_traits traits;
|
|
auto approx = s_traits.approximate_2_object();
|
|
std::vector<QVector3D> approx_curve;
|
|
{
|
|
std::vector<Approximate_point_2> v;
|
|
auto oi2 = approx(xcv, error, std::back_insert_iterator(v));
|
|
|
|
for (const auto& p : v)
|
|
{
|
|
const QVector3D arc_point(p.dx(), p.dy(), p.dz());
|
|
approx_curve.push_back(arc_point);
|
|
}
|
|
}
|
|
|
|
return approx_curve;
|
|
}
|
|
Aos::Approx_arcs get_approx_curves(std::vector<Curve>& xcvs, double error)
|
|
{
|
|
Aos::Approx_arcs approx_curves;
|
|
for (const auto& xcv : xcvs)
|
|
{
|
|
auto approx_curve = get_approx_curve(xcv, error);
|
|
approx_curves.push_back(std::move(approx_curve));
|
|
}
|
|
|
|
return approx_curves;
|
|
}
|
|
}
|
|
|
|
|
|
Aos::Approx_arc Aos::get_approx_identification_curve(double error)
|
|
{
|
|
//Geom_traits traits;
|
|
auto ctr_p = s_traits.construct_point_2_object();
|
|
auto ctr_cv = s_traits.construct_curve_2_object();
|
|
|
|
// identification curve (meridian pierced by NEGATIVE Y-AXIS)
|
|
auto xcv = ctr_cv(ctr_p(0, 0, -1), ctr_p(0, 0, 1), Dir3(0, 1, 0));
|
|
|
|
auto approx = s_traits.approximate_2_object();
|
|
Approx_arc approx_arc;
|
|
{
|
|
std::vector<Approximate_point_2> v;
|
|
auto oi2 = approx(xcv, error, std::back_insert_iterator(v));
|
|
for (const auto& p : v)
|
|
{
|
|
const QVector3D arc_point(p.dx(), p.dy(), p.dz());
|
|
approx_arc.push_back(arc_point);
|
|
}
|
|
}
|
|
|
|
return approx_arc;
|
|
}
|
|
|
|
Aos::Approx_arcs Aos::get_approx_arcs(double error)
|
|
{
|
|
//Geom_traits traits;
|
|
Arrangement arr(&s_traits);
|
|
|
|
auto ctr_p = s_traits.construct_point_2_object();
|
|
auto ctr_cv = s_traits.construct_curve_2_object();
|
|
|
|
std::vector<Curve> xcvs;
|
|
xcvs.push_back(ctr_cv(ctr_p(1, 0, 0), ctr_p(0, 1, 0)));
|
|
xcvs.push_back(ctr_cv(ctr_p(1, 0, 0), ctr_p(0, 0, 1)));
|
|
xcvs.push_back(ctr_cv(ctr_p(0, 1, 0), ctr_p(0, 0, 1)));
|
|
//xcvs.push_back(ctr_cv(ctr_p(1, 0, 0), ctr_p(0, 1, 0), Dir3(0, 0, -1)));
|
|
//xcvs.push_back(ctr_cv(Dir3(0, 0, -1)));
|
|
|
|
auto approx = s_traits.approximate_2_object();
|
|
|
|
auto approx_arcs = get_approx_curves(xcvs, error);
|
|
//std::vector<std::vector<QVector3D>> arcs;
|
|
//for (const auto& xcv : xcvs)
|
|
//{
|
|
// std::vector<Approximate_point_2> v;
|
|
// auto oi2 = approx(xcv, error, std::back_insert_iterator(v));
|
|
//
|
|
// std::vector<QVector3D> arc_points;
|
|
// for (const auto& p : v)
|
|
// {
|
|
// const QVector3D arc_point(p.dx(), p.dy(), p.dz());
|
|
// arc_points.push_back(arc_point);
|
|
// }
|
|
// arcs.push_back(std::move(arc_points));
|
|
//}
|
|
//std::cout << "offset count = " << m_arc_offsets.size() << std::endl;
|
|
|
|
return approx_arcs;
|
|
}
|
|
Aos::Approx_arcs Aos::get_approx_arcs(const Kml::Placemark& placemark, double error)
|
|
{
|
|
//Geom_traits traits;
|
|
auto ctr_p = s_traits.construct_point_2_object();
|
|
auto ctr_cv = s_traits.construct_curve_2_object();
|
|
|
|
auto xcvs = get_arcs(placemark);
|
|
|
|
auto approx = s_traits.approximate_2_object();
|
|
std::vector<std::vector<QVector3D>> arcs;
|
|
for (const auto& xcv : xcvs)
|
|
{
|
|
std::vector<Approximate_point_2> v;
|
|
auto oi2 = approx(xcv, error, std::back_insert_iterator(v));
|
|
|
|
std::vector<QVector3D> arc_points;
|
|
for (const auto& p : v)
|
|
{
|
|
const QVector3D arc_point(p.dx(), p.dy(), p.dz());
|
|
arc_points.push_back(arc_point);
|
|
}
|
|
arcs.push_back(std::move(arc_points));
|
|
}
|
|
//std::cout << "offset count = " << m_arc_offsets.size() << std::endl;
|
|
|
|
return arcs;
|
|
}
|
|
|
|
void Aos::check(const Kml::Placemarks& placemarks)
|
|
{
|
|
//Geom_traits traits;
|
|
Arrangement arr(&s_traits);
|
|
|
|
auto xcvs = get_arcs(placemarks, arr);
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num arr vertices (before adding arcs) = " <<
|
|
arr.number_of_vertices() << std::endl;
|
|
// add arcs
|
|
for (auto xcv : xcvs)
|
|
CGAL::insert(arr, xcv);
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num nodes = " << num_counted_nodes << std::endl;
|
|
std::cout << "num arr vertices = " << arr.number_of_vertices() << std::endl;
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num counted arcs = " << num_counted_arcs << std::endl;
|
|
std::cout << "num arr edges = " << arr.number_of_edges() << std::endl;
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num polygons = " << num_counted_polygons << std::endl;
|
|
std::cout << "num arr faces = " << arr.number_of_faces() << std::endl;
|
|
}
|
|
|
|
std::vector<QVector3D> Aos::ext_check(const Kml::Placemarks& placemarks)
|
|
{
|
|
// Construct the arrangement from 12 geodesic arcs.
|
|
//Geom_traits traits;
|
|
Ext_aos arr(&s_traits);
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "** num arr FACES (before adding arcs) = " <<
|
|
arr.number_of_faces() << std::endl;
|
|
|
|
auto xcvs = get_arcs(placemarks, arr);
|
|
|
|
// MARK all vertices as true
|
|
for (auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit)
|
|
{
|
|
vit->set_data(Flag(true));
|
|
}
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num arr vertices (before adding arcs) = " <<
|
|
arr.number_of_vertices() << std::endl;
|
|
|
|
// add arcs
|
|
for (auto& xcv : xcvs)
|
|
CGAL::insert(arr, xcv);
|
|
|
|
// extract all vertices that are ADDED when inserting the arcs!
|
|
int num_created_vertices = 0;
|
|
std::vector<QVector3D> created_vertices;
|
|
auto approx = s_traits.approximate_2_object();
|
|
for (auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit)
|
|
{
|
|
auto& d = vit->data();
|
|
if (vit->data().v == false)
|
|
{
|
|
std::cout << "-------------------------------------\n";
|
|
std::cout << vit->point() << std::endl;
|
|
|
|
if (2 == vit->degree())
|
|
;//continue;
|
|
|
|
if (1 == vit->degree())
|
|
{
|
|
auto p = vit->point();
|
|
auto p2 = p.location();
|
|
std::cout << " deg-1 vertex = " << p << std::endl;
|
|
std::cout << " deg-1 vertex: " << std::boolalpha << vit->incident_halfedges()->target()->data().v << std::endl;
|
|
}
|
|
|
|
|
|
num_created_vertices++;
|
|
auto p = vit->point();
|
|
auto ap = approx(p);
|
|
QVector3D new_vertex(ap.dx(), ap.dy(), ap.dz());
|
|
new_vertex.normalize();
|
|
std::cout << new_vertex << std::endl;
|
|
std::cout << "degree = " << vit->degree() << std::endl;
|
|
|
|
created_vertices.push_back(new_vertex);
|
|
|
|
// find the arcs that are adjacent to the vertex of degree 4
|
|
if (4 == vit->degree())
|
|
{
|
|
std::cout << "**************************\n DEGREE 4 VERTEX: \n";
|
|
const auto first = vit->incident_halfedges();
|
|
auto curr = first;
|
|
do {
|
|
auto tvh = curr->twin()->target();
|
|
//std::cout << std::boolalpha << svh->data().v << " - " << tvh->data().v << std::endl;
|
|
auto it = vertex_node_map.find(tvh);
|
|
if (it != vertex_node_map.end())
|
|
std::cout << std::setprecision(16) << it->second << std::endl;
|
|
else
|
|
std::cout << "NOT FOUND!!\n";
|
|
} while (++curr != first);
|
|
}
|
|
|
|
std::cout << "\n";
|
|
}
|
|
}
|
|
Kml::Node n{ 180.0, -84.71338 };
|
|
std::cout << "Node itself = " << n.get_coords_3d() << std::endl;
|
|
std::cout << "*** num created vertices = " << num_created_vertices << std::endl;
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num nodes = " << num_counted_nodes << std::endl;
|
|
std::cout << "num arr vertices = " << arr.number_of_vertices() << std::endl;
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num counted arcs = " << num_counted_arcs << std::endl;
|
|
std::cout << "num arr edges = " << arr.number_of_edges() << std::endl;
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num polygons = " << num_counted_polygons << std::endl;
|
|
std::cout << "num arr faces = " << arr.number_of_faces() << std::endl;
|
|
|
|
return created_vertices;
|
|
}
|
|
|
|
|
|
std::vector<QVector3D> Aos::ext_check_id_based(Kml::Placemarks& placemarks)
|
|
{
|
|
// Construct the arrangement from 12 geodesic arcs.
|
|
//Geom_traits traits;
|
|
Ext_aos arr(&s_traits);
|
|
|
|
//
|
|
auto nodes = Kml::generate_ids(placemarks);
|
|
//auto nodes = Kml::generate_ids_approx(placemarks, 0.001);
|
|
|
|
//Segment s()
|
|
auto ctr_p = s_traits.construct_point_2_object();
|
|
auto ctr_cv = s_traits.construct_curve_2_object();
|
|
|
|
int num_counted_arcs = 0;
|
|
int num_counted_polygons = 0;
|
|
//
|
|
std::vector<Point> points;
|
|
std::vector<Ext_aos::Vertex_handle> vertices;
|
|
for (const auto& node : nodes)
|
|
{
|
|
auto n = node.get_coords_3d();
|
|
auto p = ctr_p(n.x, n.y, n.z);
|
|
auto v = CGAL::insert_point(arr, p);
|
|
points.push_back(p);
|
|
vertices.push_back(v);
|
|
//arr.insert_at_vertices(Segment(p, p), v, v);
|
|
}
|
|
std::cout << "num nodes = " << nodes.size() << std::endl;
|
|
std::cout << "num points = " << points.size() << std::endl;
|
|
// MARK all vertices as true
|
|
for (auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit)
|
|
{
|
|
vit->set_data(Flag(true));
|
|
}
|
|
|
|
|
|
for (auto& placemark : placemarks)
|
|
{
|
|
for (auto& polygon : placemark.polygons)
|
|
{
|
|
num_counted_polygons++;
|
|
|
|
// TO DO : ADD the outer boundaries!
|
|
auto& ids = polygon.outer_boundary.ids;
|
|
int num_nodes = ids.size();
|
|
for (int i = 0; i < num_nodes - 1; ++i)
|
|
{
|
|
num_counted_arcs++;
|
|
const auto nid1 = ids[i];
|
|
const auto nid2 = ids[i + 1];
|
|
auto p1 = points[nid1];
|
|
auto p2 = points[nid2];
|
|
CGAL::insert(arr, ctr_cv(p1, p2));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num arr vertices (before adding arcs) = " <<
|
|
arr.number_of_vertices() << std::endl;
|
|
|
|
// extract all vertices that are ADDED when inserting the arcs!
|
|
int num_created_vertices = 0;
|
|
std::vector<QVector3D> created_vertices;
|
|
auto approx = s_traits.approximate_2_object();
|
|
for (auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit)
|
|
{
|
|
auto& d = vit->data();
|
|
if (vit->data().v == false)
|
|
{
|
|
std::cout << "-------------------------------------\n";
|
|
std::cout << vit->point() << std::endl;
|
|
|
|
if (2 == vit->degree())
|
|
;//continue;
|
|
|
|
if (1 == vit->degree())
|
|
{
|
|
auto p = vit->point();
|
|
auto p2 = p.location();
|
|
std::cout << "deg-1 vertex = " << p << std::endl;
|
|
std::cout << "deg-1 vertex: " << std::boolalpha << vit->incident_halfedges()->target()->data().v << std::endl;
|
|
}
|
|
|
|
|
|
num_created_vertices++;
|
|
auto p = vit->point();
|
|
auto ap = approx(p);
|
|
QVector3D new_vertex(ap.dx(), ap.dy(), ap.dz());
|
|
new_vertex.normalize();
|
|
std::cout << new_vertex << std::endl;
|
|
std::cout << "degree = " << vit->degree() << std::endl;
|
|
|
|
created_vertices.push_back(new_vertex);
|
|
|
|
//// find the arcs that are adjacent to this vertex
|
|
//const auto first = vit->incident_halfedges();
|
|
//auto curr = first;
|
|
//do {
|
|
|
|
//} while (++curr != first);
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
std::cout << "*** num created vertices = " << num_created_vertices << std::endl;
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num nodes = " << nodes.size() << std::endl;
|
|
std::cout << "num arr vertices = " << arr.number_of_vertices() << std::endl;
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num counted arcs = " << num_counted_arcs << std::endl;
|
|
std::cout << "num arr edges = " << arr.number_of_edges() << std::endl;
|
|
|
|
std::cout << "-------------------------------\n";
|
|
std::cout << "num polygons = " << num_counted_polygons << std::endl;
|
|
std::cout << "num arr faces = " << arr.number_of_faces() << std::endl;
|
|
|
|
return created_vertices;
|
|
}
|
|
|
|
|
|
Aos::Approx_arcs Aos::find_new_faces(Kml::Placemarks& placemarks)
|
|
{
|
|
//Geom_traits traits;
|
|
Ext_aos arr(&s_traits);
|
|
auto ctr_p = s_traits.construct_point_2_object();
|
|
auto ctr_cv = s_traits.construct_curve_2_object();
|
|
|
|
using Face_handle = Ext_aos::Face_handle;
|
|
auto fh = arr.faces_begin();
|
|
fh->data().v = true;
|
|
std::cout << "num faces = " << arr.number_of_faces() << std::endl;
|
|
|
|
auto nodes = Kml::generate_ids(placemarks);
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// define a set of vertex-handles: use this to check if the face is
|
|
// obtained from the polygon definition, or if it is an additional face
|
|
using Vertex_handle = Ext_aos::Vertex_handle;
|
|
std::map<Vertex_handle, int> vertex_id_map;
|
|
std::set<std::set<int>> all_polygon_node_ids;
|
|
|
|
|
|
num_counted_nodes = 0;
|
|
num_counted_arcs = 0;
|
|
num_counted_polygons = 0;
|
|
std::vector<Curve> xcvs;
|
|
for (auto& pm : placemarks)
|
|
{
|
|
std::cout << pm.name << std::endl;
|
|
for (auto& polygon : pm.polygons)
|
|
{
|
|
num_counted_polygons++;
|
|
|
|
// colect all rings into a single list (FOR NOW!!!)
|
|
// TO-DO: PROCESS OUTER & INNER BOUNDARIES SEPARATELY!!!
|
|
auto linear_rings = polygon.get_all_boundaries();
|
|
//Kml::LinearRings linear_rings;
|
|
//linear_rings.push_back(polygon.outer_boundary);
|
|
//for (const auto& inner_boundary : polygon.inner_boundaries)
|
|
// linear_rings.push_back(inner_boundary);
|
|
|
|
// loop on outer and inner boundaries
|
|
//for (auto* lring : linear_rings)
|
|
auto* lring = &polygon.outer_boundary;
|
|
{
|
|
int num_faces_before = arr.number_of_faces();
|
|
std::set<int> polygon_node_ids;
|
|
|
|
// convert the nodes to points on unit-sphere
|
|
std::vector<Approximate_Vector_3> sphere_points;
|
|
//for (const auto& node : lring->nodes)
|
|
//std::cout << " NUM POLYGON-NODES SIZE = " << lring->ids.size() << std::endl;
|
|
for (int i = 0; i < lring->ids.size(); ++i)
|
|
{
|
|
num_counted_nodes++;
|
|
const auto id = lring->ids[i];
|
|
const auto& node = lring->nodes[i];
|
|
const auto p = node.get_coords_3d();
|
|
Approximate_Vector_3 v(p.x, p.y, p.z);
|
|
sphere_points.push_back(v);
|
|
auto vh = CGAL::insert_point(arr, ctr_p(p.x, p.y, p.z));
|
|
polygon_node_ids.insert(id);
|
|
// assert node-id and vertex-handle consistency
|
|
//{
|
|
// auto it = vertex_id_map.find(vh);
|
|
// if (vertex_id_map.cend() != it)
|
|
// {
|
|
// if (id != it->second)
|
|
// std::cout << "*** ERROR!!!\n";
|
|
// }
|
|
//}
|
|
vertex_id_map[vh] = id;
|
|
vh->data().v = true;
|
|
}
|
|
//std::cout << " POLYGON-NODES SET SIZE = " << polygon_node_ids.size() << std::endl;
|
|
if (lring->ids.size() != (1 + polygon_node_ids.size()))
|
|
std::cout << "*** ASSERTION ERROR!!!!\n";
|
|
|
|
all_polygon_node_ids.insert(std::move(polygon_node_ids));
|
|
|
|
// add curves
|
|
int num_points = sphere_points.size();
|
|
for (int i = 0; i < num_points - 1; i++)
|
|
{
|
|
num_counted_arcs++;
|
|
const auto p1 = sphere_points[i];
|
|
const auto p2 = sphere_points[i + 1];
|
|
auto xcv = ctr_cv(ctr_p(p1.x(), p1.y(), p1.z()),
|
|
ctr_p(p2.x(), p2.y(), p2.z()));
|
|
//xcvs.push_back(xcv);
|
|
CGAL::insert(arr, xcv);
|
|
}
|
|
|
|
int num_faces_after = arr.number_of_faces();
|
|
int num_new_faces = num_faces_after - num_faces_before;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// mark all faces as TRUE (= as existing faces)
|
|
int num_found = 0;
|
|
int num_not_found = 0;
|
|
std::vector<Curve> new_face_arcs;
|
|
for (auto fh = arr.faces_begin(); fh != arr.faces_end(); ++fh)
|
|
{
|
|
// skip the spherical face
|
|
std::cout << "num outer_ccbs = " << fh->number_of_outer_ccbs() << std::endl;
|
|
if (fh->number_of_outer_ccbs() == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
// construct the set of all node-ids for the current face
|
|
std::set<int> face_node_ids_set;
|
|
std::vector<int> face_node_ids;
|
|
std::vector<Curve> face_arcs;
|
|
auto first = fh->outer_ccb();
|
|
auto curr = first;
|
|
do {
|
|
auto vh = curr->source();
|
|
// skip if the vertex is due to intersection with the identification curve
|
|
if ((vh->data().v == false) && (vh->degree() == 2))
|
|
continue;
|
|
|
|
auto id = vertex_id_map[vh];
|
|
face_node_ids_set.insert(id);
|
|
|
|
face_arcs.push_back(ctr_cv(curr->source()->point(), curr->target()->point()));
|
|
} while (++curr != first);
|
|
//std::cout << "counted vertices = " << num_vertices << std::endl;
|
|
//std::cout << "vertices in the set = " << polygon_node_ids.size() << std::endl;
|
|
|
|
auto it = all_polygon_node_ids.find(face_node_ids_set);
|
|
if (it == all_polygon_node_ids.cend())
|
|
{
|
|
std::cout << "NOT FOUND!!!\n";
|
|
std::cout << "num nodes = " << face_node_ids_set.size() << std::endl;
|
|
num_not_found++;
|
|
new_face_arcs.insert(new_face_arcs.end(), face_arcs.begin(), face_arcs.end());
|
|
}
|
|
else
|
|
num_found++;
|
|
}
|
|
std::cout << "num not found = " << num_not_found << std::endl;
|
|
|
|
auto approx_arcs = get_approx_curves(new_face_arcs, 0.001);
|
|
return approx_arcs;
|
|
}
|
|
|
|
|
|
void Aos::save_arr(Kml::Placemarks& placemarks, const std::string& file_name)
|
|
{
|
|
#ifndef USE_EPIC
|
|
//Geom_traits traits;
|
|
Ext_aos arr(&s_traits);
|
|
auto ctr_p = s_traits.construct_point_2_object();
|
|
auto ctr_cv = s_traits.construct_curve_2_object();
|
|
|
|
using Face_handle = Ext_aos::Face_handle;
|
|
auto fh = arr.faces_begin();
|
|
fh->data().v = true;
|
|
std::cout << "num faces = " << arr.number_of_faces() << std::endl;
|
|
|
|
auto nodes = Kml::generate_ids(placemarks);
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// define a set of vertex-handles: use this to check if the face is
|
|
// obtained from the polygon definition, or if it is an additional face
|
|
using Vertex_handle = Ext_aos::Vertex_handle;
|
|
using Halfedge_handle = Ext_aos::Halfedge_handle;
|
|
using Face_handle = Ext_aos::Face_handle;
|
|
std::map<Vertex_handle, int> vertex_id_map;
|
|
std::map<std::set<int>, std::string> all_polygon_node_ids_map;
|
|
|
|
// map to associate the created faces with the country names
|
|
// CAUTION: the newly created faces
|
|
|
|
num_counted_nodes = 0;
|
|
num_counted_arcs = 0;
|
|
num_counted_polygons = 0;
|
|
std::vector<Curve> xcvs;
|
|
for (auto& pm : placemarks)
|
|
{
|
|
std::cout << pm.name << std::endl;
|
|
for (auto& polygon : pm.polygons)
|
|
{
|
|
num_counted_polygons++;
|
|
|
|
// colect all rings into a single list (FOR NOW!!!)
|
|
// TO-DO: PROCESS OUTER & INNER BOUNDARIES SEPARATELY!!!
|
|
//auto linear_rings = polygon.get_all_boundaries();
|
|
//Kml::LinearRings linear_rings;
|
|
//linear_rings.push_back(polygon.outer_boundary);
|
|
//for (const auto& inner_boundary : polygon.inner_boundaries)
|
|
// linear_rings.push_back(inner_boundary);
|
|
|
|
// loop on outer and inner boundaries
|
|
//for (auto* lring : linear_rings)
|
|
auto* lring = &polygon.outer_boundary;
|
|
{
|
|
int num_faces_before = arr.number_of_faces();
|
|
std::set<int> polygon_node_ids;
|
|
|
|
// convert the nodes to points on unit-sphere
|
|
std::vector<Approximate_Vector_3> sphere_points;
|
|
//for (const auto& node : lring->nodes)
|
|
//std::cout << " NUM POLYGON-NODES SIZE = " << lring->ids.size() << std::endl;
|
|
for (int i = 0; i < lring->ids.size(); ++i)
|
|
{
|
|
num_counted_nodes++;
|
|
const auto id = lring->ids[i];
|
|
const auto& node = lring->nodes[i];
|
|
const auto p = node.get_coords_3d();
|
|
Approximate_Vector_3 v(p.x, p.y, p.z);
|
|
sphere_points.push_back(v);
|
|
auto vh = CGAL::insert_point(arr, ctr_p(p.x, p.y, p.z));
|
|
polygon_node_ids.insert(id);
|
|
// assert node-id and vertex-handle consistency
|
|
//{
|
|
// auto it = vertex_id_map.find(vh);
|
|
// if (vertex_id_map.cend() != it)
|
|
// {
|
|
// if (id != it->second)
|
|
// std::cout << "*** ERROR!!!\n";
|
|
// }
|
|
//}
|
|
vertex_id_map[vh] = id;
|
|
vh->data().v = true;
|
|
}
|
|
//std::cout << " POLYGON-NODES SET SIZE = " << polygon_node_ids.size() << std::endl;
|
|
if (lring->ids.size() != (1 + polygon_node_ids.size()))
|
|
std::cout << "*** ASSERTION ERROR!!!!\n";
|
|
|
|
all_polygon_node_ids_map.insert(std::make_pair(
|
|
std::move(polygon_node_ids), pm.name));
|
|
|
|
// add curves
|
|
int num_points = sphere_points.size();
|
|
for (int i = 0; i < num_points - 1; i++)
|
|
{
|
|
num_counted_arcs++;
|
|
const auto p1 = sphere_points[i];
|
|
const auto p2 = sphere_points[i + 1];
|
|
auto xcv = ctr_cv(ctr_p(p1.x(), p1.y(), p1.z()),
|
|
ctr_p(p2.x(), p2.y(), p2.z()));
|
|
//xcvs.push_back(xcv);
|
|
CGAL::insert(arr, xcv);
|
|
}
|
|
|
|
int num_faces_after = arr.number_of_faces();
|
|
int num_new_faces = num_faces_after - num_faces_before;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::cout << "*** arr.number_of_faces = " << arr.number_of_faces() << std::endl;
|
|
std::cout << "*** arr.number_of_halfedges = " << arr.number_of_halfedges() << std::endl;
|
|
std::cout << "*** arr.number_of_vertices = " << arr.number_of_vertices() << std::endl;
|
|
|
|
// DEFINE JSON OBJECT
|
|
json js;
|
|
auto& js_points = js["points"] = json::array();
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// POINTS
|
|
// define a map from each vertex to its position in the arrangement
|
|
//auto get_num_denum
|
|
using FT = typename Kernel::FT;
|
|
//using json = nlohmann::ordered_json;
|
|
FT ft(0);
|
|
auto ex = ft.exact();
|
|
CGAL::Rational_traits<decltype(ex)> rt;
|
|
typename CGAL::Algebraic_structure_traits<decltype(ex)>::Simplify simplify;
|
|
|
|
|
|
auto set_num_denum = [&](decltype(ex)& x, json& ratx)
|
|
{
|
|
simplify(x);
|
|
std::stringstream ss_x_num;
|
|
CGAL::IO::set_ascii_mode(ss_x_num);
|
|
ss_x_num << rt.numerator(x);
|
|
std::string xnum;
|
|
ss_x_num >> xnum;
|
|
ratx["num"] = xnum;
|
|
|
|
std::stringstream ss_x_den;
|
|
CGAL::IO::set_ascii_mode(ss_x_den);
|
|
ss_x_den << rt.denominator(x);
|
|
std::string xden;
|
|
ss_x_den >> xden;
|
|
ratx["den"] = xden;
|
|
};
|
|
|
|
|
|
using Point_ = std::decay_t<decltype(arr.vertices_begin()->point())>;
|
|
std::map<void*, int> point_pos_map;
|
|
std::vector<Point_> points;
|
|
std::map<Vertex_handle, int> vertex_pos_map;
|
|
for (auto vh = arr.vertices_begin(); vh != arr.vertices_end(); ++vh)
|
|
{
|
|
// add the vertex if not found in the map
|
|
auto it = vertex_pos_map.find(vh);
|
|
if (it == vertex_pos_map.end())
|
|
{
|
|
int new_vh_pos = vertex_pos_map.size();
|
|
vertex_pos_map[vh] = new_vh_pos;
|
|
|
|
// write the vertex-data to JSON object
|
|
auto& p = vh->point();
|
|
points.push_back(p);
|
|
point_pos_map[&p] = new_vh_pos;
|
|
auto dx = p.dx().exact();
|
|
auto dy = p.dy().exact();
|
|
auto dz = p.dz().exact();
|
|
|
|
json jv;
|
|
jv["location"] = p.location();
|
|
set_num_denum(dx, jv["dx"]);
|
|
set_num_denum(dy, jv["dy"]);
|
|
set_num_denum(dz, jv["dz"]);
|
|
js_points.push_back(std::move(jv));
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// CURVES
|
|
// define a map from each curve to its position in the arrangment
|
|
auto& js_curves = js["curves"] = json::array();
|
|
using Ext_curve = Ext_aos::X_monotone_curve_2;
|
|
std::map<Ext_curve*, int> curve_pos_map;
|
|
int num_edges = 0;
|
|
for (auto eh = arr.edges_begin(); eh != arr.edges_end(); ++eh)
|
|
{
|
|
num_edges++;
|
|
auto& xcv = eh->curve();
|
|
auto it = curve_pos_map.find(&xcv);
|
|
if (it == curve_pos_map.end())
|
|
{
|
|
int new_xcv_pos = curve_pos_map.size();
|
|
curve_pos_map[&xcv] = new_xcv_pos;
|
|
|
|
json je;
|
|
auto& sv = xcv.source();
|
|
auto& tv = xcv.target();
|
|
auto svit = std::find(points.begin(), points.end(), sv);
|
|
auto tvit = std::find(points.begin(), points.end(), tv);
|
|
auto svp = std::distance(points.begin(), svit);
|
|
auto tvp = std::distance(points.begin(), tvit);
|
|
//std::cout << "svp= " << point_pos_map[(void*)&sv] << std::endl;
|
|
je["source"] = svp;
|
|
je["target"] = tvp;
|
|
auto& je_normal = je["normal"];
|
|
|
|
// write the vertex-data to JSON object
|
|
auto& n = xcv.normal();
|
|
auto dx = n.dx().exact();
|
|
auto dy = n.dy().exact();
|
|
auto dz = n.dz().exact();
|
|
set_num_denum(dx, je_normal["dx"]);
|
|
set_num_denum(dy, je_normal["dy"]);
|
|
set_num_denum(dz, je_normal["dz"]);
|
|
|
|
je["is_vertical"] = xcv.is_vertical();
|
|
je["is_directed_right"] = xcv.is_directed_right();
|
|
je["is_full"] = xcv.is_full();
|
|
|
|
js_curves.push_back(std::move(je));
|
|
}
|
|
}
|
|
std::cout << "num edges = " << num_edges << std::endl;
|
|
std::cout << "curve map size = " << curve_pos_map.size() << std::endl;
|
|
std::cout << "js_curves size = " << js_curves.size() << std::endl;
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// VERTICES
|
|
// there is a one-to-one corresponce between vertices and points
|
|
auto& js_vertices = js["vertices"] = json::array();
|
|
for (auto vh = arr.vertices_begin(); vh != arr.vertices_end(); ++vh)
|
|
{
|
|
json js_vertex;
|
|
auto vpos = vertex_pos_map[vh];
|
|
js_vertex["point"] = vpos;
|
|
js_vertices.push_back(std::move(js_vertex));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// HALF-EDGES
|
|
//int num_half_edges = 0;
|
|
//auto& js_halfedges = js["halfedges"] = json::array();
|
|
//std::map<Halfedge_handle, int> halfedge_pos_map;
|
|
//auto write_half_edge = [&](auto& he)
|
|
//{
|
|
// auto it = halfedge_pos_map.find(&he);
|
|
// if (it == halfedge_pos_map.end())
|
|
// {
|
|
// auto new_he_pos = halfedge_pos_map.size();
|
|
// halfedge_pos_map[&he] = new_he_pos;
|
|
// }
|
|
// auto svh = he.source();
|
|
// auto tvh = he.target();
|
|
// auto& xcv = he.curve();
|
|
// auto svp = vertex_pos_map[svh];
|
|
// auto tvp = vertex_pos_map[tvh];
|
|
// auto xcvp = curve_pos_map[&xcv];
|
|
// json js_he;
|
|
// js_he["source"] = svp;
|
|
// js_he["target"] = tvp;
|
|
// js_he["curve"] = xcvp;
|
|
// js_halfedges.push_back(std::move(js_he));
|
|
//};
|
|
//for (auto it = arr.halfedges_begin(); it != arr.halfedges_end(); ++it)
|
|
//{
|
|
// auto& he = *it;
|
|
// write_half_edge(he);
|
|
// num_half_edges++;
|
|
//}
|
|
//std::cout << "HALF-EDGE CHECKS:\n";
|
|
//std::cout << " *** num total half-edges = " << num_half_edges << std::endl;
|
|
//std::cout << " *** halfedge-pos-map size = " << halfedge_pos_map.size() << std::endl;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// EDGES
|
|
num_edges = 0;
|
|
auto& js_edges = js["edges"] = json::array();
|
|
////using Edge_ = decltype(*arr.edges_begin());
|
|
//std::map<void*, int> edge_pos_map;
|
|
std::map<void*, int> halfedge_pos_map;
|
|
//Edge_const_iterator eit;
|
|
for (auto eit = arr.edges_begin(); eit != arr.edges_end(); ++eit)
|
|
{
|
|
auto& edge = *eit;
|
|
//auto it = edge_pos_map.find(&edge);
|
|
//if (it == edge_pos_map.end())
|
|
//{
|
|
// auto new_edge_pos = edge_pos_map.size();
|
|
// edge_pos_map[&edge] = new_edge_pos;
|
|
//}
|
|
auto& xcv = edge.curve();
|
|
auto xcvp = curve_pos_map[&xcv];
|
|
json js_edge;
|
|
js_edge["curve"] = xcvp;
|
|
js_edge["direction"] = edge.direction();
|
|
js_edge["source"] = vertex_pos_map[edge.source()];
|
|
js_edge["target"] = vertex_pos_map[edge.target()];
|
|
js_edges.push_back(std::move(js_edge));
|
|
num_edges++;
|
|
|
|
// add the halfedge indices to the map
|
|
int new_halfedge_index = halfedge_pos_map.size();
|
|
auto& twin = *edge.twin();
|
|
halfedge_pos_map[&edge] = new_halfedge_index;
|
|
halfedge_pos_map[&twin] = new_halfedge_index + 1;
|
|
}
|
|
std::cout << "EDGE CHECKS:\n";
|
|
std::cout << " *** num edges = " << num_edges << std::endl;
|
|
std::cout << " *** js_edges size = " << js_edges.size() << std::endl;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// FACES
|
|
// CONDITION DATA: Caspian Sea needs to be defined
|
|
//num_half_edges = 0;
|
|
num_edges = 0;
|
|
int num_found = 0;
|
|
int num_not_found = 0;
|
|
std::map<Face_handle, std::string> face_name_map;
|
|
for (auto fh = arr.faces_begin(); fh != arr.faces_end(); ++fh)
|
|
{
|
|
// skip the spherical face
|
|
std::cout << "num outer_ccbs = " << fh->number_of_outer_ccbs() << std::endl;
|
|
if (fh->number_of_outer_ccbs() == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// construct the set of all node-ids for the current face
|
|
std::set<int> face_node_ids_set;
|
|
std::vector<int> face_node_ids;
|
|
auto first = fh->outer_ccb();
|
|
auto curr = first;
|
|
do {
|
|
//num_half_edges++;
|
|
auto vh = curr->source();
|
|
// skip if the vertex is due to intersection with the identification curve
|
|
if ((vh->data().v == false) && (vh->degree() == 2))
|
|
continue;
|
|
|
|
auto id = vertex_id_map[vh];
|
|
face_node_ids_set.insert(id);
|
|
|
|
//face_arcs.push_back(ctr_cv(curr->source()->point(), curr->target()->point()));
|
|
auto& xcv = curr->curve();
|
|
} while (++curr != first);
|
|
//std::cout << "counted vertices = " << num_vertices << std::endl;
|
|
//std::cout << "vertices in the set = " << polygon_node_ids.size() << std::endl;
|
|
|
|
std::string name;
|
|
auto it = all_polygon_node_ids_map.find(face_node_ids_set);
|
|
if (it == all_polygon_node_ids_map.cend())
|
|
{
|
|
std::cout << "NOT FOUND!!!\n";
|
|
std::cout << "num nodes = " << face_node_ids_set.size() << std::endl;
|
|
num_not_found++;
|
|
name = "Caspian Sea";
|
|
}
|
|
else
|
|
{
|
|
num_found++;
|
|
name = it->second;
|
|
}
|
|
face_name_map[fh] = name;
|
|
}
|
|
std::cout << "num not found = " << num_not_found << std::endl;
|
|
|
|
|
|
// RECORD FACES
|
|
json& js_faces = js["faces"] = json::array();
|
|
auto get_ccb_json = [&](Ext_aos::Ccb_halfedge_circulator first)
|
|
{
|
|
json js_edges;
|
|
auto& ccb_edge_indices = js_edges["halfedges"] = json::array();
|
|
auto curr = first;
|
|
do {
|
|
auto& he = *curr;
|
|
//auto& xcv = he.curve();
|
|
//auto it = curve_pos_map.find(&xcv);
|
|
//if (it == curve_pos_map.end())
|
|
//{
|
|
// std::cout << "ASSERTION ERROR!!!" << std::endl;
|
|
//}
|
|
auto it = halfedge_pos_map.find(&he);
|
|
if (it == halfedge_pos_map.end())
|
|
{
|
|
std::cout << "ASSERTION ERROR!!!" << std::endl;
|
|
}
|
|
|
|
auto edge_pos = it->second;
|
|
ccb_edge_indices.push_back(edge_pos);
|
|
} while (++curr != first);
|
|
|
|
return js_edges;
|
|
};
|
|
|
|
int total_num_half_edges = 0;
|
|
for (auto fh = arr.faces_begin(); fh != arr.faces_end(); ++fh)
|
|
{
|
|
//// skip the spherical face
|
|
//if (fh->number_of_outer_ccbs() == 0)
|
|
// continue;
|
|
|
|
// json object for the current face
|
|
json js_face;
|
|
auto face_name = face_name_map[fh];
|
|
js_face["name"] = face_name;
|
|
js_face["is_unbounded"] = false;
|
|
js_face["is_valid"] = true;
|
|
|
|
// at this point we are sure that we have at least 1 outer-ccb
|
|
auto& js_outer_ccbs = js_face["outer_ccbs"] = json::array();
|
|
for (auto ccb = fh->outer_ccbs_begin(); ccb != fh->outer_ccbs_end(); ++ccb)
|
|
{
|
|
auto js_ccb = get_ccb_json(*ccb);
|
|
js_outer_ccbs.push_back(std::move(js_ccb));
|
|
}
|
|
|
|
// INNER CCBS
|
|
if (fh->number_of_inner_ccbs() > 0)
|
|
{
|
|
auto& js_inner_ccbs = js_face["inner_ccbs"] = json::array();
|
|
for (auto ccb = fh->inner_ccbs_begin(); ccb != fh->inner_ccbs_end(); ++ccb)
|
|
{
|
|
auto js_ccb = get_ccb_json(*ccb);
|
|
js_inner_ccbs.push_back(std::move(js_ccb));
|
|
}
|
|
}
|
|
|
|
js_faces.push_back(std::move(js_face));
|
|
}
|
|
std::cout << "total num half-edges = " << total_num_half_edges << std::endl;
|
|
|
|
// save the arrangment
|
|
std::ofstream ofile(file_name);
|
|
ofile << js.dump(2);
|
|
ofile.close();
|
|
#endif
|
|
}
|
|
|
|
|
|
namespace
|
|
{
|
|
enum Error_id {
|
|
FILE_NOT_FOUND,
|
|
ILLEGAL_EXTENSION,
|
|
UNABLE_TO_OPEN,
|
|
FILE_IS_EMPTY,
|
|
INVALID_INITIAL_POLYGON,
|
|
UNSUPPORTED,
|
|
INVALID_OUTPUT_FILE,
|
|
ILLEGAL_SOLUTION_FILE
|
|
};
|
|
|
|
struct Illegal_input : public std::logic_error {
|
|
Illegal_input(Error_id /* err */, const std::string& msg,
|
|
const std::string& filename) :
|
|
std::logic_error(std::string(msg).append(" (").append(filename).
|
|
append(")!"))
|
|
{}
|
|
|
|
Illegal_input(Error_id /* err */, const std::string& msg) :
|
|
std::logic_error(std::string(msg).append("!"))
|
|
{}
|
|
};
|
|
|
|
struct Input_file_missing_error : public std::logic_error {
|
|
Input_file_missing_error(std::string& str) : std::logic_error(str) {}
|
|
};
|
|
|
|
//
|
|
struct country {
|
|
//! Constructor
|
|
country(std::string& name) :
|
|
m_name(std::move(name))
|
|
{}
|
|
|
|
std::string m_name;
|
|
};
|
|
|
|
/*! Read a json file.
|
|
*/
|
|
bool read_json(const std::string& filename, nlohmann::json& data) {
|
|
using json = nlohmann::json;
|
|
std::ifstream infile(filename);
|
|
if (!infile.is_open()) {
|
|
throw Illegal_input(UNABLE_TO_OPEN, "Cannot open file", filename);
|
|
return false;
|
|
}
|
|
data = json::parse(infile);
|
|
infile.close();
|
|
if (data.empty()) {
|
|
throw Illegal_input(FILE_IS_EMPTY, "File is empty", filename);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename FT>
|
|
FT to_ft(const nlohmann::json& js_ft) {
|
|
using Exact_type = typename FT::Exact_type;
|
|
const std::string& js_num = js_ft["num"];
|
|
const std::string& js_den = js_ft["den"];
|
|
std::string str = js_num + "/" + js_den;
|
|
Exact_type eft(str);
|
|
return FT(eft);
|
|
}
|
|
|
|
template <typename Arrangement_, typename Kernel_>
|
|
bool read_arrangement(const std::string& filename, Arrangement_& arr,
|
|
const Kernel_& kernel) {
|
|
using Arrangement = Arrangement_;
|
|
using Kernel = Kernel_;
|
|
|
|
using json = nlohmann::json;
|
|
json data;
|
|
auto rc = read_json(filename, data);
|
|
if (!rc) return false;
|
|
|
|
// points
|
|
auto it = data.find("points");
|
|
if (it == data.end()) {
|
|
std::cerr << "The points item is missing " << " (" << filename << ")\n";
|
|
return false;
|
|
}
|
|
const auto& js_points = it.value();
|
|
|
|
// curves
|
|
it = data.find("curves");
|
|
if (it == data.end()) {
|
|
std::cerr << "The curves item is missing " << " (" << filename << ")\n";
|
|
return false;
|
|
}
|
|
const auto& js_curves = it.value();
|
|
|
|
// vertices
|
|
it = data.find("vertices");
|
|
if (it == data.end()) {
|
|
std::cerr << "The vertices item is missing " << " (" << filename << ")\n";
|
|
return false;
|
|
}
|
|
const auto& js_vertices = it.value();
|
|
|
|
// edges
|
|
it = data.find("edges");
|
|
if (it == data.end()) {
|
|
std::cerr << "The edges item is missing " << " (" << filename << ")\n";
|
|
return false;
|
|
}
|
|
const auto& js_edges = it.value();
|
|
|
|
// faces
|
|
it = data.find("faces");
|
|
if (it == data.end()) {
|
|
std::cerr << "The faces item is missing " << " (" << filename << ")\n";
|
|
return false;
|
|
}
|
|
const auto& js_faces = it.value();
|
|
|
|
const std::size_t num_points = js_points.size();
|
|
const std::size_t num_curves = js_curves.size();
|
|
const std::size_t num_vertices = js_vertices.size();
|
|
const std::size_t num_edges = js_edges.size();
|
|
const std::size_t num_faces = js_faces.size();
|
|
const std::size_t num_halfedges = num_edges * 2;
|
|
|
|
if (num_points < num_vertices) {
|
|
std::cerr << "The no. of points (" << num_points
|
|
<< ") is smaller than the no. of vertices (" << num_vertices
|
|
<< ")\n";
|
|
return false;
|
|
}
|
|
|
|
if (num_curves < num_edges) {
|
|
std::cerr << "The no. of curves (" << num_curves
|
|
<< ") is smaller than the no. of edge (" << num_edges << ")\n";
|
|
return false;
|
|
}
|
|
|
|
std::cout << "# points: " << num_points << std::endl;
|
|
std::cout << "# curves: " << num_curves << std::endl;
|
|
std::cout << "# vertices: " << num_vertices << std::endl;
|
|
std::cout << "# halfedges: " << num_halfedges << std::endl;
|
|
std::cout << "# faces: " << num_faces << std::endl;
|
|
|
|
using Point = typename Arrangement::Point_2;
|
|
using X_monotone_curve = typename Arrangement::X_monotone_curve_2;
|
|
using FT = typename Kernel::FT;
|
|
using Exact_type = typename FT::Exact_type;
|
|
|
|
std::vector<Point> points;
|
|
points.reserve(num_points);
|
|
for (const auto& js_pnt : js_points) {
|
|
using Direction_3 = typename Kernel::Direction_3;
|
|
using Location = typename Point::Location_type;
|
|
auto location = static_cast<Location>(js_pnt["location"]);
|
|
auto dx = to_ft<FT>(js_pnt["dx"]);
|
|
auto dy = to_ft<FT>(js_pnt["dy"]);
|
|
auto dz = to_ft<FT>(js_pnt["dz"]);
|
|
Direction_3 dir(dx, dy, dz);
|
|
Point pnt(dir, location);
|
|
points.push_back(pnt);
|
|
}
|
|
|
|
std::vector<X_monotone_curve> xcurves;
|
|
xcurves.reserve(num_curves);
|
|
for (const auto& js_xcv : js_curves) {
|
|
using Direction_3 = typename Kernel::Direction_3;
|
|
std::size_t src_id = js_xcv["source"];
|
|
std::size_t trg_id = js_xcv["target"];
|
|
const auto& js_normal = js_xcv["normal"];
|
|
auto dx = to_ft<FT>(js_normal["dx"]);
|
|
auto dy = to_ft<FT>(js_normal["dy"]);
|
|
auto dz = to_ft<FT>(js_normal["dz"]);
|
|
Direction_3 normal(dx, dy, dz);
|
|
bool is_vert = js_xcv["is_vertical"];
|
|
bool is_directed_right = js_xcv["is_directed_right"];
|
|
bool is_full = js_xcv["is_full"];
|
|
const auto& src = points[src_id];
|
|
const auto& trg = points[trg_id];
|
|
X_monotone_curve xcv(src, trg, normal, is_vert, is_directed_right, is_full);
|
|
xcurves.push_back(xcv);
|
|
}
|
|
|
|
using Arr_accessor = CGAL::Arr_accessor<Arrangement>;
|
|
Arr_accessor arr_access(arr);
|
|
arr_access.clear_all();
|
|
|
|
// Vertices
|
|
using DVertex = typename Arr_accessor::Dcel_vertex;
|
|
std::vector<DVertex*> vertices(num_vertices);
|
|
size_t k = 0;
|
|
for (const auto& js_vertex : js_vertices) {
|
|
std::size_t point_id = js_vertex["point"];
|
|
const auto& point = points[point_id];
|
|
CGAL::Arr_parameter_space ps_x, ps_y;
|
|
switch (point.location()) {
|
|
case Point::NO_BOUNDARY_LOC: ps_x = ps_y = CGAL::INTERIOR; break;
|
|
case Point::MIN_BOUNDARY_LOC:
|
|
ps_x = CGAL::INTERIOR;
|
|
ps_y = CGAL::ARR_BOTTOM_BOUNDARY;
|
|
break;
|
|
case Point::MID_BOUNDARY_LOC:
|
|
ps_x = CGAL::LEFT_BOUNDARY;
|
|
ps_y = CGAL::INTERIOR;
|
|
break;
|
|
case Point::MAX_BOUNDARY_LOC:
|
|
ps_x = CGAL::INTERIOR;
|
|
ps_y = CGAL::ARR_TOP_BOUNDARY;
|
|
break;
|
|
}
|
|
vertices[k++] = arr_access.new_vertex(&point, ps_x, ps_y);
|
|
}
|
|
|
|
// Halfedges
|
|
using DHalfedge = typename Arr_accessor::Dcel_halfedge;
|
|
std::vector<DHalfedge*> halfedges(num_halfedges);
|
|
k = 0;
|
|
for (const auto& js_edge : js_edges) {
|
|
std::size_t source_id = js_edge["source"];
|
|
std::size_t target_id = js_edge["target"];
|
|
std::size_t curve_id = js_edge["curve"];
|
|
int direction = js_edge["direction"];
|
|
DVertex* src_v = vertices[source_id];
|
|
DVertex* trg_v = vertices[target_id];
|
|
const auto& curve = xcurves[curve_id];
|
|
DHalfedge* new_he = arr_access.new_edge(&curve);
|
|
trg_v->set_halfedge(new_he);
|
|
new_he->set_vertex(trg_v);
|
|
src_v->set_halfedge(new_he->opposite());
|
|
new_he->opposite()->set_vertex(src_v);
|
|
new_he->set_direction(static_cast<CGAL::Arr_halfedge_direction>(direction));
|
|
halfedges[k++] = new_he;
|
|
halfedges[k++] = new_he->opposite();
|
|
}
|
|
|
|
// Faces
|
|
using DFace = typename Arr_accessor::Dcel_face;
|
|
using DOuter_ccb = typename Arr_accessor::Dcel_outer_ccb;
|
|
using DInner_ccb = typename Arr_accessor::Dcel_inner_ccb;
|
|
using DIso_vert = typename Arr_accessor::Dcel_isolated_vertex;
|
|
const bool is_unbounded(false);
|
|
const bool is_valid(true);
|
|
for (const auto& js_face : js_faces) {
|
|
DFace* new_f = arr_access.new_face();
|
|
|
|
new_f->set_unbounded(is_unbounded);
|
|
new_f->set_fictitious(!is_valid);
|
|
new_f->set_data(js_face["name"]);
|
|
// Read the outer CCBs of the face.
|
|
auto oit = js_face.find("outer_ccbs");
|
|
if (oit != js_face.end()) {
|
|
const auto& js_outer_ccbs = *oit;
|
|
for (const auto& js_ccb : js_outer_ccbs) {
|
|
// Allocate a new outer CCB record and set its incident face.
|
|
auto* new_occb = arr_access.new_outer_ccb();
|
|
new_occb->set_face(new_f);
|
|
|
|
// Read the current outer CCB.
|
|
auto bit = js_ccb.find("halfedges");
|
|
if (bit == js_ccb.end()) {
|
|
std::cerr << "The halfedges item is missing " << " (" << filename
|
|
<< ")\n";
|
|
return false;
|
|
}
|
|
|
|
const auto& js_halfedges = *bit;
|
|
auto hit = js_halfedges.begin();
|
|
std::size_t first_idx = *hit;
|
|
DHalfedge* first_he = halfedges[first_idx];
|
|
first_he->set_outer_ccb(new_occb);
|
|
DHalfedge* prev_he = first_he;
|
|
for (++hit; hit != js_halfedges.end(); ++hit) {
|
|
std::size_t curr_idx = *hit;
|
|
auto curr_he = halfedges[curr_idx];
|
|
prev_he->set_next(curr_he); // connect
|
|
curr_he->set_outer_ccb(new_occb); // set the CCB
|
|
prev_he = curr_he;
|
|
}
|
|
prev_he->set_next(first_he); // close the loop
|
|
new_f->add_outer_ccb(new_occb, first_he);
|
|
}
|
|
}
|
|
|
|
// Read the inner CCBs of the face.
|
|
auto iit = js_face.find("inner_ccbs");
|
|
if (iit != js_face.end()) {
|
|
const auto& js_inner_ccbs = *iit;
|
|
for (const auto& js_ccb : js_inner_ccbs) {
|
|
// Allocate a new inner CCB record and set its incident face.
|
|
auto* new_iccb = arr_access.new_inner_ccb();
|
|
new_iccb->set_face(new_f);
|
|
|
|
// Read the current inner CCB.
|
|
auto bit = js_ccb.find("halfedges");
|
|
if (bit == js_ccb.end()) {
|
|
std::cerr << "The halfedges item is missing " << " (" << filename
|
|
<< ")\n";
|
|
return false;
|
|
}
|
|
|
|
const auto& js_halfedges = *bit;
|
|
auto hit = js_halfedges.begin();
|
|
std::size_t first_idx = *hit;
|
|
DHalfedge* first_he = halfedges[first_idx];
|
|
first_he->set_inner_ccb(new_iccb);
|
|
DHalfedge* prev_he = first_he;
|
|
for (++hit; hit != js_halfedges.end(); ++hit) {
|
|
std::size_t curr_idx = *hit;
|
|
auto curr_he = halfedges[curr_idx];
|
|
prev_he->set_next(curr_he); // connect
|
|
curr_he->set_inner_ccb(new_iccb); // set the CCB
|
|
prev_he = curr_he;
|
|
}
|
|
prev_he->set_next(first_he); // close the loop
|
|
new_f->add_inner_ccb(new_iccb, first_he);
|
|
}
|
|
}
|
|
|
|
// // Read the isolated vertices inside the face.
|
|
// Size n_isolated_vertices =
|
|
// formatter.read_size("number_of_isolated_vertices");
|
|
// if (n_isolated_vertices) {
|
|
// formatter.read_isolated_vertices_begin();
|
|
// Size k;
|
|
// for (k = 0; k < n_isolated_vertices; k++) {
|
|
// // Allocate a new isolated vertex record and set its incident face.
|
|
// DIso_vert* new_iso_vert = arr_access.new_isolated_vertex();
|
|
// new_iso_vert->set_face(new_f);
|
|
// // Read the current isolated vertex.
|
|
// std::size_t v_idx = formatter.read_vertex_index();
|
|
// DVertex* iso_v = m_vertices[v_idx];
|
|
// iso_v->set_isolated_vertex(new_iso_vert);
|
|
// new_f->add_isolated_vertex(new_iso_vert, iso_v);
|
|
// }
|
|
// }
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
template<typename T>
|
|
Aos::Arr_handle get_handle(T* arr)
|
|
{
|
|
return Aos::Arr_handle(arr, [](void* ptr)
|
|
{
|
|
std::cout << "*** DELETING THE ARRANGEMENT WITH SHARED_PTR DELETER!!\n";
|
|
delete reinterpret_cast<T*>(ptr);
|
|
});
|
|
}
|
|
}
|
|
|
|
Aos::Arr_handle Aos::load_arr(const std::string& file_name)
|
|
{
|
|
auto* arr = new Countries_arr(&s_traits);
|
|
auto arrh = get_handle(arr);
|
|
|
|
Kernel kernel;
|
|
auto rc = read_arrangement(file_name, *arr, kernel);
|
|
if (!rc) {
|
|
std::cerr << "Failed to load database!\n";
|
|
return nullptr;
|
|
}
|
|
|
|
return arrh;
|
|
}
|
|
|
|
|
|
Aos::Arr_handle Aos::construct(Kml::Placemarks& placemarks)
|
|
{
|
|
auto* arr = new Arrangement(&s_traits);
|
|
auto arrh = get_handle(arr);
|
|
|
|
auto xcvs = get_arcs(placemarks, *arr);
|
|
for (auto& xcv : xcvs)
|
|
CGAL::insert(*arr, xcv);
|
|
|
|
return arrh;
|
|
}
|
|
|
|
|
|
std::vector<QVector3D> Aos::get_triangles(Arr_handle arrh)
|
|
{
|
|
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
|
//using K = CGAL::Projection_traits_3<K_epic>;
|
|
using Vb = CGAL::Triangulation_vertex_base_2<K>;
|
|
using Fb = CGAL::Constrained_triangulation_face_base_2<K>;
|
|
using TDS = CGAL::Triangulation_data_structure_2<Vb, Fb>;
|
|
using Itag = CGAL::Exact_predicates_tag;
|
|
using CDT = CGAL::Constrained_Delaunay_triangulation_2<K, TDS, Itag>;
|
|
using Face_handle = CDT::Face_handle;
|
|
using Point = CDT::Point;
|
|
using Polygon_2 = CGAL::Polygon_2<K>;
|
|
|
|
auto& arr = *reinterpret_cast<Arrangement*>(arrh.get());
|
|
|
|
//Geom_traits traits;
|
|
auto approx = s_traits.approximate_2_object();
|
|
|
|
|
|
std::vector<std::vector<QVector3D>> all_faces;
|
|
// loop on all faces of the arrangement
|
|
for (auto fit = arr.faces_begin(); fit != arr.faces_end(); ++fit)
|
|
{
|
|
// skip any face with no OUTER-CCB
|
|
if (0 == fit->number_of_outer_ccbs())
|
|
continue;
|
|
|
|
// COMPUTE THE CENTROID OF ALL FACE-POINTS
|
|
std::vector<QVector3D> face_points;
|
|
|
|
// loop on the egdes of the current outer-ccb
|
|
auto first = fit->outer_ccb();
|
|
auto curr = first;
|
|
do {
|
|
auto ap = approx(curr->source()->point());
|
|
QVector3D p(ap.dx(), ap.dy(), ap.dz());
|
|
p.normalize();
|
|
face_points.push_back(p);
|
|
} while (++curr != first);
|
|
|
|
all_faces.push_back(std::move(face_points));
|
|
}
|
|
|
|
// RESULTING TRIANGLE POINTS (every 3 point => triangle)
|
|
std::vector<QVector3D> triangles;
|
|
|
|
std::cout << "triangulating individual faces\n";
|
|
|
|
// loop on all approximated faces
|
|
for (auto& face_points : all_faces)
|
|
{
|
|
//std::cout << "num face points = " << face_points.size() << std::endl;
|
|
// no need to triangulate if the number of points is 3
|
|
if (face_points.size() == 3)
|
|
{
|
|
triangles.insert(triangles.end(), face_points.begin(), face_points.end());
|
|
continue;
|
|
}
|
|
|
|
|
|
// find the centroid of all face-points
|
|
QVector3D centroid(0, 0, 0);
|
|
for (const auto& fp : face_points)
|
|
centroid += fp;
|
|
centroid /= face_points.size();
|
|
centroid.normalize();
|
|
auto normal = centroid;
|
|
|
|
K::Point_3 plane_origin(centroid.x(), centroid.y(), centroid.z());
|
|
K::Vector_3 plane_normal(normal.x(), normal.y(), normal.z());
|
|
K::Plane_3 plane(plane_origin, plane_normal);
|
|
|
|
Polygon_2 polygon;
|
|
|
|
// project all points onto the plane
|
|
K::Point_3 origin(0, 0, 0);
|
|
for (const auto& fp : face_points)
|
|
{
|
|
// define a ray through the origin and the current point
|
|
K::Point_3 current_point(fp.x(), fp.y(), fp.z());
|
|
K::Ray_3 ray(origin, current_point);
|
|
|
|
auto intersection = CGAL::intersection(plane, ray);
|
|
if (!intersection.has_value())
|
|
std::cout << "INTERSECTION ASSERTION ERROR!!!\n";
|
|
auto ip = boost::get<K::Point_3>(intersection.value());
|
|
auto ip2 = plane.to_2d(ip);
|
|
|
|
// add this to the polygon constraint
|
|
polygon.push_back(ip2);
|
|
}
|
|
|
|
CDT cdt;
|
|
cdt.insert_constraint(polygon.vertices_begin(), polygon.vertices_end(), true);
|
|
|
|
std::unordered_map<Face_handle, bool> in_domain_map;
|
|
boost::associative_property_map< std::unordered_map<Face_handle, bool> >
|
|
in_domain(in_domain_map);
|
|
|
|
//Mark facets that are inside the domain bounded by the polygon
|
|
CGAL::mark_domain_in_triangulation(cdt, in_domain);
|
|
|
|
// loop on all the triangles ("faces" in triangulation doc)
|
|
for (Face_handle f : cdt.finite_face_handles())
|
|
{
|
|
// if the current triangles is not inside the polygon -> skip it
|
|
if (false == get(in_domain, f))
|
|
continue;
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
auto tp = f->vertex(i)->point();
|
|
auto tp3 = plane.to_3d(tp);
|
|
QVector3D p3(tp3.x(), tp3.y(), tp3.z());
|
|
p3.normalize();
|
|
triangles.push_back(p3);
|
|
}
|
|
}
|
|
}
|
|
|
|
return triangles;
|
|
}
|
|
|
|
|
|
Aos::Country_triangles_map Aos::get_triangles_by_country(Arr_handle arrh)
|
|
{
|
|
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
|
//using K = CGAL::Projection_traits_3<K_epic>;
|
|
using Vb = CGAL::Triangulation_vertex_base_2<K>;
|
|
using Fb = CGAL::Constrained_triangulation_face_base_2<K>;
|
|
using TDS = CGAL::Triangulation_data_structure_2<Vb, Fb>;
|
|
using Itag = CGAL::Exact_predicates_tag;
|
|
using CDT = CGAL::Constrained_Delaunay_triangulation_2<K, TDS, Itag>;
|
|
using Face_handle = CDT::Face_handle;
|
|
using Point = CDT::Point;
|
|
using Polygon_2 = CGAL::Polygon_2<K>;
|
|
|
|
auto& arr = *reinterpret_cast<Countries_arr*>(arrh.get());
|
|
auto approx = s_traits.approximate_2_object();
|
|
|
|
// group the faces by their country name
|
|
using Face_ = Countries_arr::Face_handle::value_type;
|
|
std::map<std::string, std::vector<Face_*>> country_faces_map;
|
|
for (auto fit = arr.faces_begin(); fit != arr.faces_end(); ++fit)
|
|
{
|
|
auto& face = *fit;
|
|
const auto& country_name = fit->data();
|
|
// skipping spherical-face
|
|
if (country_name.empty())
|
|
continue;
|
|
country_faces_map[country_name].push_back(&face);
|
|
}
|
|
|
|
std::cout << "triangulating individual faces\n";
|
|
Country_triangles_map result;
|
|
for (auto& [country_name, faces] : country_faces_map)
|
|
{
|
|
// CONVERT the face-points to QVector3D
|
|
using Face_points = std::vector<QVector3D>;
|
|
using Faces_ = std::vector<Face_points>;
|
|
Faces_ all_faces_of_current_country;
|
|
for (auto* face : faces)
|
|
{
|
|
// skip any face with no OUTER-CCB
|
|
if (0 == face->number_of_outer_ccbs())
|
|
continue;
|
|
|
|
std::vector<QVector3D> face_points;
|
|
// loop on the egdes of the current outer-ccb
|
|
auto first = face->outer_ccb();
|
|
auto curr = first;
|
|
do {
|
|
auto ap = approx(curr->source()->point());
|
|
QVector3D p(ap.dx(), ap.dy(), ap.dz());
|
|
p.normalize();
|
|
face_points.push_back(p);
|
|
} while (++curr != first);
|
|
|
|
all_faces_of_current_country.push_back(std::move(face_points));
|
|
}
|
|
|
|
// RESULTING TRIANGLE POINTS (every 3 point => triangle)
|
|
auto& triangles = result[country_name];
|
|
// loop on all approximated faces
|
|
for (auto& face_points : all_faces_of_current_country)
|
|
{
|
|
//std::cout << "num face points = " << face_points.size() << std::endl;
|
|
// no need to triangulate if the number of points is 3
|
|
if (face_points.size() == 3)
|
|
{
|
|
triangles.insert(triangles.end(), face_points.begin(), face_points.end());
|
|
continue;
|
|
}
|
|
|
|
// find the centroid of all face-points
|
|
QVector3D centroid(0, 0, 0);
|
|
for (const auto& fp : face_points)
|
|
centroid += fp;
|
|
centroid /= face_points.size();
|
|
centroid.normalize();
|
|
auto normal = centroid;
|
|
|
|
K::Point_3 plane_origin(centroid.x(), centroid.y(), centroid.z());
|
|
K::Vector_3 plane_normal(normal.x(), normal.y(), normal.z());
|
|
K::Plane_3 plane(plane_origin, plane_normal);
|
|
|
|
Polygon_2 polygon;
|
|
|
|
// project all points onto the plane
|
|
K::Point_3 origin(0, 0, 0);
|
|
for (const auto& fp : face_points)
|
|
{
|
|
// define a ray through the origin and the current point
|
|
K::Point_3 current_point(fp.x(), fp.y(), fp.z());
|
|
K::Ray_3 ray(origin, current_point);
|
|
|
|
auto intersection = CGAL::intersection(plane, ray);
|
|
if (!intersection.has_value())
|
|
std::cout << "INTERSECTION ASSERTION ERROR!!!\n";
|
|
auto ip = boost::get<K::Point_3>(intersection.value());
|
|
auto ip2 = plane.to_2d(ip);
|
|
|
|
// add this to the polygon constraint
|
|
polygon.push_back(ip2);
|
|
}
|
|
|
|
CDT cdt;
|
|
cdt.insert_constraint(polygon.vertices_begin(), polygon.vertices_end(), true);
|
|
|
|
std::unordered_map<Face_handle, bool> in_domain_map;
|
|
boost::associative_property_map< std::unordered_map<Face_handle, bool> >
|
|
in_domain(in_domain_map);
|
|
|
|
//Mark facets that are inside the domain bounded by the polygon
|
|
CGAL::mark_domain_in_triangulation(cdt, in_domain);
|
|
|
|
// loop on all the triangles ("faces" in triangulation doc)
|
|
for (Face_handle f : cdt.finite_face_handles())
|
|
{
|
|
// if the current triangles is not inside the polygon -> skip it
|
|
if (false == get(in_domain, f))
|
|
continue;
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
auto tp = f->vertex(i)->point();
|
|
auto tp3 = plane.to_3d(tp);
|
|
QVector3D p3(tp3.x(), tp3.y(), tp3.z());
|
|
p3.normalize();
|
|
triangles.push_back(p3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
Aos::Country_color_map Aos::get_color_mapping(Arr_handle arrh)
|
|
{
|
|
auto& arr = *reinterpret_cast<Countries_arr*>(arrh.get());
|
|
|
|
// group the faces by their country name,
|
|
std::vector<std::string> all_countries;
|
|
using Face_ = Countries_arr::Face_handle::value_type;
|
|
std::map<std::string, std::vector<Face_*>> country_faces_map;
|
|
for (auto fit = arr.faces_begin(); fit != arr.faces_end(); ++fit)
|
|
{
|
|
auto& face = *fit;
|
|
const auto& country_name = fit->data();
|
|
// skipping spherical-face
|
|
if (country_name.empty())
|
|
continue;
|
|
country_faces_map[country_name].push_back(&face);
|
|
all_countries.push_back(country_name);
|
|
}
|
|
|
|
// prepare a map of neighboring countries
|
|
std::map<std::string, std::set<std::string>> country_neighbors_map;
|
|
for (auto& [country_name, faces] : country_faces_map)
|
|
{
|
|
// loop on all of the faces of the current country
|
|
for (auto* face : faces)
|
|
{
|
|
auto first = face->outer_ccb();
|
|
auto curr = first;
|
|
do {
|
|
const auto& neighbor_country_name = curr->twin()->face()->data();
|
|
|
|
// skip the spherical face
|
|
if (neighbor_country_name.empty())
|
|
continue;
|
|
|
|
country_neighbors_map[country_name].insert(neighbor_country_name);
|
|
} while (++curr != first);
|
|
}
|
|
}
|
|
|
|
// find a color index for each country by looking at its neighbors
|
|
Country_color_map result;
|
|
for(const auto& country_name : all_countries)
|
|
{
|
|
// first: find a free color index
|
|
bool color_used[5] = { false, false, false, false, false };
|
|
auto& neighbor_set = country_neighbors_map[country_name];
|
|
for (auto& neighbor : neighbor_set)
|
|
{
|
|
auto it = result.find(neighbor);
|
|
// if there is a country in the map, then it must have been assigned one!
|
|
if (it != result.end())
|
|
{
|
|
auto used_color_index = it->second;
|
|
color_used[used_color_index] = true;
|
|
}
|
|
}
|
|
|
|
// find the first color index not used
|
|
bool found = false;
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
if (color_used[i] == false)
|
|
{
|
|
found = true;
|
|
result[country_name] = i;
|
|
}
|
|
}
|
|
// assertion check!!!
|
|
if(!found)
|
|
std::cout << "*** ASSERTION ERROR: NO INDEX FOUND!!!\n";
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
std::string Aos::locate_country(Arr_handle arrh, const QVector3D& point)
|
|
{
|
|
using Point_2 = Countries_arr::Point_2;
|
|
using Naive_pl = CGAL::Arr_naive_point_location<Countries_arr>;
|
|
|
|
auto& arr = *reinterpret_cast<Countries_arr*>(arrh.get());
|
|
Point_2 query_point(Dir3(point.x(), point.y(), point.z()),
|
|
Point_2::Location_type::NO_BOUNDARY_LOC);
|
|
|
|
Naive_pl npl(arr);
|
|
auto obj = npl.locate(query_point);
|
|
|
|
using Arrangement_2 = Countries_arr;
|
|
using Vertex_const_handle = typename Arrangement_2::Vertex_const_handle;
|
|
using Halfedge_const_handle = typename Arrangement_2::Halfedge_const_handle;
|
|
using Face_const_handle = typename Arrangement_2::Face_const_handle;
|
|
//const Vertex_const_handle* v;
|
|
//const Halfedge_const_handle* e;
|
|
//const Face_const_handle* f;
|
|
//std::cout << "The point (" << query_point << ") is located ";
|
|
if (auto f = boost::get<Face_const_handle>(&obj)) // located inside a face
|
|
{
|
|
const auto country_name = f->ptr()->data();
|
|
return country_name;
|
|
}
|
|
//else if (auto e = boost::get<Halfedge_const_handle>(&obj)) // located on an edge
|
|
// std::cout << "on an edge: " << (*e)->curve() << std::endl;
|
|
//else if (auto v = boost::get<Vertex_const_handle>(&obj)) // located on a vertex
|
|
// std::cout << "on " << (((*v)->is_isolated()) ? "an isolated" : "a")
|
|
// << " vertex: " << (*v)->point() << std::endl;
|
|
//else CGAL_error_msg("Invalid object.");
|
|
|
|
return "";
|
|
} |