mirror of https://github.com/CGAL/cgal
1009 lines
35 KiB
C++
1009 lines
35 KiB
C++
// Copyright (c) 2013 Technical University Braunschweig (Germany).
|
|
// All rights reserved.
|
|
//
|
|
// This file is part of CGAL (www.cgal.org).
|
|
// You can redistribute it and/or modify it under the terms of the GNU
|
|
// General Public License as published by the Free Software Foundation,
|
|
// either version 3 of the License, or (at your option) any later version.
|
|
//
|
|
// Licensees holding a valid commercial license may use this file in
|
|
// accordance with the commercial license agreement provided with the software.
|
|
//
|
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// $URL$
|
|
// $Id$
|
|
//
|
|
//
|
|
// Author(s): Ning Xu <longyin0904@gmail.com>
|
|
//
|
|
|
|
#ifndef CGAL_ROTATIONAL_SWEEP_VISIBILITY_2_H
|
|
#define CGAL_ROTATIONAL_SWEEP_VISIBILITY_2_H
|
|
|
|
#include <CGAL/Arrangement_2.h>
|
|
#include <CGAL/tags.h>
|
|
#include <CGAL/enum.h>
|
|
#include <CGAL/Visibility_2/visibility_utils.h>
|
|
#include <vector>
|
|
#include <set>
|
|
|
|
//#define MYDEBUG
|
|
#ifdef MYDEBUG
|
|
#include <iostream>
|
|
using namespace std;
|
|
#endif
|
|
|
|
namespace CGAL {
|
|
|
|
template < class Arrangement_2_, class RegularizationTag = CGAL::Tag_true >
|
|
class Rotational_sweep_visibility_2
|
|
{
|
|
public:
|
|
typedef Arrangement_2_ Arrangement_2;
|
|
typedef RegularizationTag Regularization_tag;
|
|
typedef typename Arrangement_2::Geometry_traits_2 Geometry_traits_2;
|
|
typedef typename Arrangement_2::Traits_2 Traits_2;
|
|
typedef CGAL::Tag_true Supports_general_polygon_tag;
|
|
typedef CGAL::Tag_true Supports_simple_polygon_tag;
|
|
|
|
typedef typename Geometry_traits_2::FT Number_type;
|
|
typedef typename Geometry_traits_2::Point_2 Point_2;
|
|
typedef typename Geometry_traits_2::Ray_2 Ray_2;
|
|
typedef typename Geometry_traits_2::Segment_2 Segment_2;
|
|
typedef typename Geometry_traits_2::Direction_2 Direction_2;
|
|
|
|
typedef typename Arrangement_2::Face_const_handle Face_const_handle;
|
|
typedef typename Arrangement_2::Halfedge_const_handle
|
|
Halfedge_const_handle;
|
|
typedef typename Arrangement_2::Hole_const_iterator Hole_const_iterator;
|
|
typedef typename Arrangement_2::Ccb_halfedge_const_circulator
|
|
Circulator;
|
|
|
|
|
|
private:
|
|
template < class Arr_2_ED >
|
|
class Edge
|
|
{
|
|
public:
|
|
typedef Arr_2_ED Arrangement_2;
|
|
typedef typename Arrangement_2::Geometry_traits_2 Geometry_traits_2;
|
|
|
|
typedef typename Geometry_traits_2::Point_2 Point_2;
|
|
typedef typename Geometry_traits_2::FT Number_type;
|
|
typedef typename Arrangement_2::Ccb_halfedge_const_circulator
|
|
Circulator;
|
|
private:
|
|
Circulator circ;
|
|
CGAL::Orientation orient;
|
|
CGAL::Orientation boundary_orient;
|
|
enum { NOT_APPLIED, INWARD, OUTWARD, IN_EDGE, AT_SOURCE, AT_TARGET } mode;
|
|
int idx;
|
|
int prev_idx;
|
|
int quad;
|
|
private:
|
|
int compute_quad( const Point_2& query_pt, const Point_2& p )
|
|
{
|
|
CGAL::Comparison_result cx = CGAL::compare_x( query_pt, p );
|
|
CGAL::Comparison_result cy = CGAL::compare_y( query_pt, p );
|
|
if ( cy == CGAL::SMALLER ) {
|
|
if ( cx == CGAL::SMALLER )
|
|
return 0;
|
|
else
|
|
return 1;
|
|
} else if ( cy == CGAL::LARGER ) {
|
|
if ( cx == CGAL::LARGER )
|
|
return 2;
|
|
else
|
|
return 3;
|
|
} else {
|
|
if ( cx != CGAL::LARGER )
|
|
return 0;
|
|
else
|
|
return 2;
|
|
}
|
|
}
|
|
public:
|
|
Edge ( const Point_2& query_pt, const Circulator& he, bool on_edge, bool on_vertex )
|
|
: circ( he )
|
|
{
|
|
if ( on_vertex ) {
|
|
orient = CGAL::COLLINEAR;
|
|
if ( query_pt == he->source()->point() )
|
|
mode = AT_SOURCE;
|
|
else if ( query_pt == he->target()->point() )
|
|
mode = AT_TARGET;
|
|
else
|
|
mode = IN_EDGE;
|
|
} else if ( on_edge ) {
|
|
orient = CGAL::COLLINEAR;
|
|
mode = IN_EDGE;
|
|
} else {
|
|
orient = CGAL::orientation( query_pt,
|
|
he->source()->point(),
|
|
he->target()->point() );
|
|
if ( orient == CGAL::COLLINEAR ) {
|
|
if ( CGAL::collinear_are_ordered_along_line( query_pt,
|
|
he->source()->point(),
|
|
he->target()->point() ) )
|
|
mode = OUTWARD;
|
|
else
|
|
mode = INWARD;
|
|
} else {
|
|
mode = NOT_APPLIED;
|
|
}
|
|
}
|
|
quad = compute_quad( query_pt, he->source()->point() );
|
|
boundary_orient = CGAL::orientation( prev_source(), source(), target() );
|
|
}
|
|
void set_index ( int i, int p )
|
|
{ idx = i; prev_idx = p; }
|
|
|
|
const Point_2& source () const
|
|
{ return circ->source()->point(); }
|
|
const Point_2& target () const
|
|
{ return circ->target()->point(); }
|
|
const Point_2& prev_source() const
|
|
{ return circ->prev()->source()->point(); }
|
|
const Point_2& next_target() const
|
|
{ return circ->next()->target()->point(); }
|
|
Circulator circulator () const
|
|
{ return circ; }
|
|
CGAL::Orientation orientation () const
|
|
{ return orient; }
|
|
CGAL::Orientation boundary_orientation () const
|
|
{ return boundary_orient; }
|
|
bool inward () const
|
|
{ return ( mode == INWARD ); }
|
|
bool outward () const
|
|
{ return ( mode == OUTWARD ); }
|
|
bool in_edge () const
|
|
{ return ( mode == IN_EDGE ); }
|
|
bool at_source () const
|
|
{ return ( mode == AT_SOURCE ); }
|
|
bool at_target () const
|
|
{ return ( mode == AT_TARGET ); }
|
|
int index () const
|
|
{ return idx; }
|
|
int prev_index () const
|
|
{ return prev_idx; }
|
|
int quadrant () const
|
|
{ return quad; }
|
|
bool angle_less_than_pi () const
|
|
{ return ( quadrant() < 2 ); }
|
|
|
|
#ifdef MYDEBUG
|
|
void trace ( ostream& os )
|
|
{
|
|
os << "source=[" << source() << "],target=[" << target() << "],";
|
|
os << "orientation=[";
|
|
switch( orient ) {
|
|
case CGAL::LEFT_TURN:
|
|
os << "left";
|
|
break;
|
|
case CGAL::RIGHT_TURN:
|
|
os << "right";
|
|
break;
|
|
case CGAL::COLLINEAR:
|
|
os << "collinear";
|
|
break;
|
|
}
|
|
os << "],mode =[";
|
|
switch( mode ) {
|
|
case INWARD:
|
|
os << "inward";
|
|
break;
|
|
case OUTWARD:
|
|
os << "outward";
|
|
break;
|
|
case IN_EDGE:
|
|
os << "in_edge";
|
|
break;
|
|
case AT_SOURCE:
|
|
os << "at_source";
|
|
break;
|
|
case AT_TARGET:
|
|
os << "at_target";
|
|
break;
|
|
case NOT_APPLIED:
|
|
os << "not_applied";
|
|
break;
|
|
}
|
|
os << "]" << endl;
|
|
os << "\t\tquadrant=[" << quadrant() << "], idx=[" << index() << "], prev_idx=[" << prev_index() << "]" << endl;
|
|
}
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
class Less_Source
|
|
Compare the halfedges with their source point by their polar angle.
|
|
The class rovides a comparator:
|
|
Less_Source( const Edge_type& he1, const Edge_type& he2 )
|
|
where he1 and he2 are two half edges.
|
|
|
|
Precondition: he1 != he2
|
|
|
|
Special cases:
|
|
1) If two source points have the same angle, the halfedge goes outward
|
|
or makes a right turn is less than the halfedge goes inward
|
|
or makes a left turn.
|
|
If two halfedges both go outward or make a right turn, the one with
|
|
closer source point is less.
|
|
If two halfedges both go inward or make a left turn, the one with
|
|
farther source point is less.
|
|
2) If the source of an halfedge is the query point, consider the case
|
|
as moving the source point slightly along the line through the
|
|
halfedge, either toward the target or away the target, so that
|
|
the query point is still in the face.
|
|
*/
|
|
template < class E >
|
|
class Less_Source
|
|
{
|
|
private:
|
|
typedef E Edge_type;
|
|
typedef typename E::Geometry_traits_2 Geometry_traits_2;
|
|
typedef typename Geometry_traits_2::Point_2 Point_2;
|
|
typedef typename Geometry_traits_2::FT Number_type;
|
|
private:
|
|
// less_source_at_query_pt
|
|
// Precondition: he1.source() == query_pt
|
|
bool less_source_at_query_pt( const Edge_type& he1, const Edge_type& he2 )
|
|
{
|
|
CGAL::Orientation orient1 = he1.boundary_orientation();
|
|
CGAL::Orientation orient2 = CGAL::orientation( he1.source(), he1.target(), he2.source() );
|
|
if ( orient1 == CGAL::LEFT_TURN ) {
|
|
// The boundary makes a left turn at query_pt
|
|
// Consider the case as moving he1.source() slightly
|
|
// along the line through he1, away he1.target()
|
|
if ( orient2 == CGAL::COLLINEAR ) {
|
|
// he1.source(), he1.target(), he2.source() are collinear
|
|
if ( CGAL::collinear_are_ordered_along_line( he2.source(), he1.source(), he1.target() ) ) {
|
|
// he1.source() is between he1.target() and he2.source()
|
|
// he1 will be considered going inward
|
|
return false;
|
|
} else {
|
|
// he1.source(), he1.target(), he2.source() are ordered along ray
|
|
return !he2.angle_less_than_pi();
|
|
}
|
|
} else if ( orient2 == CGAL::LEFT_TURN ) {
|
|
// he1.source(), he1.target(), he2.source() make a left turn
|
|
if ( he2.angle_less_than_pi() ) {
|
|
return false;
|
|
} else {
|
|
return ( he1.target().y() < query_pt.y() ||
|
|
( he1.target().y() == query_pt.y() &&
|
|
he1.target().x() < query_pt.x() ) );
|
|
}
|
|
} else {
|
|
// he1.source(), he1.target(), he2.source() make a right turn
|
|
if ( he2.angle_less_than_pi() ) {
|
|
return ( he1.target().y() < query_pt.y() ||
|
|
( he1.target().y() == query_pt.y() &&
|
|
he1.target().x() < query_pt.x() ) );
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
} else {
|
|
// The boundary makes a right turn at query_pt,
|
|
// or does not make a turn at query_pt.
|
|
// Consider the case as moving he1.source() slightly
|
|
// along the line through he1, toward he1.target()
|
|
if ( orient2 == CGAL::COLLINEAR ) {
|
|
// he1.source(), he1.target(), he2.source() are collinear
|
|
if ( CGAL::collinear_are_ordered_along_line( he2.source(), he1.source(), he1.target() ) ) {
|
|
// he1.source() is between he1.target() and he2.source()
|
|
return !he2.angle_less_than_pi();
|
|
} else {
|
|
// he1.source(), he1.target(), he2.source() are ordered along ray
|
|
return true;
|
|
}
|
|
} else if ( orient2 == CGAL::LEFT_TURN ) {
|
|
// he1.source(), he1.target(), he2.source() make a left turn
|
|
if ( he2.angle_less_than_pi() ) {
|
|
return ( he1.target().y() > query_pt.y() ||
|
|
( he1.target().y() == query_pt.y() &&
|
|
he1.target().x() > query_pt.x() ) );
|
|
} else {
|
|
return true;
|
|
}
|
|
} else {
|
|
// he1.source(), he1.target(), he2.source() make a right turn
|
|
if ( he2.angle_less_than_pi() ) {
|
|
return false;
|
|
} else {
|
|
return ( he1.target().y() > query_pt.y() ||
|
|
( he1.target().y() == query_pt.y() &&
|
|
he1.target().x() > query_pt.x() ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// less
|
|
bool less ( const Edge_type& he1, const Edge_type& he2 )
|
|
{
|
|
if ( he2.at_source() )
|
|
return !less_source_at_query_pt( he2, he1 );
|
|
if ( he1.at_source() )
|
|
return less_source_at_query_pt( he1, he2 );
|
|
|
|
if ( he1.quadrant() != he2.quadrant() )
|
|
return ( he1.quadrant() < he2.quadrant() );
|
|
// In the same quadrant
|
|
CGAL::Orientation orient = CGAL::orientation( query_pt, he1.source(), he2.source() );
|
|
if ( orient != CGAL::COLLINEAR ) {
|
|
// General case, in the same quadrant
|
|
return ( orient == CGAL::LEFT_TURN );
|
|
} else {
|
|
// query_pt, he1.source(), he2.source() are collinear on a ray
|
|
if ( CGAL::collinear_are_ordered_along_line( query_pt, he1.source(), he2.source() ) ) {
|
|
// he1.source() is closer
|
|
return ( he1.orientation() == CGAL::RIGHT_TURN || he1.outward() );
|
|
} else {
|
|
// he2.source() is closer
|
|
return !( he2.orientation() == CGAL::RIGHT_TURN || he2.outward() );
|
|
}
|
|
}
|
|
}
|
|
public:
|
|
// Constructor
|
|
Less_Source( const Point_2& q )
|
|
: query_pt( q )
|
|
{}
|
|
// Comparator
|
|
// Precondition: he1 and he2 are not the same halfedge
|
|
bool operator () ( const Edge_type& he1, const Edge_type& he2 )
|
|
{ return less( he1, he2 ); }
|
|
private:
|
|
Point_2 query_pt;
|
|
Point_2 aux;
|
|
};
|
|
|
|
/*
|
|
class Less_Edge
|
|
Compare the halfedges intersecting a ray, find whose intersection point
|
|
is closer to the query point.
|
|
The class rovides a comparator:
|
|
Less_Edge( const Edge_type& he1, const Edge_type& he2 )
|
|
where he1 and he2 are two half edges.
|
|
|
|
Precondition: he1 != he2
|
|
*/
|
|
template < class E >
|
|
class Less_Edge
|
|
{
|
|
private:
|
|
typedef E Edge_type;
|
|
typedef typename E::Geometry_traits_2 Geometry_traits_2;
|
|
typedef typename Geometry_traits_2::Point_2 Point_2;
|
|
private:
|
|
Point_2 query_pt;
|
|
private:
|
|
// less_vertex
|
|
// Precondition: (1) he1 contains query_pt
|
|
// (2) he2 does not contain query_pt
|
|
bool less_vertex ( const Edge_type& he1, const Edge_type& he2 )
|
|
{
|
|
assert( !(he2.at_source() || he2.at_target() || he2.in_edge() ) );
|
|
return true;
|
|
}
|
|
// less_consecutive
|
|
// Precondition: (1) Both he1 and he2 does not contain query_pt
|
|
// (2) he1 is the previous halfedge of he2
|
|
bool less_consecutive ( const Edge_type& he1, const Edge_type& he2 )
|
|
{
|
|
if ( he1.outward() )
|
|
return true;
|
|
else if ( he1.inward() )
|
|
return false;
|
|
else if ( he1.orientation() == CGAL::LEFT_TURN ) {
|
|
return ( he2.boundary_orientation() == CGAL::RIGHT_TURN );
|
|
} else {
|
|
// he1.orientation() == CGAL::RIGHT_TURN
|
|
return ( he2.boundary_orientation() != CGAL::RIGHT_TURN );
|
|
}
|
|
}
|
|
// less_collinear
|
|
// Precondition: (1) Both he1 and he2 does not contain query_pt
|
|
// (2) he1 and he2 are not incident
|
|
// (3) query_pt, he1.source(), he1.target() are collinear
|
|
bool less_collinear ( const Edge_type& he1, const Edge_type& he2 )
|
|
{
|
|
if ( he2.inward() || he2.outward() ) {
|
|
return CGAL::collinear_are_ordered_along_line( query_pt,
|
|
he1.source(),
|
|
he2.source() );
|
|
} else {
|
|
return ( CGAL::orientation( he2.source(), he2.target(), he1.source() )
|
|
== he2.orientation() );
|
|
}
|
|
}
|
|
// less_general
|
|
// Precondition: (1) Both he1 and he2 does not contain query_pt
|
|
// (2) he1 and he2 are not incident
|
|
// (3) he1.orientation() == CGAL::LEFT_TURN || RIGHT_TURN
|
|
// (4) he2.orientation() == CGAL::LEFT_TURN || RIGHT_TURN
|
|
bool less_general ( const Edge_type& he1, const Edge_type& he2 )
|
|
{
|
|
CGAL::Orientation orient1 = CGAL::orientation( he1.source(),
|
|
he1.target(),
|
|
he2.source() );
|
|
CGAL::Orientation orient2 = CGAL::orientation( he1.source(),
|
|
he1.target(),
|
|
he2.target() );
|
|
if ( orient1 == orient2 ) {
|
|
// he2.source() and he2.target() lies on the same side of he1
|
|
return ( CGAL::orientation( he1.source(), he1.target(), query_pt )
|
|
!= orient1 );
|
|
} else {
|
|
// he2.source() and he2.target() lies on the different side of he1
|
|
return ( CGAL::orientation( he2.source(), he2.target(), he1.source() )
|
|
== he2.orientation() );
|
|
}
|
|
}
|
|
// less
|
|
bool less ( const Edge_type& he1, const Edge_type& he2 )
|
|
{
|
|
if ( he1.circulator() == he2.circulator() )
|
|
return false;
|
|
if ( he1.at_source() || he1.at_target() || he1.in_edge() )
|
|
return less_vertex( he1, he2 );
|
|
if ( he2.at_source() || he2.at_target() || he2.in_edge() )
|
|
return !less_vertex( he2, he1 );
|
|
if ( he1.index() == he2.prev_index() )
|
|
return less_consecutive( he1, he2 );
|
|
if ( he1.prev_index() == he2.index() )
|
|
return !less_consecutive( he2, he1 );
|
|
if ( he1.inward() || he1.outward() )
|
|
return less_collinear( he1, he2 );
|
|
if ( he2.inward() || he2.outward() )
|
|
return !less_collinear( he2, he1 );
|
|
return less_general( he1, he2 );
|
|
}
|
|
public:
|
|
Less_Edge ( const Point_2& q )
|
|
: query_pt( q )
|
|
{}
|
|
bool operator () ( const Edge_type& he1, const Edge_type& he2 )
|
|
{ return less( he1, he2 ); }
|
|
};
|
|
|
|
template < class E, class Comp >
|
|
class Active_Edge
|
|
{
|
|
private:
|
|
typedef E Edge_type;
|
|
typedef Comp Sorter;
|
|
typedef typename E::Geometry_traits_2 Geometry_traits_2;
|
|
typedef typename Geometry_traits_2::Point_2 Point_2;
|
|
typedef typename std::set<Edge_type,Sorter> Active_edge_container;
|
|
typedef typename std::vector<Edge_type> Edge_vector;
|
|
private:
|
|
Active_edge_container active_edges;
|
|
const Edge_vector * p_edges;
|
|
std::vector<bool> active;
|
|
public:
|
|
Active_Edge ( const Edge_vector * pe, const Sorter& s )
|
|
: p_edges( pe ), active_edges( s )
|
|
{ active.assign( pe->size(), false ); }
|
|
|
|
void insert ( const Edge_type& he )
|
|
{
|
|
active_edges.insert( he );
|
|
active[he.index()] = true;
|
|
}
|
|
void erase ( const Edge_type& he )
|
|
{
|
|
active_edges.erase( he );
|
|
active[he.index()] = false;
|
|
}
|
|
void replace ( const Edge_type& he1, const Edge_type& he2 )
|
|
{
|
|
typename Active_edge_container::const_iterator ait;
|
|
ait = active_edges.find( he1 );
|
|
assert( ait != active_edges.end() );
|
|
const Edge_type& const_he = *ait;
|
|
Edge_type& tmp_he = const_cast<Edge_type&>(const_he);
|
|
tmp_he = he2;
|
|
active[he1.index()] = false;
|
|
active[he2.index()] = true;
|
|
}
|
|
bool is_active( int idx ) const
|
|
{ return active[idx]; }
|
|
const Edge_type& closest () const
|
|
{ return *(active_edges.begin()); }
|
|
#ifdef MYDEBUG
|
|
void trace ( ostream& os )
|
|
{
|
|
typename Active_edge_container::const_iterator ait;
|
|
for( ait = active_edges.begin(); ait != active_edges.end(); ait++ ) {
|
|
cout << "Edge: " << ait->source() << " --> " << ait->target() << endl;
|
|
}
|
|
cout << "Active:[ ";
|
|
for ( int i = 0; i < active.size(); i++ ) {
|
|
if ( active[i] )
|
|
cout << i << " ";
|
|
}
|
|
cout << "]" << endl;
|
|
}
|
|
#endif
|
|
};
|
|
|
|
private:
|
|
typedef Edge<Arrangement_2> Edge_type;
|
|
typedef std::vector<Edge_type> Edge_vector;
|
|
typedef typename Edge_vector::const_iterator Edge_iterator;
|
|
typedef Less_Edge<Edge_type> Active_edge_sorter;
|
|
|
|
private:
|
|
// do_intersect_ray
|
|
// Verify whether the halfedge he will intersect the ray obtained by
|
|
// by slightly rotating the ray (query_pt, aux ) clockwisely
|
|
bool do_intersect_ray ( const Point_2& query_pt, const Point_2& aux, const Edge_type& he )
|
|
{
|
|
if ( he.orientation() != CGAL::COLLINEAR ) {
|
|
CGAL::Orientation orient1 = CGAL::orientation( query_pt, aux, he.source() );
|
|
CGAL::Orientation orient2 = CGAL::orientation( query_pt, aux, he.target() );
|
|
if ( orient1 == orient2 )
|
|
return false;
|
|
if ( orient1 == CGAL::COLLINEAR ) {
|
|
if ( CGAL::collinear_are_ordered_along_line( aux, query_pt, he.source() ) )
|
|
return false;
|
|
// Ray intersects he at he.source()
|
|
return ( he.orientation() == CGAL::RIGHT_TURN );
|
|
} else if ( orient2 == CGAL::COLLINEAR ) {
|
|
if ( CGAL::collinear_are_ordered_along_line( aux, query_pt, he.target() ) )
|
|
return false;
|
|
// Ray intersects he at he.target()
|
|
return ( he.orientation() == CGAL::LEFT_TURN );
|
|
} else {
|
|
return ( CGAL::orientation( query_pt, aux, he.target() ) == he.orientation() );
|
|
}
|
|
} else if ( he.inward() || he.outward() ) {
|
|
return false;
|
|
} else if ( he.at_source() ) {
|
|
CGAL::Orientation orient1 = CGAL::orientation( he.prev_source(), he.source(), he.target() );
|
|
if ( orient1 == CGAL::LEFT_TURN ) {
|
|
// The boundary makes a left turn at the query_pt
|
|
// Consider the case as moving he.source() slightly along the line
|
|
// through he, away he.target()
|
|
CGAL::Orientation orient2 = CGAL::orientation( he.source(), he.target(), aux );
|
|
if ( orient2 == CGAL::LEFT_TURN ) {
|
|
return false;
|
|
} else if ( orient2 == CGAL::RIGHT_TURN ) {
|
|
return true;
|
|
} else {
|
|
// he.source(), he.target(), aux ) are collinear
|
|
return !CGAL::collinear_are_ordered_along_line( aux, he.source(), he.target() );
|
|
}
|
|
} else {
|
|
// The boundary makes a right turn or does not turn at the query_pt
|
|
// Consider the case as moving he.source() slightly along the line
|
|
// through he, toward he.target()
|
|
return false;
|
|
}
|
|
} else if ( he.at_target() ) {
|
|
CGAL::Orientation orient1 = CGAL::orientation( he.source(), he.target(), he.next_target() );
|
|
if ( orient1 == CGAL::LEFT_TURN ) {
|
|
// The boundary makes a left turn at the query_pt
|
|
CGAL::Orientation orient2 = CGAL::orientation( he.target(), he.next_target(), aux );
|
|
if ( orient2 == CGAL::LEFT_TURN ) {
|
|
return ( CGAL::orientation( he.source(), he.target(), aux ) == CGAL::RIGHT_TURN );
|
|
} else if ( orient2 == CGAL::RIGHT_TURN ) {
|
|
return false;
|
|
} else {
|
|
return CGAL::collinear_are_ordered_along_line( aux, he.target(), he.next_target() );
|
|
}
|
|
} else if ( orient1 == CGAL::RIGHT_TURN ) {
|
|
// The boundary makes a right turn at the query_pt
|
|
CGAL::Orientation orient2 = CGAL::orientation( he.target(), he.next_target(), aux );
|
|
if ( orient2 == CGAL::LEFT_TURN ) {
|
|
return false;
|
|
} else if ( orient2 == CGAL::RIGHT_TURN ) {
|
|
return ( CGAL::orientation( he.source(), he.target(), aux ) == CGAL::RIGHT_TURN );
|
|
} else {
|
|
return !CGAL::collinear_are_ordered_along_line( aux, he.target(), he.next_target() );
|
|
}
|
|
}
|
|
} else {
|
|
// he.in_edge() == true
|
|
CGAL::Orientation orient1 = CGAL::orientation( query_pt, he.target(), aux );
|
|
if ( orient1 == CGAL::LEFT_TURN ) {
|
|
return false;
|
|
} else if ( orient1 == CGAL::RIGHT_TURN ) {
|
|
return true;
|
|
} else {
|
|
return !CGAL::collinear_are_ordered_along_line( aux, query_pt, he.target() );
|
|
}
|
|
}
|
|
}
|
|
Point_2 calculate_intersection( const Point_2& query_pt, const Point_2& aux, const Circulator& he )
|
|
{
|
|
Ray_2 ray ( query_pt, aux );
|
|
Segment_2 seg ( he->source()->point(), he->target()->point() );
|
|
CGAL::Object res = CGAL::intersection( ray, seg );
|
|
const Point_2 * ipoint = CGAL::object_cast<Point_2>(&res);
|
|
if ( ipoint ) {
|
|
return *ipoint;
|
|
} else {
|
|
assert( seg.has_on( query_pt ) );
|
|
return query_pt;
|
|
}
|
|
}
|
|
void compute_visibility_partition ( const Point_2& query_pt,
|
|
Edge_iterator first,
|
|
Edge_iterator last,
|
|
std::vector<Point_2>& out_points )
|
|
{
|
|
Point_2 aux;
|
|
Active_edge_sorter closer( query_pt );
|
|
Active_Edge< Edge_type, Active_edge_sorter > active( &unsorted_edges, closer );
|
|
|
|
// Initialize the edges intersecting the ray
|
|
aux = first->source();
|
|
if ( query_on_vertex && query_pt == aux ) {
|
|
CGAL::Orientation orient1 = CGAL::orientation( first->prev_source(), first->source(), first->target() );
|
|
if ( orient1 == CGAL::LEFT_TURN ) {
|
|
aux = Point_2( query_pt.x()+query_pt.x()-first->target().x(), query_pt.y()+query_pt.y()-first->target().y() );
|
|
} else {
|
|
aux = first->target();
|
|
}
|
|
}
|
|
|
|
for ( Edge_iterator eit = edges.begin(); eit != edges.end(); eit++ ) {
|
|
if ( do_intersect_ray( query_pt, aux, *eit ) ) {
|
|
active.insert( *eit );
|
|
}
|
|
}
|
|
|
|
#ifdef MYDEBUG
|
|
cout << "query_pt = [" << query_pt << "]" <<endl;
|
|
cout << "Unsorted edges" << endl;
|
|
cout << "================================" << endl;
|
|
for ( int i = 0; i < unsorted_edges.size(); i++ ) {
|
|
unsorted_edges[i].trace( cout );
|
|
}
|
|
cout << endl;
|
|
cout << "Sorted edges" << endl;
|
|
cout << "================================" << endl;
|
|
for ( int i = 0; i < edges.size(); i++ ) {
|
|
edges[i].trace( cout );
|
|
}
|
|
cout << endl;
|
|
cout << "After Initialization" << endl;
|
|
cout << "================================" << endl;
|
|
active.trace( cout );
|
|
cout << endl;
|
|
#endif
|
|
|
|
// Rotational sweep the ray, until reach the end of the cone
|
|
Point_2 first_pt, last_pt;
|
|
first_pt = last_pt = calculate_intersection( query_pt, aux, active.closest().circulator() );
|
|
bool first_at_vertex = ( first_pt == active.closest().source() || first_pt == active.closest().target() );
|
|
for ( Edge_iterator eit = first; eit != last; eit++ ) {
|
|
aux = eit->source();
|
|
int idx = eit->index();
|
|
int prev = eit->prev_index();
|
|
Circulator top = active.closest().circulator();
|
|
assert( unsorted_edges[idx].circulator() == eit->circulator() );
|
|
|
|
#ifdef MYDEBUG
|
|
cout << "idx = " << idx << " , prev = " << prev << endl;
|
|
cout << "Current edge: " << eit->source() << " --> " << eit->target() << " Previous edge: " << unsorted_edges[prev].source() << " --> " << unsorted_edges[prev].target() << endl;
|
|
cout << "top: " << top->source()->point() << " --> " << top->target()->point() << endl;
|
|
#endif
|
|
|
|
if ( active.is_active( idx ) && active.is_active( prev ) ) {
|
|
// Both edges incident to the current vertex are active
|
|
#ifdef MYDEBUG
|
|
cout << "Both Active!" << endl;
|
|
#endif
|
|
|
|
active.erase( *eit );
|
|
active.erase( unsorted_edges[prev] );
|
|
if ( top != active.closest().circulator() ) {
|
|
Point_2 u;
|
|
u = calculate_intersection( query_pt, aux, active.closest().circulator() );
|
|
if ( last_pt != eit->source() )
|
|
out_points.push_back( eit->source() );
|
|
out_points.push_back( u );
|
|
last_pt = u;
|
|
#ifdef MYDEBUG
|
|
cout << "New Top! Intersection = " << u << endl;
|
|
#endif
|
|
}
|
|
} else if ( active.is_active( idx ) ) {
|
|
#ifdef MYDEBUG
|
|
cout << "Current Active!" << endl;
|
|
#endif
|
|
// Only one edge whose source is the current vertex is active.
|
|
active.replace( *eit, unsorted_edges[prev] );
|
|
if ( top != active.closest().circulator() ) {
|
|
if ( last_pt != eit->source() ) {
|
|
out_points.push_back( eit->source() );
|
|
last_pt = eit->source();
|
|
}
|
|
#ifdef MYDEBUG
|
|
cout << "New Top! Intersection = " << eit->source() << endl;
|
|
#endif
|
|
}
|
|
} else if ( active.is_active( prev ) ) {
|
|
#ifdef MYDEBUG
|
|
cout << "Previous Active!" << endl;
|
|
#endif
|
|
// Only one edge whose target is the current vertex is active.
|
|
active.replace( unsorted_edges[prev], *eit );
|
|
if ( top != active.closest().circulator() ) {
|
|
if ( last_pt != eit->source() ) {
|
|
out_points.push_back( eit->source() );
|
|
last_pt = eit->source();
|
|
}
|
|
#ifdef MYDEBUG
|
|
cout << "New Top! Intersection = " << eit->source() << endl;
|
|
#endif
|
|
}
|
|
} else {
|
|
// Both edges incident to the current vertex are not active
|
|
#ifdef MYDEBUG
|
|
cout << "Both Inctive!" << endl;
|
|
#endif
|
|
active.insert( *eit );
|
|
active.insert( unsorted_edges[prev] );
|
|
if ( top != active.closest().circulator() ) {
|
|
Point_2 u;
|
|
if ( query_on_vertex && query_pt == aux )
|
|
u = aux;
|
|
else
|
|
u = calculate_intersection( query_pt, aux, top );
|
|
if ( last_pt != u )
|
|
out_points.push_back( u );
|
|
out_points.push_back( eit->source() );
|
|
last_pt = eit->source();
|
|
#ifdef MYDEBUG
|
|
cout << "New Top! Intersection = " << u << endl;
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef MYDEBUG
|
|
cout << "*** After Iteration ***" << endl;
|
|
active.trace( cout );
|
|
cout << endl;
|
|
#endif
|
|
}
|
|
|
|
if ( first_pt != last_pt ) {
|
|
if ( first_at_vertex ||
|
|
( CGAL::orientation( out_points.front(), out_points.back(), first_pt ) != CGAL::COLLINEAR ) )
|
|
out_points.push_back( first_pt );
|
|
}
|
|
#ifdef MYDEBUG
|
|
cout << "Visibility segments:" << endl;
|
|
for ( int i = 0; i < out_points.size(); i++ ) {
|
|
cout << out_points[i] << endl;
|
|
}
|
|
cout << endl;
|
|
#endif
|
|
}
|
|
|
|
|
|
template < class VARR >
|
|
typename VARR::Face_handle
|
|
compute_visibility_impl ( const Point_2& query_pt, VARR& arr_out )
|
|
{
|
|
Point_2 aux( query_pt.x()+Number_type(1), query_pt.y() );
|
|
Less_Source<Edge_type> comp ( query_pt );
|
|
// Sort halfedges with their source point by polar angle
|
|
std::sort( edges.begin(), edges.end(), comp );
|
|
#ifdef MYDEBUG
|
|
for ( int i = 0; i < edges.size(); i++ ) {
|
|
for ( int j = 0; j < edges.size(); j++ ) {
|
|
if ( i == j )
|
|
continue;
|
|
bool res = comp( edges[i], edges[j] );
|
|
if ( res != ( i < j ) ) {
|
|
cout << "WRONG: Edges1: " << edges[i].source() << " --> " << edges[i].target() << " ";
|
|
cout << "Edges2: " << edges[j].source() << " --> " << edges[j].target() << " ";
|
|
if ( res )
|
|
cout << "Result: 1<2" <<endl;
|
|
else
|
|
cout << "Result: 2<1" <<endl;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
std::vector<Point_2> vec;
|
|
compute_visibility_partition( query_pt, edges.begin(), edges.end(), vec );
|
|
|
|
// Construct arrangement
|
|
CGAL::Visibility_2::report_while_handling_needles
|
|
< Rotational_sweep_visibility_2 >
|
|
( geom_traits, query_pt, vec, arr_out );
|
|
|
|
conditional_regularize( arr_out, Regularization_tag() );
|
|
|
|
edges.clear();
|
|
unsorted_edges.clear();
|
|
|
|
return arr_out.faces_begin();
|
|
}
|
|
|
|
/*! Regularize output if flag is set to true*/
|
|
template <typename VARR>
|
|
void conditional_regularize(VARR& out_arr, CGAL::Tag_true) {
|
|
regularize_output(out_arr);
|
|
}
|
|
/*! No need to regularize output if flag is set to false*/
|
|
template <typename VARR>
|
|
void conditional_regularize(VARR& out_arr, CGAL::Tag_false) {
|
|
//do nothing
|
|
}
|
|
|
|
/*! Regularizes the output - removes edges that have the same face on both
|
|
sides */
|
|
template <typename VARR>
|
|
void regularize_output(VARR& out_arr) {
|
|
typename VARR::Edge_iterator e_itr;
|
|
for (e_itr = out_arr.edges_begin() ;
|
|
e_itr != out_arr.edges_end() ; e_itr++) {
|
|
|
|
typename VARR::Halfedge_handle he = e_itr;
|
|
typename VARR::Halfedge_handle he_twin = he->twin();
|
|
if (he->face() == he_twin->face()) {
|
|
out_arr.remove_edge(he);
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
// Constructor
|
|
Rotational_sweep_visibility_2 ()
|
|
: p_arr( NULL ), geom_traits( NULL )
|
|
{}
|
|
Rotational_sweep_visibility_2 ( const Arrangement_2& arr )
|
|
: p_arr( &arr )
|
|
{ geom_traits = p_arr->geometry_traits(); }
|
|
|
|
const std::string name ()
|
|
{ return std::string( "R_visibility_2" ); }
|
|
bool is_attached () const
|
|
{ return (p_arr != NULL); }
|
|
void attach ( const Arrangement_2& arr )
|
|
{ p_arr = &arr; geom_traits = p_arr->geometry_traits(); }
|
|
void detach ()
|
|
{ p_arr = NULL; geom_traits = NULL; }
|
|
const Arrangement_2& arr () const
|
|
{ return *p_arr; }
|
|
|
|
template < typename VARR >
|
|
typename VARR::Face_handle
|
|
compute_visibility ( const Point_2& query_pt,
|
|
const Halfedge_const_handle he,
|
|
VARR& arr_out )
|
|
{
|
|
Face_const_handle f = he->face();
|
|
assert( !f->is_unbounded() );
|
|
|
|
Halfedge_const_handle he2 = he;
|
|
query_on_edge = true;
|
|
query_on_vertex = false;
|
|
if ( query_pt == he2->target()->point() )
|
|
he2 = he2->next();
|
|
if ( query_pt == he2->source()->point() )
|
|
query_on_vertex = true;
|
|
|
|
arr_out.clear();
|
|
edges.clear();
|
|
|
|
Circulator circ, curr;
|
|
Circulator qedge( he2 );
|
|
Circulator qprev( he2->prev() );
|
|
|
|
int hole_base = 0, hole_edge = 0;
|
|
|
|
if ( f->has_outer_ccb() ) {
|
|
curr = circ = f->outer_ccb();
|
|
do {
|
|
bool on_edge = false, on_vertex = false;
|
|
if ( curr == qedge ) {
|
|
on_edge = true;
|
|
on_vertex = query_on_vertex;
|
|
} else if ( curr == qprev ) {
|
|
on_edge = on_vertex = query_on_vertex;
|
|
}
|
|
edges.push_back( Edge_type( query_pt, curr, on_edge, on_vertex ) );
|
|
hole_edge++;
|
|
} while ( ++curr != circ );
|
|
for ( int i = 0; i < hole_edge; i++ ) {
|
|
edges[hole_base+i].set_index( hole_base+i, hole_base+(i+hole_edge-1)%hole_edge );
|
|
}
|
|
hole_base += hole_edge;
|
|
}
|
|
for ( Hole_const_iterator hi = f->holes_begin();
|
|
hi != f->holes_end(); hi++ ) {
|
|
curr = circ = *hi;
|
|
hole_edge = 0;
|
|
do {
|
|
bool on_edge = false, on_vertex = false;
|
|
if ( curr == qedge ) {
|
|
on_edge = true;
|
|
on_vertex = query_on_vertex;
|
|
} else if ( curr == qprev ) {
|
|
on_edge = on_vertex = query_on_vertex;
|
|
}
|
|
edges.push_back( Edge_type( query_pt, curr, on_edge, on_vertex ) );
|
|
hole_edge++;
|
|
} while ( ++curr != circ );
|
|
for ( int i = 0; i < hole_edge; i++ ) {
|
|
edges[hole_base+i].set_index( hole_base+i, hole_base+(i+hole_edge-1)%hole_edge );
|
|
}
|
|
hole_base += hole_edge;
|
|
}
|
|
|
|
unsorted_edges.assign( edges.begin(), edges.end() );
|
|
|
|
return compute_visibility_impl( query_pt, arr_out );
|
|
}
|
|
|
|
template < typename VARR >
|
|
typename VARR::Face_handle
|
|
compute_visibility ( const Point_2& query_pt,
|
|
const Face_const_handle f,
|
|
VARR& arr_out )
|
|
{
|
|
assert( !f->is_unbounded() );
|
|
|
|
query_on_edge = query_on_vertex = false;
|
|
arr_out.clear();
|
|
edges.clear();
|
|
|
|
Circulator circ, curr;
|
|
|
|
int hole_base = 0, hole_edge = 0;
|
|
|
|
if ( f->has_outer_ccb() ) {
|
|
curr = circ = f->outer_ccb();
|
|
do {
|
|
edges.push_back( Edge_type( query_pt, curr, false, false ) );
|
|
hole_edge++;
|
|
} while ( ++curr != circ );
|
|
for ( int i = 0; i < hole_edge; i++ ) {
|
|
edges[hole_base+i].set_index( hole_base+i, hole_base+(i+hole_edge-1)%hole_edge );
|
|
}
|
|
hole_base += hole_edge;
|
|
}
|
|
for ( Hole_const_iterator hi = f->holes_begin();
|
|
hi != f->holes_end(); hi++ ) {
|
|
curr = circ = *hi;
|
|
hole_edge = 0;
|
|
do {
|
|
edges.push_back( Edge_type( query_pt, curr, false, false ) );
|
|
hole_edge++;
|
|
} while ( ++curr != circ );
|
|
for ( int i = 0; i < hole_edge; i++ ) {
|
|
edges[hole_base+i].set_index( hole_base+i, hole_base+(i+hole_edge-1)%hole_edge );
|
|
}
|
|
hole_base += hole_edge;
|
|
}
|
|
|
|
unsorted_edges.assign( edges.begin(), edges.end() );
|
|
|
|
return compute_visibility_impl( query_pt, arr_out );
|
|
}
|
|
|
|
private:
|
|
const Arrangement_2 * p_arr;
|
|
const Geometry_traits_2 * geom_traits;
|
|
|
|
bool query_on_edge;
|
|
bool query_on_vertex;
|
|
Edge_vector edges;
|
|
Edge_vector unsorted_edges;
|
|
}; // End of class Rotational_sweep_visibility_2
|
|
|
|
} // End namespace CGAL
|
|
|
|
#endif
|