cgal/Envelope_3/test/Envelope_3/Envelope_test_3.h

519 lines
18 KiB
C++

// Copyright (c) 2005 Tel-Aviv University (Israel).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $Source: /CVSROOT/CGAL/Packages/Envelope_3/include/CGAL/Envelope_test_3.h,v $
// $Revision$ $Date$
// $Name: $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Michal Meyerovitch <gorgymic@post.tau.ac.il>
#ifndef CGAL_ENVELOPE_TEST_3_H
#define CGAL_ENVELOPE_TEST_3_H
#include <CGAL/Envelope_3/Envelope_base.h>
#include "Envelope_test_overlay_functor.h"
#include <CGAL/Envelope_3/Envelope_overlay_2.h>
#include <CGAL/Arr_walk_along_line_point_location.h>
#include <CGAL/Object.h>
#include <CGAL/enum.h>
#include <iostream>
#include <cassert>
#include <list>
#include <set>
#include <vector>
#include <map>
//#define CGAL_DEBUG_ENVELOPE_TEST_3
// this is a very trivial and not efficient algorithm for computing the lower envelope
// of general surfaces in 3d, used for testing.
// The algorithm projects the surfaces on the plane, and projects all the intersections
// between surfaces, to get an arrangement that is a partition of the real envelope.
// Then it computes for each part in the arrangement the surfaces on the envelope over it
// by comparing them all.
namespace CGAL {
template <class EnvelopeTraits_3, class MinimizationDiagram_2>
class Envelope_test_3
{
public:
typedef EnvelopeTraits_3 Traits;
typedef typename Traits::Surface_3 Surface_3;
typedef typename Traits::Xy_monotone_surface_3 Xy_monotone_surface_3;
typedef MinimizationDiagram_2 Minimization_diagram_2;
typedef typename Minimization_diagram_2::Point_2 Point_2;
typedef typename Minimization_diagram_2::X_monotone_curve_2 X_monotone_curve_2;
typedef typename Traits::Curve_2 Curve_2;
protected:
typedef Envelope_test_overlay_functor<Minimization_diagram_2> Overlay_functor;
typedef Envelope_overlay_2<Minimization_diagram_2, Overlay_functor> Overlay_2;
typedef Arr_walk_along_line_point_location<Minimization_diagram_2> Md_point_location;
typedef typename Minimization_diagram_2::Halfedge_const_iterator Halfedge_const_iterator;
typedef typename Minimization_diagram_2::Halfedge_const_handle Halfedge_const_handle;
typedef typename Minimization_diagram_2::Halfedge_handle Halfedge_handle;
typedef typename Minimization_diagram_2::Halfedge_iterator Halfedge_iterator;
typedef typename Minimization_diagram_2::Vertex_const_handle Vertex_const_handle;
typedef typename Minimization_diagram_2::Vertex_handle Vertex_handle;
typedef typename Minimization_diagram_2::Vertex_iterator Vertex_iterator;
typedef typename Minimization_diagram_2::Face_handle Face_handle;
typedef typename Minimization_diagram_2::Face_const_iterator Face_const_iterator;
typedef typename Minimization_diagram_2::Face_const_handle Face_const_handle;
typedef typename Minimization_diagram_2::Face_iterator Face_iterator;
typedef typename Minimization_diagram_2::Ccb_halfedge_circulator Ccb_halfedge_circulator;
typedef typename Minimization_diagram_2::Inner_ccb_iterator Hole_iterator;
typedef typename Minimization_diagram_2::Dcel::Face_data_iterator Face_data_iterator;
typedef std::pair<X_monotone_curve_2,
typename EnvelopeTraits_3::Multiplicity> Intersection_curve;
public:
// c'tor
Envelope_test_3()
{
}
// virtual destructor.
virtual ~Envelope_test_3(){}
template <class SurfaceIterator>
void construct_lu_envelope(SurfaceIterator begin,
SurfaceIterator end,
Minimization_diagram_2 &result)
{
if (begin == end)
{
return; // result is empty
}
std::vector<Xy_monotone_surface_3> surfaces;
SurfaceIterator si = begin;
for(; si != end; ++si)
{
surfaces.push_back(*si);
}
Md_point_location pl(result);
std::size_t number_of_surfaces = surfaces.size();
std::size_t j;
std::list<X_monotone_curve_2> curves_col;
std::list<Point_2> points_col;
typename std::list<Curve_2>::iterator boundary_it;
for(std::size_t i=0; i<number_of_surfaces; ++i)
{
Xy_monotone_surface_3 &cur_surface = surfaces[i];
// first insert all the projected curves of the boundary of the current surface
// collect the curve in this list, and use sweepline at the end
std::list<Object> boundary_list;
typedef std::pair<X_monotone_curve_2, Oriented_side> Boundary_xcurve;
typedef std::list<Object>::const_iterator Boundary_iterator;
traits.construct_projected_boundary_2_object()(cur_surface, std::back_inserter(boundary_list));
for(Boundary_iterator boundary_it = boundary_list.begin();
boundary_it != boundary_list.end();
++boundary_it)
{
const Object& obj = *boundary_it;
Boundary_xcurve boundary_cv;
assert(assign(boundary_cv, obj));
assign(boundary_cv, obj);
curves_col.push_back(boundary_cv.first);
}
// second, intersect it with all surfaces before it
Object cur_obj;
for(j=0; j<i; ++j)
{
Xy_monotone_surface_3& prev_surface = surfaces[j];
std::vector<Object> inter_objs;
traits.construct_projected_intersections_2_object()(cur_surface, prev_surface, std::back_inserter(inter_objs));
// we collect all intersections and use sweep to insert them
Point_2 point;
Intersection_curve curve;
for(std::size_t k=0; k<inter_objs.size(); ++k)
{
cur_obj = inter_objs[k];
assert(!cur_obj.is_empty());
if (CGAL::assign(point, cur_obj))
{
#ifdef CGAL_DEBUG_ENVELOPE_TEST_3
std::cout << "intersection between surfaces is a point: " << point << std::endl;
#endif
//insert_vertex(result, point, pl);
points_col.push_back(point);
}
else if (CGAL::assign(curve, cur_obj))
{
curves_col.push_back(curve.first);
/*#ifdef CGAL_DEBUG_ENVELOPE_TEST_3
std::cout << "intersection between surfaces is a curve: " << curve.first << std::endl;
#endif
std::list<Object> objs;
traits.make_x_monotone_2_object()(curve.first, std::back_inserter(objs));
std::list<Object>::iterator itr;
for(itr = objs.begin(); itr != objs.end(); ++itr)
{
X_monotone_curve_2 curr_cv;
assert(assign(curr_cv, *itr));
assign(curr_cv, *itr);
curves_col.push_back(curr_cv);
}*/
//insert(result, curve.first, pl);
}
else
{
assert_msg(false, "wrong intersection type");
}
}
}
}
// insert the curves
insert(result, curves_col.begin(), curves_col.end());
// insert the points
typename std::list<Point_2>::iterator pit = points_col.begin();
for(; pit != points_col.end(); ++pit)
insert_point(result, *pit, pl);
m_result = &result;
// now, foreach vertex, edge and face, we should determine which surfaces are minimal over it.
// update vertices' data
Vertex_iterator vi = result.vertices_begin();
for(; vi != result.vertices_end(); ++vi)
{
Vertex_handle vh = vi;
// first we find the surfaces that are defined over the vertex
std::list<Xy_monotone_surface_3> defined_surfaces;
typename Traits::Is_defined_over is_defined_over = traits.is_defined_over_object();
for(std::size_t i=0; i<number_of_surfaces; ++i)
if (is_defined_over(vh->point(), surfaces[i]))
defined_surfaces.push_back(surfaces[i]);
// now compare them over the vertex
set_minimum_over_vertex(vh, defined_surfaces.begin(), defined_surfaces.end());
}
// update edges' data
Halfedge_iterator hi = result.halfedges_begin();
for(; hi != result.halfedges_end(); ++hi, ++hi)
{
Halfedge_handle hh = hi;
// first we find the surfaces that are defined over the edge
std::list<Xy_monotone_surface_3> defined_surfaces;
for(std::size_t i=0; i<number_of_surfaces; ++i)
if (is_surface_defined_over_edge(hh, surfaces[i]))
defined_surfaces.push_back(surfaces[i]);
// now compare them over the edge
set_minimum_over_edge(hh, defined_surfaces.begin(), defined_surfaces.end());
}
// update faces' data
// init current face for caching of computation
current_face = Face_handle();
Face_iterator fi;
for (fi = result.faces_begin(); fi != result.faces_end(); ++fi)
{
#ifdef CGAL_DEBUG_ENVELOPE_TEST_3
std::cout << "deal with face" << std::endl;
#endif
Face_handle fh = fi;
// first we find the surfaces that are defined over the face
std::list<Xy_monotone_surface_3> defined_surfaces;
for (std::size_t i=0; i<number_of_surfaces; ++i)
if (is_surface_defined_over_face(fh, surfaces[i]))
defined_surfaces.push_back(surfaces[i]);
// now compare them over the face
set_minimum_over_face(fh, defined_surfaces.begin(),
defined_surfaces.end());
}
}
/*!
* compare the 2 envelopes by overlaying them, and then comparing the
* surfaces over the faces of the result map.
*
* \todo The overlay compares the data using assertions. This should be
* replaced, but since we want to terminate the overlay once we
* determine that the 2 diagrams differ, we cannot simply remove the
* assertions. One option is to generate an exception and catch it.
*/
bool compare_diagrams(Minimization_diagram_2 &test_env,
Minimization_diagram_2 &env)
{
Minimization_diagram_2 overlay_map;
overlay(test_env, env, overlay_map);
return true;
}
protected:
// fill the vertex with the surface on the envelope
// all the surfaces are known to be defined over the vertex' point
template <class SurfaceIterator>
void set_minimum_over_vertex(const Vertex_handle& v,
SurfaceIterator begin, SurfaceIterator end)
{
if (begin == end)
v->set_no_data();
else
{
SurfaceIterator si = begin;
// we set the first surface as the minimum, and then compare all the others
v->set_data(*si);
++si;
for(; si != end; ++si)
{
Comparison_result cr = traits.compare_z_at_xy_3_object()(v->point(), v->get_data(), *si);
if (cr == EQUAL)
v->add_data(*si);
else if (cr == LARGER)
v->set_data(*si); // this erases all surfaces from vertex's list
// else - new surface has no affect on the envelope
}
}
}
// fill the edge with the surface on the envelope
// all the surfaces are known to be defined over the edge's curve
template <class SurfaceIterator>
void set_minimum_over_edge(const Halfedge_handle& h, SurfaceIterator begin, SurfaceIterator end)
{
if (begin == end)
h->set_no_data();
else
{
if (h != current_edge)
compute_point_in_current_edge(h);
SurfaceIterator si = begin;
// we set the first surface as the minimum, and then compare all the others
h->set_data(*si);
++si;
for(; si != end; ++si)
{
Comparison_result cr = traits.compare_z_at_xy_3_object()(current_point_inside_edge,
h->get_data(), *si);
if (cr == EQUAL)
h->add_data(*si);
else if (cr == LARGER)
h->set_data(*si); // this erases all surfaces from halfedge's list
// else - new surface has no affect on the envelope
}
// set twin's data
h->twin()->set_data(h->begin_data(), h->end_data());
}
}
// fill the face with the surface on the envelope
// the surfaces are known to not intersect inside the face
// (but might intersect on its edges)
template <class SurfaceIterator>
void set_minimum_over_face(const Face_handle& face, SurfaceIterator begin, SurfaceIterator end)
{
if (face->is_unbounded() || begin == end)
{
// a special case - no surface over the unbounded face, and when there are no surfaces at all
face->set_no_data();
}
else
{
SurfaceIterator si = begin;
// we set the first surface as the minimum, and then compare all the others
face->set_data(*si);
++si;
for(; si != end; ++si)
{
Comparison_result cr = compare_surfaces_over_face(face, face->get_data(), *si);
if (cr == EQUAL)
face->add_data(*si);
else if (cr == LARGER)
face->set_data(*si); // this erases all surfaces from face's list
// else - new surface has no affect on the envelope
}
}
}
// compare surfaces over face
// return SMALLER if the first surface is closer to the envelope
// LARGER if the second surface is closer to the envelope
// EQUAL otherwise
// this is version 2 which uses a calculated point inside the face
Comparison_result compare_surfaces_over_face(const Face_handle& face,
const Xy_monotone_surface_3 &surf1,
const Xy_monotone_surface_3& surf2)
{
assert(!face->is_unbounded());
Comparison_result cur_res;
if (face != current_face)
compute_point_in_current_face(face);
cur_res = traits.compare_z_at_xy_3_object()(current_point,surf1,surf2);
#ifdef CGAL_DEBUG_ENVELOPE_TEST_3
std::cout << "for comparison inside face, current result = " << cur_res << std::endl;
#endif
return cur_res;
}
// check if the surface is defines over the edge
bool is_surface_defined_over_edge(const Halfedge_handle& h, Xy_monotone_surface_3 &surf)
{
// check it over a point inside the edge's curve
if (h != current_edge)
compute_point_in_current_edge(h);
bool result = traits.is_defined_over_object()(current_point_inside_edge, surf);
return result;
}
// check if the surface is defines over the face
// this is version checks the point inside the face
bool is_surface_defined_over_face(const Face_handle& face,
Xy_monotone_surface_3 &surf)
{
// we always have bounded surfaces
if (face->is_unbounded())
return false;
if (face != current_face)
compute_point_in_current_face(face);
bool result = traits.is_defined_over_object()(current_point,surf);
return result;
}
// compute a point inside the face of the arranegement
Point_2 compute_point_inside_face(Minimization_diagram_2 &env, Face_handle face)
{
assert(!face->is_unbounded());
#ifdef CGAL_DEBUG_ENVELOPE_TEST_3
std::cout << "in compute point inside face" << std::endl;
#endif
// 1. find an edge on the outer ccb of the face that is not vertical
Ccb_halfedge_circulator hec = face->outer_ccb();
Ccb_halfedge_circulator hec_begin = hec;
bool found = false;
do {
if (!traits.is_vertical_2_object()(hec->curve()))
{
found = true;
continue;
}
hec++;
} while(hec != hec_begin && !found);
assert(found);
Halfedge_handle found_hh = hec;
// 2. find a point on this edge's curve that is not one of its vertices
// (we use the middle of the curve)
Point_2 shoot_source = traits.construct_middle_point(found_hh->curve());
// 3. ray shoot up or down, into the face
// and find the intersection point of the ray. the segment between
// the point from which we shoot, and the intersection point lies
// inside the face. we take its middle point as a point inside the face
bool shoot_up = true;
// TODO_NEW_DESIGN - check this
// if (traits.compare_x(found_hh->source()->point(), found_hh->target()->point()) == LARGER)
// shoot_up = false;
if (traits.equal_2_object()(found_hh->source()->point(),
traits.construct_max_vertex_2_object()(found_hh->curve())))
shoot_up = false;
Md_point_location pl(env);
Object shoot_obj;
Halfedge_const_handle shoot_hh;
Vertex_const_handle shoot_vh;
Point_2 shoot_target;
if (shoot_up)
shoot_obj = pl.ray_shoot_up(shoot_source);
else
shoot_obj = pl.ray_shoot_down(shoot_source);
if (assign(shoot_hh, shoot_obj))
{
shoot_target = traits.vertical_ray_shoot_2(shoot_source, shoot_hh->curve());
}
else if (assign(shoot_vh, shoot_obj))
{
shoot_target = (env.non_const_handle(shoot_vh))->point();
}
else
CGAL_error(); // it cannot be the unbounded face
Point_2 res_point = traits.construct_middle_point(shoot_source, shoot_target);
#ifdef CGAL_DEBUG_ENVELOPE_TEST_3
std::cout << "finished computing point in face" << std::endl;
// just for checking, locate res_point in env to find face
Object test_pl_obj = pl.locate(res_point);
Face_const_handle test_fh;
assert(assign(test_fh, test_pl_obj));
assert(test_fh == face);
#endif
return res_point;
}
// compute a point inside the face saved in current_face
// and put the result into current_point
void compute_point_in_current_face(Face_handle face)
{
current_face = face;
current_point = compute_point_inside_face(*m_result, current_face);
}
// compute a point inside the edge saved in current_edge
// and put the result into current_point_inside_edge
void compute_point_in_current_edge(Halfedge_handle h)
{
current_edge = h;
current_point_inside_edge = traits.construct_middle_point(h->curve());
}
protected:
Overlay_2 overlay;
Traits traits;
Minimization_diagram_2 *m_result;
Face_handle current_face;
Point_2 current_point;
Halfedge_handle current_edge;
Point_2 current_point_inside_edge;
};
} //namespace CGAL
#endif