3976 lines
102 KiB
C++
3976 lines
102 KiB
C++
/*
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2017,
|
|
University in Hagen,
|
|
Faculty of Mathematics and Computer Science,
|
|
Database Systems for New Applications.
|
|
|
|
SECONDO is free software; 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 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
SECONDO is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with SECONDO; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
----
|
|
|
|
//[_] [\_]
|
|
|
|
*/
|
|
|
|
|
|
#include "RegionT.h"
|
|
#include "SimpleLineT.h"
|
|
#include "RegionCreator.h"
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
#include "RobustSetOps.h"
|
|
|
|
#include "WinUnix.h"
|
|
|
|
/*
|
|
11.4 Implementation of Class ~Region~
|
|
|
|
11.4.1 Constructors
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
inline RegionT<Array>::RegionT( const int initsize ) :
|
|
StandardSpatialAttribute<2>(true),
|
|
region( initsize ),
|
|
bbox( false ),
|
|
noComponents( 0 ),
|
|
ordered( true )
|
|
{
|
|
}
|
|
|
|
|
|
bool IsInsideAbove(const HalfSegment& hs,
|
|
const Point& thirdPoint);
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
RegionT<Array>::RegionT( const Point& p1, const Point& p2, const Point& p3 ):
|
|
StandardSpatialAttribute<2>(true),region(6)
|
|
{
|
|
Clear();
|
|
if( !p1.IsDefined() || !p2.IsDefined() || !p3.IsDefined() ){
|
|
SetDefined(false); // UNDEFINED region
|
|
} else if(AlmostEqual(p1,p2) || AlmostEqual(p2,p3) || AlmostEqual(p3,p1) ){
|
|
SetDefined(true); // EMPTY region
|
|
} else { // triangular region
|
|
SetDefined(true);
|
|
StartBulkLoad();
|
|
|
|
HalfSegment hs;
|
|
int edgecnt = 0;
|
|
|
|
hs.Set(true,p1,p2);
|
|
hs.attr.faceno = 0; // only one face
|
|
hs.attr.cycleno = 0; // only one cycle
|
|
hs.attr.edgeno = edgecnt;
|
|
hs.attr.partnerno = edgecnt++;
|
|
hs.attr.insideAbove = IsInsideAbove(hs,p3);
|
|
*this += hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
*this += hs;
|
|
|
|
hs.Set(true,p2,p3);
|
|
hs.attr.faceno = 0; // only one face
|
|
hs.attr.cycleno = 0; // only one cycle
|
|
hs.attr.edgeno = edgecnt;
|
|
hs.attr.partnerno = edgecnt++;
|
|
hs.attr.insideAbove = IsInsideAbove(hs,p1);
|
|
*this += hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
*this += hs;
|
|
|
|
hs.Set(true,p3,p1);
|
|
hs.attr.faceno = 0; // only one face
|
|
hs.attr.cycleno = 0; // only one cycle
|
|
hs.attr.edgeno = edgecnt;
|
|
hs.attr.partnerno = edgecnt++;
|
|
hs.attr.insideAbove = IsInsideAbove(hs,p2);
|
|
*this += hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
*this += hs;
|
|
|
|
EndBulkLoad();
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2> class Array2>
|
|
RegionT<Array>::RegionT( const RegionT<Array2>& cr, bool onlyLeft ) :
|
|
StandardSpatialAttribute<2>(cr.IsDefined()),
|
|
region( cr.Size() ),
|
|
bbox( cr.bbox ),
|
|
noComponents( cr.NoComponents() ),
|
|
ordered( true )
|
|
{
|
|
|
|
if( IsDefined() && cr.Size() >0 ) {
|
|
assert( cr.IsOrdered() );
|
|
if( !onlyLeft ){
|
|
convertDbArrays(cr.region,region);
|
|
} else {
|
|
StartBulkLoad();
|
|
HalfSegment hs;
|
|
int j=0;
|
|
for( int i = 0; i < cr.Size(); i++ ) {
|
|
cr.Get( i, hs );
|
|
if (hs.IsLeftDomPoint()) {
|
|
region.Put( j, hs );
|
|
j++;
|
|
}
|
|
}
|
|
EndBulkLoad( false, false, false, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
RegionT<Array>::RegionT( const RegionT& cr) :
|
|
StandardSpatialAttribute<2>(cr.IsDefined()),
|
|
region( cr.Size() ),
|
|
bbox( cr.bbox ),
|
|
ordered( true )
|
|
{
|
|
if(IsDefined()) {
|
|
// NoComponents() can only be called on defined regions
|
|
noComponents = cr.NoComponents();
|
|
|
|
if(cr.Size() > 0) {
|
|
assert( cr.IsOrdered() );
|
|
region.copyFrom(cr.region);
|
|
}
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2> class Array2>
|
|
RegionT<Array>::RegionT( const RegionT<Array2>& cr) :
|
|
StandardSpatialAttribute<2>(cr.IsDefined()),
|
|
region( cr.Size() ),
|
|
bbox( cr.bbox ),
|
|
noComponents( cr.NoComponents() ),
|
|
ordered( true )
|
|
{
|
|
if( IsDefined() && cr.Size() >0 ) {
|
|
assert( cr.IsOrdered() );
|
|
convertDbArrays(cr.region,region);
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
RegionT<Array>::RegionT( const Rectangle<2>& r )
|
|
:StandardSpatialAttribute<2>(r.IsDefined()),
|
|
region(8)
|
|
{
|
|
Clear();
|
|
if( r.IsDefined() )
|
|
{
|
|
SetDefined( true);
|
|
HalfSegment hs;
|
|
int partnerno = 0;
|
|
double min0 = r.MinD(0), max0 = r.MaxD(0),
|
|
min1 = r.MinD(1), max1 = r.MaxD(1);
|
|
|
|
Point v1(true, max0, min1),
|
|
v2(true, max0, max1),
|
|
v3(true, min0, max1),
|
|
v4(true, min0, min1);
|
|
|
|
if( AlmostEqual(v1, v2) ||
|
|
AlmostEqual(v2, v3) ||
|
|
AlmostEqual(v3, v4) ||
|
|
AlmostEqual(v4, v1) )
|
|
{ // one interval is (almost) empty, so will be the region
|
|
SetDefined( true );
|
|
return;
|
|
}
|
|
|
|
SetDefined( true );
|
|
StartBulkLoad();
|
|
|
|
hs.Set(true, v1, v2);
|
|
hs.attr.faceno = 0; // only one face
|
|
hs.attr.cycleno = 0; // only one cycle
|
|
hs.attr.edgeno = partnerno;
|
|
hs.attr.partnerno = partnerno++;
|
|
hs.attr.insideAbove = (hs.GetLeftPoint() == v1);
|
|
*this += hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
*this += hs;
|
|
|
|
hs.Set(true, v2, v3);
|
|
hs.attr.faceno = 0; // only one face
|
|
hs.attr.cycleno = 0; // only one cycle
|
|
hs.attr.edgeno = partnerno;
|
|
hs.attr.partnerno = partnerno++;
|
|
hs.attr.insideAbove = (hs.GetLeftPoint() == v2);
|
|
*this += hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
*this += hs;
|
|
|
|
hs.Set(true, v3, v4);
|
|
hs.attr.faceno = 0; // only one face
|
|
hs.attr.cycleno = 0; // only one cycle
|
|
hs.attr.edgeno = partnerno;
|
|
hs.attr.partnerno = partnerno++;
|
|
hs.attr.insideAbove = (hs.GetLeftPoint() == v3);
|
|
*this += hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
*this += hs;
|
|
|
|
hs.Set(true, v4, v1);
|
|
hs.attr.faceno = 0; // only one face
|
|
hs.attr.cycleno = 0; // only one cycle
|
|
hs.attr.edgeno = partnerno;
|
|
hs.attr.partnerno = partnerno++;
|
|
hs.attr.insideAbove = (hs.GetLeftPoint() == v4);
|
|
*this += hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
*this += hs;
|
|
|
|
EndBulkLoad();
|
|
}
|
|
else {
|
|
SetDefined( false );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
Destroy
|
|
|
|
This will destroy the contained flob.
|
|
|
|
*/
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
inline void RegionT<Array>::Destroy()
|
|
{
|
|
region.destroy();
|
|
}
|
|
|
|
/*
|
|
~Bounding Box~
|
|
|
|
If the geoid is null, this function just returns the
|
|
internally stored box. Otherwise, a box is computed
|
|
according the given geoid.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
inline const Rectangle<2> RegionT<Array>::BoundingBox(
|
|
const Geoid* geoid /*=0*/) const
|
|
{
|
|
if(geoid){ // spherical geometry case:
|
|
if(!geoid->IsDefined() || !IsDefined()){
|
|
return Rectangle<2>(false);
|
|
}
|
|
Rectangle<2> geobbox = Rectangle<2>(false);
|
|
for (int i=0; i<Size() ;i++){
|
|
HalfSegment hs;
|
|
Get( i, hs );
|
|
if( hs.IsLeftDomPoint() ){
|
|
if(!geobbox.IsDefined()){
|
|
geobbox = hs.BoundingBox(geoid);
|
|
} else {
|
|
geobbox = geobbox.Union(hs.BoundingBox(geoid));
|
|
}
|
|
} // else: ignore inverse HalfSegments
|
|
} // endfor
|
|
} // else: euclidean case
|
|
return bbox;
|
|
}
|
|
|
|
/*
|
|
~isOrdered~
|
|
|
|
CHecks whether the halfsegment array is sorted.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
inline bool RegionT<Array>::IsOrdered() const
|
|
{
|
|
return ordered;
|
|
}
|
|
|
|
|
|
/*
|
|
~GetAttr~
|
|
|
|
Returns the attributes of the halfsegment stored at the
|
|
specified position.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
inline const AttrType& RegionT<Array>::GetAttr( int position ) const
|
|
{
|
|
assert(( position>=0) && (position<=Size()-1));
|
|
HalfSegment hs;
|
|
region.Get( position, &hs);
|
|
return hs.GetAttr();
|
|
}
|
|
|
|
|
|
/*
|
|
~UpdateAttr~
|
|
|
|
Changes the attributes of the halfsegment at the current position of
|
|
the 'iterator'.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
inline void RegionT<Array>::UpdateAttr( AttrType& attr )
|
|
{
|
|
if (( pos>=0) && (pos<=Size()-1))
|
|
{
|
|
HalfSegment hs;
|
|
region.Get( pos, &hs);
|
|
hs.SetAttr( attr );
|
|
region.Put( pos, hs );
|
|
}
|
|
}
|
|
|
|
/*
|
|
~UpdateAttr~
|
|
|
|
Changes the attributes of the halfssegment at the speicfied position,
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
inline void RegionT<Array>::UpdateAttr( int position, AttrType& attr )
|
|
{
|
|
if (( position>=0) && (position<=Size()-1))
|
|
{
|
|
HalfSegment hs;
|
|
region.Get( position, &hs );
|
|
hs.SetAttr( attr );
|
|
region.Put( position, hs );
|
|
}
|
|
}
|
|
|
|
/*
|
|
Resets the internally 'iterator'.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
inline void RegionT<Array>::SelectFirst() const
|
|
{
|
|
if( IsEmpty() )
|
|
pos = -1;
|
|
else pos = 0;
|
|
}
|
|
|
|
/*
|
|
moves the internally iterator forward.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
inline void RegionT<Array>::SelectNext() const
|
|
{
|
|
if( pos >= 0 && pos < Size() - 1 )
|
|
pos++;
|
|
else pos = -1;
|
|
}
|
|
|
|
/*
|
|
Checks whether the internally iterator is at the end.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
inline bool RegionT<Array>::EndOfHs() const
|
|
{
|
|
return (pos==-1);
|
|
}
|
|
|
|
|
|
/*
|
|
Returns the halfsegment at the current iterator position.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
inline bool RegionT<Array>::GetHs(HalfSegment& hs ) const
|
|
{
|
|
assert( IsDefined() );
|
|
if( pos >= 0 && pos <= Size()-1 )
|
|
{
|
|
region.Get( pos, &hs );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
resizes the halfsegment array.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
inline void RegionT<Array>::Resize(const int newSize){
|
|
if(newSize>Size()){
|
|
region.resize(newSize);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Resizes the halfsegment array to fit exactly the contained halfsegments.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
inline void RegionT<Array>::TrimToSize(){
|
|
region.TrimToSize();
|
|
}
|
|
|
|
/*
|
|
switches to bulkload mode
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::StartBulkLoad()
|
|
{
|
|
ordered = false;
|
|
}
|
|
|
|
|
|
/*
|
|
Finishes the bulkload mode.
|
|
Be carefully with the flags.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::EndBulkLoad( bool sort, bool setCoverageNo,
|
|
bool setPartnerNo, bool computeRegion )
|
|
{
|
|
if( !IsDefined() ) {
|
|
Clear();
|
|
return;
|
|
}
|
|
|
|
if( sort ) {
|
|
Sort();
|
|
}
|
|
|
|
if( setCoverageNo )
|
|
{
|
|
HalfSegment hs;
|
|
int currCoverageNo = 0;
|
|
|
|
for( int i = 0; i < this->Size(); i++ )
|
|
{
|
|
Get( i, hs );
|
|
|
|
if( hs.IsLeftDomPoint() )
|
|
currCoverageNo++;
|
|
else
|
|
currCoverageNo--;
|
|
|
|
hs.attr.coverageno = currCoverageNo;
|
|
|
|
region.Put( i, hs );
|
|
}
|
|
}
|
|
|
|
if( setPartnerNo )
|
|
SetPartnerNo();
|
|
|
|
|
|
if( computeRegion ) {
|
|
try{
|
|
ComputeRegion();
|
|
} catch(...){
|
|
RegionCreator<Array>::createRegion(®ion,this);
|
|
}
|
|
}
|
|
|
|
region.TrimToSize();
|
|
ordered = true;
|
|
}
|
|
|
|
|
|
/*
|
|
Checks whether the region contains p. Returns also true if the p
|
|
is located of the region's boundary.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::Contains( const Point& p, const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
|
|
assert( IsDefined() );
|
|
assert( p.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
|
|
if( IsEmpty() || !p.IsDefined() || (geoid && !geoid->IsDefined())){
|
|
return false;
|
|
}
|
|
if( !p.Inside(bbox) ){
|
|
return false;
|
|
}
|
|
assert( IsOrdered() );
|
|
std::map<int, int> faceISN;
|
|
HalfSegment hs;
|
|
|
|
int coverno=0;
|
|
int startpos=0;
|
|
double y0;
|
|
|
|
//1. find the right place by binary search
|
|
if( Find( p, startpos ) ){
|
|
return true;
|
|
}
|
|
if ( startpos == 0 ){ //p is smallest
|
|
return false;
|
|
} else if ( startpos == Size() ){ //p is largest
|
|
return false;
|
|
}
|
|
|
|
int i = startpos - 1;
|
|
|
|
//2. deal with equal-x hs's
|
|
region.Get( i, hs );
|
|
while( i > 0 &&
|
|
hs.GetDomPoint().GetX() == p.GetX() ){
|
|
if( hs.Contains(p,geoid) ){
|
|
return true;
|
|
}
|
|
if( hs.IsLeftDomPoint() && hs.RayAbove( p, y0 ) ){
|
|
if( faceISN.find( hs.attr.faceno ) != faceISN.end() ){
|
|
faceISN[ hs.attr.faceno ]++;
|
|
} else {
|
|
faceISN[ hs.attr.faceno ] = 1;
|
|
}
|
|
}
|
|
region.Get( --i, hs );
|
|
}
|
|
|
|
// at this point, i is pointing to the last hs whose dp.x != p.x
|
|
|
|
//3. get the coverage value
|
|
coverno = hs.attr.coverageno;
|
|
|
|
//4. search the region value for coverageno steps
|
|
int touchedNo = 0;
|
|
while( i >= 0 && touchedNo < coverno ){
|
|
this->Get(i, hs);
|
|
if( hs.Contains(p) ){
|
|
return true;
|
|
}
|
|
if( hs.IsLeftDomPoint() &&
|
|
hs.GetLeftPoint().GetX() <= p.GetX() &&
|
|
p.GetX() <= hs.GetRightPoint().GetX() ){
|
|
touchedNo++;
|
|
}
|
|
if( hs.IsLeftDomPoint() && hs.RayAbove( p, y0 ) ){
|
|
if( faceISN.find( hs.attr.faceno ) != faceISN.end() ){
|
|
faceISN[ hs.attr.faceno ]++;
|
|
} else {
|
|
faceISN[ hs.attr.faceno ] = 1;
|
|
}
|
|
}
|
|
i--; //the iterator
|
|
}
|
|
|
|
for( std::map<int, int>::iterator iter = faceISN.begin();
|
|
iter != faceISN.end();
|
|
iter++ ){
|
|
if( iter->second % 2 != 0 ){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Checks whether p is located in the interior of this region.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::InnerContains( const Point& p,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( p.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
|
|
if( IsEmpty() || !p.IsDefined() || (geoid && !geoid->IsDefined())){
|
|
return false;
|
|
}
|
|
if( !p.Inside(bbox) ){
|
|
return false;
|
|
}
|
|
assert( IsOrdered() );
|
|
std::map<int, int> faceISN;
|
|
HalfSegment hs;
|
|
|
|
int coverno = 0,
|
|
startpos = 0;
|
|
double y0;
|
|
|
|
//1. find the right place by binary search
|
|
if( Find( p, startpos ) ){
|
|
return false;
|
|
}
|
|
if ( startpos == 0 ){ //p is smallest
|
|
return false;
|
|
} else if ( startpos == Size() ){ //p is largest
|
|
return false;
|
|
}
|
|
int i = startpos - 1;
|
|
|
|
//2. deal with equal-x hs's
|
|
region.Get( i, hs );
|
|
while( i > 0 &&
|
|
hs.GetDomPoint().GetX() == p.GetX() ){
|
|
if( hs.Contains(p,geoid) ){
|
|
return false;
|
|
}
|
|
if( hs.IsLeftDomPoint() && hs.RayAbove( p, y0 ) ){
|
|
if( faceISN.find( hs.attr.faceno ) != faceISN.end() ){
|
|
faceISN[ hs.attr.faceno ]++;
|
|
} else {
|
|
faceISN[ hs.attr.faceno ] = 1;
|
|
}
|
|
}
|
|
region.Get( --i, hs );
|
|
}
|
|
|
|
// at this point, i is pointing to the last hs whose dp.x != p.x
|
|
|
|
//3. get the coverage value
|
|
coverno = hs.attr.coverageno;
|
|
|
|
//4. search the region value for coverageno steps
|
|
int touchedNo = 0;
|
|
while( i >= 0 && touchedNo < coverno ){
|
|
this->Get(i, hs);
|
|
if( hs.Contains(p) ){
|
|
return false;
|
|
}
|
|
if( hs.IsLeftDomPoint() &&
|
|
hs.GetLeftPoint().GetX() <= p.GetX() &&
|
|
p.GetX() <= hs.GetRightPoint().GetX() ){
|
|
touchedNo++;
|
|
}
|
|
if( hs.IsLeftDomPoint() && hs.RayAbove( p, y0 ) ){
|
|
if( faceISN.find( hs.attr.faceno ) != faceISN.end() ){
|
|
faceISN[ hs.attr.faceno ]++;
|
|
} else {
|
|
faceISN[ hs.attr.faceno ] = 1;
|
|
}
|
|
}
|
|
i--; //the iterator
|
|
}
|
|
|
|
for( std::map<int, int>::iterator iter = faceISN.begin();
|
|
iter != faceISN.end();
|
|
iter++ ){
|
|
if( iter->second % 2 != 0 ){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
Checks whether this regions intersects inHs.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::Intersects( const HalfSegment& inHs,
|
|
const Geoid* geoid/*=0*/) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if( !IsEmpty() && bbox.Intersects( inHs.BoundingBox() ) )
|
|
{
|
|
if( Contains( inHs.GetLeftPoint(), geoid ) ||
|
|
Contains( inHs.GetRightPoint(), geoid ) ){
|
|
return true;
|
|
}
|
|
HalfSegment hs;
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get( i, hs );
|
|
if( hs.Intersects( inHs, geoid ) ){
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
CHecks whether this region contains hs.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::Contains( const HalfSegment& hs,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(geoid){
|
|
std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented!."
|
|
<< std::endl;
|
|
assert(false); // TODO: implement sperical geometry case
|
|
}
|
|
if( IsEmpty() ){
|
|
return false;
|
|
}
|
|
if( !hs.GetLeftPoint().Inside(bbox,geoid) ||
|
|
!hs.GetRightPoint().Inside(bbox,geoid) ){
|
|
return false;
|
|
}
|
|
if( !Contains(hs.GetLeftPoint(),geoid) ||
|
|
!Contains(hs.GetRightPoint(),geoid) ){
|
|
return false;
|
|
}
|
|
HalfSegment auxhs;
|
|
bool checkMidPoint = false;
|
|
std::vector<Point> intersection_points;
|
|
//now we know that both endpoints of hs is inside region
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get(i, auxhs);
|
|
if( auxhs.IsLeftDomPoint() ){
|
|
if( hs.Crosses(auxhs,geoid) ){
|
|
return false;
|
|
} else if( hs.Inside(auxhs,geoid) ){//hs is part of the border
|
|
return true;
|
|
} else if( hs.Intersects(auxhs,geoid)){
|
|
if(auxhs.Contains(hs.GetLeftPoint(),geoid) ||
|
|
auxhs.Contains(hs.GetRightPoint(),geoid) ||
|
|
hs.Contains(auxhs.GetLeftPoint(),geoid) ||
|
|
hs.Contains(auxhs.GetRightPoint(),geoid)){
|
|
checkMidPoint = true;
|
|
//the intersection point that is not the endpoint
|
|
Point temp_p;
|
|
|
|
if(hs.Intersection(auxhs,temp_p, geoid))
|
|
intersection_points.push_back(temp_p);
|
|
HalfSegment temp_hs;
|
|
if(hs.Intersection(auxhs, temp_hs, geoid)){
|
|
intersection_points.push_back(temp_hs.GetLeftPoint());
|
|
intersection_points.push_back(temp_hs.GetRightPoint());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( checkMidPoint ){
|
|
Point midp( true,
|
|
( hs.GetLeftPoint().GetX() + hs.GetRightPoint().GetX() ) / 2,
|
|
( hs.GetLeftPoint().GetY() + hs.GetRightPoint().GetY() ) / 2 );
|
|
if( !Contains( midp ) ){ return false; }
|
|
}
|
|
for(unsigned int i = 0;i < intersection_points.size();i++){
|
|
//cout<<intersection_points.size()<<endl;
|
|
Point p = intersection_points[i];
|
|
double x1 = (hs.GetLeftPoint().GetX() + p.GetX())/2;
|
|
double y1 = (hs.GetLeftPoint().GetY() + p.GetY())/2;
|
|
double x2 = (hs.GetRightPoint().GetX() + p.GetX())/2;
|
|
double y2 = (hs.GetRightPoint().GetY() + p.GetY())/2;
|
|
Point midp1(true, x1, y1);
|
|
Point midp2(true, x2, y2);
|
|
if(!Contains(midp1,geoid)){ return false; }
|
|
if(!Contains(midp2,geoid)){ return false; }
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
Checks wther hs is completely in the interior of this region.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::InnerContains( const HalfSegment& hs,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if( IsEmpty() ){
|
|
return false;
|
|
}
|
|
if( !hs.GetLeftPoint().Inside(bbox,geoid) ||
|
|
!hs.GetRightPoint().Inside(bbox,geoid) ){
|
|
return false;
|
|
}
|
|
if( !InnerContains(hs.GetLeftPoint(),geoid) ||
|
|
!InnerContains(hs.GetRightPoint(),geoid) ){
|
|
return false;
|
|
}
|
|
HalfSegment auxhs;
|
|
|
|
//now we know that both endpoints of hs are completely inside the region
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get(i, auxhs);
|
|
if( auxhs.IsLeftDomPoint() ){
|
|
if( hs.Intersects( auxhs, geoid ) ){
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::HoleEdgeContain( const HalfSegment& hs,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if( !IsEmpty() ) {
|
|
return false;
|
|
}
|
|
HalfSegment auxhs;
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get( i, auxhs );
|
|
if( auxhs.IsLeftDomPoint() &&
|
|
auxhs.attr.cycleno > 0 &&
|
|
hs.Inside( auxhs, geoid ) ){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
4.4.7 Operation ~intersects~
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2> class ArrayT2>
|
|
bool RegionT<Array>::Intersects( const RegionT<ArrayT2> &r,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( r.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if( IsEmpty() || r.IsEmpty() || (geoid && !geoid->IsDefined()) ){
|
|
return false;
|
|
}
|
|
if( !BoundingBox().Intersects( r.BoundingBox(), geoid ) ){
|
|
return false;
|
|
}
|
|
assert( IsOrdered() );
|
|
assert( r.IsOrdered() );
|
|
if( Inside( r, geoid ) || r.Inside( *this, geoid ) ){
|
|
return true;
|
|
}
|
|
HalfSegment hs1, hs2;
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get( i, hs1 );
|
|
if( hs1.IsLeftDomPoint() ){
|
|
for( int j = 0; j < r.Size(); j++ ){
|
|
r.Get( j, hs2 );
|
|
if( hs2.IsLeftDomPoint() &&
|
|
hs1.Intersects( hs2, geoid ) ){
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
Intersection with different data types
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::Intersection(const Point& p, PointsT<Array2>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
result.Clear();
|
|
if(!IsDefined() || !p.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
if(this->Contains(p,geoid)){
|
|
result+= p;
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void RegionT<Array>::Intersection(const PointsT<Array2>& ps,
|
|
PointsT<Array3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
result.Clear();
|
|
if(!IsDefined() || !ps.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
Point p(true);
|
|
result.StartBulkLoad();
|
|
for(int i=0;i<ps.Size();i++){
|
|
ps.Get(i,p);
|
|
if(this->Contains(p,geoid)){
|
|
result += p;
|
|
}
|
|
}
|
|
result.EndBulkLoad(false,false,false);
|
|
}
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void RegionT<Array>::Intersection(const LineT<Array2>& l, LineT<Array3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
|
|
try{
|
|
SetOp(l,*this,result,avlseg::intersection_op, geoid, true);
|
|
} catch (...){
|
|
// compute intersection using robust implementation
|
|
std::cerr << "Problem during plane sweep detected, "
|
|
<< "switch to slow implementation" << std::endl;
|
|
robust::intersection(*this,l,result);
|
|
}
|
|
}
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void RegionT<Array>::Intersection(const RegionT<Array2>& r,
|
|
RegionT<Array3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
SetOp(*this,r,result,avlseg::intersection_op, geoid);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void RegionT<Array>::Intersection(const SimpleLineT<Array2>& l,
|
|
SimpleLineT<Array3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
SetOp(l,*this,result,avlseg::intersection_op, geoid);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::Union(const Point& p, RegionT<Array2>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
if(!IsDefined() || !p.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result.Clear();
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
result.CopyFrom(this);
|
|
}
|
|
|
|
|
|
/*
|
|
Union with different spatial types.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void RegionT<Array>::Union(const PointsT<Array2>& ps, RegionT<Array3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
if(!IsDefined() || !ps.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result.Clear();
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
result.CopyFrom(this);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void RegionT<Array>::Union(const LineT<Array2>& line, RegionT<Array3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
if(!IsDefined() || !line.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result.Clear();
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
result.CopyFrom(this);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2> class ArrayT2,
|
|
template<typename T3> class ArrayT3>
|
|
void RegionT<Array>::Union(const RegionT<ArrayT2>& region,
|
|
RegionT<ArrayT3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
SetOp(*this,region,result,avlseg::union_op, geoid);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void RegionT<Array>::Union(const SimpleLineT<Array2>& line,
|
|
RegionT<Array3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
if(!IsDefined() || !line.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result.Clear();
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
result.CopyFrom(this);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::Minus(const Point& p, RegionT<Array2>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
if(!IsDefined() || !p.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result.Clear();
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
result.CopyFrom(this);
|
|
}
|
|
|
|
|
|
/*
|
|
Implementation of Minus.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void RegionT<Array>::Minus(const PointsT<Array2>& ps, RegionT<Array3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
if(!IsDefined() || !ps.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result.Clear();
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
result.CopyFrom(this);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void RegionT<Array>::Minus(const LineT<Array2>& line, RegionT<Array3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
if(!IsDefined() || !line.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result.Clear();
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
result.CopyFrom(this);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void RegionT<Array>::Minus(const RegionT<Array2>& region,
|
|
RegionT<Array3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
SetOp(*this,region,result,avlseg::difference_op, geoid);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void RegionT<Array>::Minus(const SimpleLineT<Array2> & line,
|
|
RegionT<Array3>& result,
|
|
const Geoid* geoid/*=0*/) const{
|
|
if(!IsDefined() || !line.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result.Clear();
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
result.CopyFrom(this);
|
|
}
|
|
|
|
/*
|
|
Inside check.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2> class Array2>
|
|
bool RegionT<Array>::Inside( const RegionT<Array2>& r,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
|
|
assert( IsDefined() );
|
|
assert( r.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
|
|
if(!IsDefined() || !r.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
return false;
|
|
}
|
|
if( IsEmpty() ){
|
|
return true;
|
|
}
|
|
if(r.IsEmpty()){
|
|
return false;
|
|
}
|
|
assert( IsOrdered() );
|
|
assert( r.IsOrdered() );
|
|
if( !r.BoundingBox().Contains( bbox, geoid ) ){
|
|
return false;
|
|
}
|
|
HalfSegment hs1, hs2;
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get( i, hs1 );
|
|
if( hs1.IsLeftDomPoint() ){
|
|
if( !r.Contains( hs1,geoid ) ){
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
bool existhole = false,
|
|
allholeedgeinside = true;
|
|
for( int j = 0; j < r.Size(); j++ ){
|
|
r.Get( j, hs2 );
|
|
if( hs2.IsLeftDomPoint() &&
|
|
hs2.attr.cycleno > 0 ){
|
|
//&& (hs2 is not masked by another face of region2)
|
|
if( !HoleEdgeContain( hs2,geoid ) ){
|
|
existhole=true;
|
|
if( !Contains( hs2,geoid ) ){
|
|
allholeedgeinside=false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if( existhole && allholeedgeinside ){
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Check for adjacency with another region.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
bool RegionT<Array>::Adjacent( const RegionT<Array2>& r,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( r.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if( IsEmpty() || r.IsEmpty() || (geoid && !geoid->IsDefined()) ){
|
|
return false;
|
|
}
|
|
if( !BoundingBox().Intersects( r.BoundingBox(),geoid ) ){
|
|
return false;
|
|
}
|
|
assert( IsOrdered() );
|
|
assert( r.IsOrdered() );
|
|
if( Inside( r,geoid ) || r.Inside( *this,geoid ) )
|
|
return false;
|
|
|
|
HalfSegment hs1, hs2;
|
|
bool found = false;
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get( i, hs1 );
|
|
if( hs1.IsLeftDomPoint() ){
|
|
for( int j = 0; j < r.Size(); j++ ){
|
|
r.Get( j, hs2 );
|
|
if( hs2.IsLeftDomPoint() && hs1.Intersects( hs2,geoid ) ){
|
|
if( hs1.Crosses( hs2, geoid ) ){
|
|
return false;
|
|
}
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/*
|
|
Check for overlap.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
bool RegionT<Array>::Overlaps( const RegionT<Array2>& r,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( r.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if( IsEmpty() || r.IsEmpty() || (geoid && !geoid->IsDefined()) ){
|
|
return false;
|
|
}
|
|
if( !BoundingBox().Intersects( r.BoundingBox(),geoid ) ){
|
|
return false;
|
|
}
|
|
assert( IsOrdered() );
|
|
assert( r.IsOrdered() );
|
|
if( Inside( r ) || r.Inside( *this ) )
|
|
return true;
|
|
|
|
HalfSegment hs1, hs2;
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get( i, hs1 );
|
|
if( hs1.IsLeftDomPoint() ){
|
|
for( int j = 0; j < r.Size(); j++ ){
|
|
r.Get( j, hs2 );
|
|
if( hs2.IsLeftDomPoint() ){
|
|
if( hs2.Crosses( hs1,geoid ) ){
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Checks whether p is on border of this region.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::OnBorder( const Point& p, const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( p.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(IsEmpty() || !p.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
return false;
|
|
}
|
|
int pos;
|
|
if( Find( p, pos ) ){ // the point is found on one half segment
|
|
return true;
|
|
}
|
|
if( pos == 0 ||
|
|
pos == Size() ){ // the point is smaller or bigger than all segments
|
|
return false;
|
|
}
|
|
HalfSegment hs;
|
|
pos--;
|
|
while( pos >= 0 ){
|
|
Get( pos--, hs );
|
|
if( hs.IsLeftDomPoint() && hs.Contains( p,geoid ) ){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::InInterior( const Point& p,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
return InnerContains( p, geoid );
|
|
}
|
|
|
|
|
|
/*
|
|
Minimum distance to p.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
double RegionT<Array>::Distance( const Point& p,
|
|
const Geoid* geoid /*=0*/ ) const
|
|
{
|
|
assert( !IsEmpty() ); // subsumes defined
|
|
assert( p.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
|
|
if(geoid){
|
|
cout << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<<endl;
|
|
assert(false); // TODO: Implement spherical geometry case.
|
|
}
|
|
|
|
assert( IsOrdered() );
|
|
if( Contains( p ) )
|
|
return 0.0;
|
|
|
|
HalfSegment hs;
|
|
double result = std::numeric_limits<double>::max();
|
|
|
|
for( int i = 0; i < Size(); i++ )
|
|
{
|
|
Get( i, hs );
|
|
|
|
if( hs.IsLeftDomPoint() )
|
|
result = MIN( result, hs.Distance( p, geoid ) );
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
Minimum distance to r.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
double RegionT<Array>::Distance( const Rectangle<2>& r,
|
|
const Geoid* geoid /*=0*/ ) const
|
|
{
|
|
assert( !IsEmpty() ); // subsumes defined
|
|
assert( r.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(geoid){
|
|
cout << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<<endl;
|
|
assert(false); // TODO: Implement spherical geometry case.
|
|
}
|
|
|
|
assert( IsOrdered() );
|
|
Point p1(true,r.MinD(0),r.MinD(1));
|
|
Point p2(true,r.MaxD(0),r.MinD(1));
|
|
Point p3(true,r.MaxD(0),r.MaxD(1));
|
|
Point p4(true,r.MinD(0),r.MaxD(1));
|
|
|
|
if(Contains(p1,geoid)) return 0.0;
|
|
if(Contains(p2,geoid)) return 0.0;
|
|
if(Contains(p3,geoid)) return 0.0;
|
|
if(Contains(p4,geoid)) return 0.0;
|
|
|
|
HalfSegment hs;
|
|
double mindist = std::numeric_limits<double>::max();
|
|
for(int i=0;i<region.Size(); i++){
|
|
Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
double d = hs.Distance(r,geoid);
|
|
if(d<mindist){
|
|
mindist = d;
|
|
if(AlmostEqual(mindist,0)){
|
|
return 0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return mindist;
|
|
}
|
|
|
|
|
|
/*
|
|
Check for intersection.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::Intersects( const Rectangle<2>& r,
|
|
const Geoid* geoid /*=0*/ ) const{
|
|
|
|
if(!IsDefined() || !r.IsDefined()){
|
|
return false;
|
|
}
|
|
if(IsEmpty() || r.IsEmpty()){
|
|
return false;
|
|
}
|
|
// either, the rectangle is contained in the region
|
|
// or one of the halfsegments of the region
|
|
// intersects the rectangle
|
|
|
|
Point p1(true,r.MinD(0),r.MinD(1));
|
|
Point p2(true,r.MaxD(0),r.MinD(1));
|
|
Point p3(true,r.MaxD(0),r.MaxD(1));
|
|
Point p4(true,r.MinD(0),r.MaxD(1));
|
|
|
|
if(Contains(p1,geoid)) return true;
|
|
if(Contains(p2,geoid)) return true;
|
|
if(Contains(p3,geoid)) return true;
|
|
if(Contains(p4,geoid)) return true;
|
|
|
|
HalfSegment hs;
|
|
for(int i=0;i<region.Size(); i++){
|
|
Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
if(hs.Intersects(r,geoid)){
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
Size of the covered area.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
double RegionT<Array>::Area(const Geoid* geoid/*=0*/) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(geoid){
|
|
std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<< std::endl; //TODO: Implement spherical geometry case.
|
|
assert(false);
|
|
}
|
|
int n = Size();
|
|
double area = 0.0,
|
|
x0 = 0.0, y0 = 0.0,
|
|
x1 = 0.0, y1 = 0.0;
|
|
|
|
// get minimum with respect to Y-dimension
|
|
double minY = MIN(BoundingBox().MinD(1), +0.0);
|
|
|
|
HalfSegment hs;
|
|
for(int i=0; i<n; i++){
|
|
Get( i, hs );
|
|
if( hs.IsLeftDomPoint() ){ // use only one halfsegment
|
|
x0 = hs.GetLeftPoint().GetX();
|
|
x1 = hs.GetRightPoint().GetX();
|
|
// y0, y1 must be >= 0, so we correct them
|
|
y0 = hs.GetLeftPoint().GetY() - minY;
|
|
y1 = hs.GetRightPoint().GetY() - minY;
|
|
double a = (x1-x0) * ((y1+y0) * 0.5);
|
|
if ( hs.attr.insideAbove ){
|
|
a = -a;
|
|
}
|
|
area += a;
|
|
}
|
|
}
|
|
|
|
return area;
|
|
}
|
|
|
|
/*
|
|
Distance to other spatial types.
|
|
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
double RegionT<Array>::Distance( const PointsT<Array2>& ps,
|
|
const Geoid* geoid /*=0*/ ) const
|
|
{
|
|
assert( !IsEmpty() ); // subsumes IsDefined()
|
|
assert( !ps.IsEmpty() ); // subsumes IsDefined()
|
|
assert( IsOrdered() );
|
|
assert( ps.IsOrdered() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
|
|
if(geoid){
|
|
cout << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<<endl;
|
|
assert(false); // TODO: Implement spherical geometry case.
|
|
}
|
|
|
|
double result = std::numeric_limits<double>::max();
|
|
Point p(true);
|
|
|
|
for( int i = 0; i < ps.Size(); i++ ){
|
|
ps.Get( i, p );
|
|
if( Contains( p ) ){
|
|
return 0.0;
|
|
}
|
|
HalfSegment hs;
|
|
for( int j = 0; j < Size(); j++ ){
|
|
Get( j, hs );
|
|
if( hs.IsLeftDomPoint() )
|
|
result = MIN( result, hs.Distance( p, geoid ) );
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
double RegionT<Array>::Distance( const RegionT<Array2> &r,
|
|
const Geoid* geoid /*=0*/ ) const
|
|
{
|
|
assert( !IsEmpty() ); // subsumes IsDefined()
|
|
assert( !r.IsEmpty() ); // subsumes IsDefined()
|
|
assert( IsOrdered() );
|
|
assert( r.IsOrdered() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
|
|
if(geoid){
|
|
cout << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<<endl;
|
|
assert(false); // TODO: Implement spherical geometry case.
|
|
}
|
|
|
|
if( Inside( r, geoid ) || r.Inside( *this, geoid ) ){
|
|
return 0.0;
|
|
}
|
|
double result = std::numeric_limits<double>::max();
|
|
HalfSegment hs1, hs2;
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get( i, hs1 );
|
|
if( hs1.IsLeftDomPoint() ){
|
|
for( int j = 0; j < r.Size(); j++ ){
|
|
r.Get( j, hs2 );
|
|
if( hs2.IsLeftDomPoint() ){
|
|
if( hs1.Intersects( hs2, geoid ) ){
|
|
return 0.0;
|
|
}
|
|
result = MIN( result, hs1.Distance( hs2, geoid ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Divides this region into their faces.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::Components( std::vector<RegionT<Array2>*>& components )
|
|
{
|
|
components.clear();
|
|
if( IsEmpty() ) { // subsumes IsDefined()
|
|
return;
|
|
}
|
|
for(int i=0;i<noComponents;i++){
|
|
components.push_back(new RegionT<Array2>(1));
|
|
components[i]->StartBulkLoad();
|
|
}
|
|
for(int i=0;i<Size();i++){
|
|
HalfSegment* hs = new HalfSegment(true, Point(true,0,0), Point(true,1,1));
|
|
region.Get(i,hs);
|
|
int face = hs->attr.faceno;
|
|
hs->attr.faceno = 0;
|
|
(*components[face]) += *hs;
|
|
delete hs;
|
|
}
|
|
for(int i=0;i<noComponents;i++){
|
|
components[i]->EndBulkLoad(false,false,false,false);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Returns the holes of this region.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::getHoles(RegionT<Array2>& result) const{
|
|
if(!IsDefined()){
|
|
result.SetDefined(false);
|
|
}
|
|
result.Clear();
|
|
result.SetDefined(true);
|
|
result.StartBulkLoad();
|
|
int edgeno = 0;
|
|
for(int i=0; i< Size(); i++){
|
|
HalfSegment hs;
|
|
Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
if(hs.attr.cycleno!=0){ // not the outer cycle
|
|
hs.attr.edgeno = edgeno;
|
|
hs.attr.insideAbove = ! hs.attr.insideAbove;
|
|
result += hs;
|
|
hs.SetLeftDomPoint(!hs.IsLeftDomPoint());
|
|
result += hs;
|
|
edgeno++;
|
|
}
|
|
}
|
|
}
|
|
result.EndBulkLoad(true,true,true,true);
|
|
}
|
|
|
|
|
|
/*
|
|
Affine transformations.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::Translate( const Coord& x, const Coord& y,
|
|
RegionT<Array2>& result ) const
|
|
{
|
|
result.Clear();
|
|
if( !IsDefined() ) {
|
|
result.SetDefined( false );
|
|
return;
|
|
}
|
|
result.SetDefined( true );
|
|
assert( IsOrdered() );
|
|
HalfSegment hs;
|
|
result.StartBulkLoad();
|
|
for( int i = 0; i < Size(); i++ )
|
|
{
|
|
Get( i, hs );
|
|
hs.Translate( x, y );
|
|
result += hs;
|
|
}
|
|
result.SetNoComponents( NoComponents() );
|
|
result.EndBulkLoad( false, false, false, false );
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::Rotate( const Coord& x, const Coord& y,
|
|
const double alpha,
|
|
RegionT<Array2>& result ) const
|
|
{
|
|
result.Clear();
|
|
if( !IsDefined() ) {
|
|
result.SetDefined( false );
|
|
return;
|
|
}
|
|
result.Resize(Size());
|
|
result.SetDefined( true );
|
|
|
|
double s = sin(alpha);
|
|
double c = cos(alpha);
|
|
|
|
double m00 = c;
|
|
double m01 = -s;
|
|
double m02 = x - x*c + y*s;
|
|
double m10 = s;
|
|
double m11 = c;
|
|
double m12 = y - x*s-y*c;
|
|
|
|
result.StartBulkLoad();
|
|
HalfSegment hso;
|
|
Point p1(true);
|
|
Point p2(true);
|
|
Point p1o(true);
|
|
Point p2o(true);
|
|
|
|
for( int i = 0; i < Size(); i++ )
|
|
{
|
|
Get( i, hso );
|
|
p1o = hso.GetLeftPoint();
|
|
p2o = hso.GetRightPoint();
|
|
p1.Set( m00*p1o.GetX()
|
|
+ m01*p1o.GetY() + m02,
|
|
m10*p1o.GetX()
|
|
+ m11*p1o.GetY() + m12);
|
|
p2.Set( m00*p2o.GetX()
|
|
+ m01*p2o.GetY() + m02,
|
|
m10*p2o.GetX()
|
|
+ m11*p2o.GetY() + m12);
|
|
|
|
HalfSegment hsr(hso); // ensure to copy attr;
|
|
hsr.Set(hso.IsLeftDomPoint(),p1,p2);
|
|
bool above = hso.attr.insideAbove;
|
|
|
|
if(p1>p2) {
|
|
above = !above;
|
|
}
|
|
|
|
hsr.attr.insideAbove = above;
|
|
result += hsr;
|
|
}
|
|
result.EndBulkLoad(true,true,true,false); // reordering may be required
|
|
|
|
}
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::Scale( const Coord& sx, const Coord& sy,
|
|
RegionT<Array2>& result ) const
|
|
{
|
|
result.Clear();
|
|
if( !IsDefined() ) {
|
|
result.SetDefined( false );
|
|
return;
|
|
}
|
|
result.Resize(Size());
|
|
result.SetDefined( true );
|
|
result.StartBulkLoad();
|
|
HalfSegment hso;
|
|
for( int i = 0; i < Size(); i++ ) {
|
|
Get( i, hso );
|
|
if(!hso.Scale(sx,sy)){
|
|
result.Clear();
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result += hso;
|
|
}
|
|
result.EndBulkLoad(); // reordering may be required
|
|
}
|
|
|
|
|
|
/*
|
|
Computres touchpooints with l.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename Ti3>class Array3>
|
|
void RegionT<Array>::TouchPoints( const LineT<Array2>& l,
|
|
PointsT<Array3>& result,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
result.Clear();
|
|
if( !IsDefined() || !l.IsDefined() || (geoid && !geoid->IsDefined()) ) {
|
|
result.SetDefined( false );
|
|
return;
|
|
}
|
|
result.SetDefined( true );
|
|
|
|
if( IsEmpty() || l.IsEmpty() ) {
|
|
return;
|
|
}
|
|
assert( IsOrdered() && l.IsOrdered() );
|
|
HalfSegment hs1, hs2;
|
|
Point p(true);
|
|
result.StartBulkLoad();
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get( i, hs1 );
|
|
if( hs1.IsLeftDomPoint() ){
|
|
for( int j = 0; j < l.Size(); j++ ){
|
|
l.Get( j, hs2 );
|
|
if( hs2.IsLeftDomPoint() ){
|
|
if( hs1.Intersection( hs2, p, geoid ) ) {
|
|
result += p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
result.EndBulkLoad();
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename Ti3>class Array3>
|
|
void RegionT<Array>::TouchPoints(
|
|
const RegionT<Array2>& r,
|
|
PointsT<Array3>& result,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
|
|
result.Clear();
|
|
if( !IsDefined() || !r.IsDefined() || (geoid && !geoid->IsDefined()) ) {
|
|
result.SetDefined( false );
|
|
return;
|
|
}
|
|
result.SetDefined( true );
|
|
if( IsEmpty() || r.IsEmpty() ){
|
|
return;
|
|
}
|
|
assert( IsOrdered() && r.IsOrdered() );
|
|
HalfSegment hs1, hs2;
|
|
Point p(false);
|
|
result.StartBulkLoad();
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get( i, hs1 );
|
|
if( hs1.IsLeftDomPoint() ){
|
|
for( int j = 0; j < r.Size(); j++ ){
|
|
r.Get( j, hs2 );
|
|
if( hs2.IsLeftDomPoint() ){
|
|
if( hs1.Intersection( hs2, p, geoid ) ){
|
|
result += p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
result.EndBulkLoad( true);
|
|
}
|
|
|
|
/*
|
|
Computes the common part of the boundary.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2,
|
|
template<typename Ti3>class Array3>
|
|
void RegionT<Array>::CommonBorder(
|
|
const RegionT<Array2>& r,
|
|
LineT<Array3>& result,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
result.Clear();
|
|
if( !IsDefined() || !r.IsDefined() || (geoid && !geoid->IsDefined()) ) {
|
|
result.SetDefined( false );
|
|
return;
|
|
}
|
|
result.SetDefined( true );
|
|
if( IsEmpty() || r.IsEmpty() ){
|
|
return;
|
|
}
|
|
assert( IsOrdered() && r.IsOrdered() );
|
|
HalfSegment hs1, hs2;
|
|
HalfSegment reshs;
|
|
int edgeno = 0;
|
|
Point p(true);
|
|
result.StartBulkLoad();
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get( i, hs1 );
|
|
if( hs1.IsLeftDomPoint() ){
|
|
for( int j = 0; j < r.Size(); j++ ){
|
|
r.Get( j, hs2 );
|
|
if( hs2.IsLeftDomPoint() ){
|
|
if( hs1.Intersection( hs2, reshs, geoid ) ){
|
|
reshs.attr.edgeno = edgeno++;
|
|
result += reshs;
|
|
reshs.SetLeftDomPoint( !reshs.IsLeftDomPoint() );
|
|
result += reshs;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
result.EndBulkLoad();
|
|
}
|
|
|
|
/*
|
|
returns the number of components.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
int RegionT<Array>::NoComponents() const
|
|
{
|
|
assert( IsDefined() );
|
|
return noComponents;
|
|
}
|
|
|
|
/*
|
|
Returns all points of the containes halfsegments.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::Vertices( PointsT<Array2>* result,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
result->Clear();
|
|
if(!IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result->SetDefined(false);
|
|
return;
|
|
}
|
|
result->SetDefined(true);
|
|
if( IsEmpty() ){
|
|
return;
|
|
}
|
|
assert( IsOrdered() );
|
|
HalfSegment hs;
|
|
int size = Size();
|
|
for( int i = 0; i < size; i++ ){
|
|
Get( i, hs );
|
|
Point p = hs.GetDomPoint();
|
|
*result += p;
|
|
}
|
|
result->EndBulkLoad( false, true );
|
|
}
|
|
|
|
/*
|
|
Computes the boundary of this region.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::Boundary( LineT<Array2>* result,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
result->Clear();
|
|
if(!IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result->SetDefined(false);
|
|
return;
|
|
}
|
|
assert( IsOrdered() );
|
|
result->SetDefined(true);
|
|
if( IsEmpty() ){
|
|
return;
|
|
}
|
|
HalfSegment hs;
|
|
result->StartBulkLoad();
|
|
int size = Size();
|
|
for( int i = 0; i < size; i++ ){
|
|
Get( i, hs );
|
|
if(hs.IsLeftDomPoint()){
|
|
hs.attr.edgeno = i;
|
|
*result += hs;
|
|
hs.SetLeftDomPoint(false);
|
|
*result += hs;
|
|
}
|
|
}
|
|
result->EndBulkLoad();
|
|
}
|
|
|
|
|
|
/*
|
|
Assignment operator.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
RegionT<Array>& RegionT<Array>::operator=( const RegionT<Array2>& r )
|
|
{
|
|
assert( r.IsOrdered() );
|
|
convertDbArrays(r.region, region);
|
|
bbox = r.bbox;
|
|
noComponents = r.noComponents;
|
|
del.isDefined = r.del.isDefined;
|
|
return *this;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
RegionT<Array>& RegionT<Array>::operator=( const RegionT& r )
|
|
{
|
|
assert( r.IsOrdered() );
|
|
region.copyFrom(r.region);
|
|
bbox = r.bbox;
|
|
noComponents = r.noComponents;
|
|
del.isDefined = r.del.isDefined;
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
auxiliary functions for parallel scan through 2 regions.
|
|
|
|
*/
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void SelectFirst_rr( const RegionT<Array2>& R1, const RegionT<Array3>& R2,
|
|
object& obj, status& stat )
|
|
{
|
|
R1.SelectFirst();
|
|
R2.SelectFirst();
|
|
|
|
HalfSegment hs1, hs2;
|
|
bool gotHs1 = R1.GetHs( hs1 ),
|
|
gotHs2 = R2.GetHs( hs2 );
|
|
|
|
if( !gotHs1 && !gotHs2 )
|
|
{
|
|
obj = none;
|
|
stat = endboth;
|
|
}
|
|
else if( !gotHs1 )
|
|
{
|
|
obj = second;
|
|
stat = endfirst;
|
|
}
|
|
else if( !gotHs2 )
|
|
{
|
|
obj = first;
|
|
stat = endsecond;
|
|
}
|
|
else //both defined
|
|
{
|
|
stat = endnone;
|
|
if( hs1 < hs2 )
|
|
obj = first;
|
|
else if( hs1 > hs2 )
|
|
obj = second;
|
|
else
|
|
obj=both;
|
|
}
|
|
}
|
|
|
|
template<template<typename T2>class Array2,
|
|
template<typename T3>class Array3>
|
|
void SelectNext_rr( const RegionT<Array2>& R1, const RegionT<Array3>& R2,
|
|
object& obj, status& stat )
|
|
{
|
|
// 1. get the current elements
|
|
HalfSegment hs1, hs2;
|
|
bool gotHs1 = R1.GetHs( hs1 ),
|
|
gotHs2 = R2.GetHs( hs2 );
|
|
|
|
//2. move the pointers
|
|
if( !gotHs1 && !gotHs2 )
|
|
{
|
|
//do nothing
|
|
}
|
|
else if( !gotHs1 )
|
|
{
|
|
R2.SelectNext();
|
|
gotHs2 = R2.GetHs( hs2 );
|
|
}
|
|
else if( !gotHs2 )
|
|
{
|
|
R1.SelectNext();
|
|
gotHs1 = R1.GetHs( hs1 );
|
|
}
|
|
else //both currently defined
|
|
{
|
|
if( hs1 < hs2 ) //then hs1 is the last output
|
|
{
|
|
R1.SelectNext();
|
|
gotHs1 = R1.GetHs( hs1 );
|
|
}
|
|
else if( hs1 > hs2 )
|
|
{
|
|
R2.SelectNext();
|
|
gotHs2 = R2.GetHs( hs2 );
|
|
}
|
|
else
|
|
{
|
|
R1.SelectNext();
|
|
gotHs1 = R1.GetHs( hs1 );
|
|
R2.SelectNext();
|
|
gotHs2 = R2.GetHs( hs2 );
|
|
}
|
|
}
|
|
|
|
//3. generate the outputs
|
|
if( !gotHs1 && !gotHs2 )
|
|
{
|
|
obj = none;
|
|
stat = endboth;
|
|
}
|
|
else if( !gotHs1 )
|
|
{
|
|
obj = second;
|
|
stat = endfirst;
|
|
}
|
|
else if( !gotHs2 )
|
|
{
|
|
obj = first;
|
|
stat = endsecond;
|
|
}
|
|
else //both defined
|
|
{
|
|
stat = endnone;
|
|
if( hs1 < hs2 )
|
|
obj = first;
|
|
else if( hs1 > hs2 )
|
|
obj = second;
|
|
else
|
|
obj = both;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Check for equality.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::operator==( const RegionT<Array>& r ) const
|
|
{
|
|
if(!IsDefined() && !r.IsDefined()){
|
|
return true;
|
|
}
|
|
|
|
if(!IsDefined() || !r.IsDefined()){
|
|
return false;
|
|
}
|
|
|
|
if( Size() != r.Size() )
|
|
return false;
|
|
|
|
if( IsEmpty() && r.IsEmpty() )
|
|
return true;
|
|
|
|
if( bbox != r.bbox )
|
|
return false;
|
|
|
|
assert( ordered && r.ordered );
|
|
object obj;
|
|
status stat;
|
|
SelectFirst_rr( *this, r, obj, stat );
|
|
|
|
while( obj == both || obj == none )
|
|
{
|
|
if( stat == endboth )
|
|
return true;
|
|
|
|
SelectNext_rr( *this, r, obj, stat );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::operator!=( const RegionT<Array> &cr) const
|
|
{
|
|
return !(*this==cr);
|
|
}
|
|
|
|
|
|
/*
|
|
Adding and subtracting halfsegments.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
RegionT<Array>& RegionT<Array>::operator+=( const HalfSegment& hs )
|
|
{
|
|
assert(IsDefined());
|
|
|
|
if( IsEmpty() )
|
|
bbox = hs.BoundingBox();
|
|
else
|
|
bbox = bbox.Union( hs.BoundingBox() );
|
|
|
|
if( !IsOrdered() )
|
|
{
|
|
region.Append(hs);
|
|
}
|
|
else
|
|
{
|
|
int pos;
|
|
if( !Find( hs, pos ) )
|
|
{
|
|
HalfSegment auxhs;
|
|
for( int i = region.Size() - 1; i >= pos; i++ )
|
|
{
|
|
region.Get( i, auxhs );
|
|
region.Put( i+1, auxhs );
|
|
}
|
|
region.Put( pos, hs );
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
RegionT<Array>& RegionT<Array>::operator-=( const HalfSegment& hs )
|
|
{
|
|
assert(IsDefined());
|
|
assert( IsOrdered() );
|
|
|
|
int pos;
|
|
if( Find( hs, pos ) )
|
|
{
|
|
HalfSegment auxhs;
|
|
for( int i = pos; i < Size(); i++ )
|
|
{
|
|
region.Get( i+1, auxhs );
|
|
region.Put( i, auxhs );
|
|
}
|
|
}
|
|
|
|
// Naive way to redo the bounding box.
|
|
if( IsEmpty() )
|
|
bbox.SetDefined( false );
|
|
int i = 0;
|
|
HalfSegment auxhs;
|
|
region.Get( i++, auxhs );
|
|
bbox = auxhs.BoundingBox();
|
|
for( ; i < Size(); i++ )
|
|
{
|
|
region.Get( i, auxhs );
|
|
bbox = bbox.Union( auxhs.BoundingBox() );
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
/*
|
|
Searches the index of a speified halfsegmnst.
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::Find( const HalfSegment& hs, int& pos ) const
|
|
{
|
|
assert( IsOrdered() );
|
|
assert(IsDefined());
|
|
return region.Find( &hs, HalfSegmentCompare, pos );
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::Find( const Point& p, int& pos ) const
|
|
{
|
|
assert( IsOrdered() );
|
|
assert( IsDefined());
|
|
return region.Find( &p, PointHalfSegmentCompare, pos );
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::Sort()
|
|
{
|
|
if(!IsDefined()){
|
|
return;
|
|
}
|
|
assert( !IsOrdered() );
|
|
|
|
region.Sort( HalfSegmentCompare );
|
|
|
|
ordered = true;
|
|
}
|
|
|
|
|
|
int HalfSegmentLogicCompare(const void *a, const void *b);
|
|
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::LogicSort()
|
|
{
|
|
|
|
if(!IsDefined()){
|
|
return;
|
|
}
|
|
region.Sort( HalfSegmentLogicCompare );
|
|
|
|
ordered = true;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
std::ostream& operator<<( std::ostream& os, const RegionT<Array>& cr )
|
|
{
|
|
os << "<"<<endl;
|
|
if( !cr.IsDefined() ) {
|
|
os << " undefined ";
|
|
} else {
|
|
HalfSegment hs;
|
|
for( int i = 0; i < cr.Size(); i++ )
|
|
{
|
|
cr.Get( i, hs );
|
|
// os << " " << hs << endl;
|
|
Point lp = hs.GetLeftPoint();
|
|
Point rp = hs.GetRightPoint();
|
|
printf("(%.10f, %.10f) (%.10f, %.10f)\n",
|
|
lp.GetX(),lp.GetY(),rp.GetX(),rp.GetY());
|
|
}
|
|
}
|
|
os << ">";
|
|
return os;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Sets the partner numbers. Is is assumed, that corresponding edges have
|
|
the same edgeno,
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::SetPartnerNo()
|
|
{
|
|
if( !IsDefined() )
|
|
return;
|
|
int size = Size();
|
|
int* tmp = new int[size/2];
|
|
memset(tmp,0,size*sizeof(int) / 2);
|
|
HalfSegment hs;
|
|
for( int i = 0; i < size; i++ )
|
|
{
|
|
Get( i, hs );
|
|
if( hs.IsLeftDomPoint() )
|
|
{
|
|
tmp[hs.attr.edgeno] = i;
|
|
}
|
|
else
|
|
{
|
|
int p = tmp[hs.attr.edgeno];
|
|
HalfSegment hs1( hs );
|
|
hs1.attr.partnerno = p;
|
|
Put( i, hs1 );
|
|
Get( p, hs );
|
|
hs1 = hs;
|
|
hs1.attr.partnerno = i;
|
|
Put( p, hs1 );
|
|
}
|
|
}
|
|
delete[] tmp;
|
|
}
|
|
|
|
bool GetAcceptedPoint( std::vector <EdgePoint>pointsOnEdge,
|
|
int &i, const int end,
|
|
EdgePoint &ep );
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::CreateNewSegments(std::vector<EdgePoint>pointsOnEdge,
|
|
RegionT<Array2> &cr,
|
|
const Point &bPoint,const Point &ePoint,
|
|
WindowEdge edge,int &partnerno,
|
|
bool inside, const Geoid* geoid/*=0*/)
|
|
//The inside attribute indicates if the points on edge will originate
|
|
//segments that are inside the window (its values is true), or outside
|
|
//the window (its value is false)
|
|
{
|
|
int begin, end, i;
|
|
HalfSegment *hs;
|
|
AttrType attr(0);
|
|
EdgePoint dp,dpAux;
|
|
|
|
if (pointsOnEdge.size()==0) return;
|
|
/*
|
|
for (int j=0;j<pointsOnEdge.size();j++)
|
|
cout<<endl<<j<<": "<<pointsOnEdge[j];
|
|
|
|
*/
|
|
sort(pointsOnEdge.begin(),pointsOnEdge.end());
|
|
|
|
/*
|
|
for (int j=0;j<pointsOnEdge.size();j++)
|
|
cout<<endl<<j<<": "<<pointsOnEdge[j];
|
|
|
|
*/
|
|
begin = 0;
|
|
end = pointsOnEdge.size()-1;
|
|
|
|
dp = pointsOnEdge[begin];
|
|
if ( dp.direction)//dp points to left or down
|
|
{
|
|
|
|
if (!dp.rejected)
|
|
{
|
|
//If dp is a rejected point then it must not be considered
|
|
//as point to be connected to the window edge
|
|
hs = new HalfSegment(true, bPoint, dp);
|
|
|
|
attr.partnerno = partnerno;
|
|
partnerno++;
|
|
if ( (edge == WTOP) || (edge == WLEFT) )
|
|
attr.insideAbove = !inside;
|
|
//If inside == true, then insideAbove attribute of the top and left
|
|
//half segments must be set to false, otherwise its value must be true.
|
|
//In other words, the insideAbove atribute value is the opposite of the
|
|
//parameter inside's value.
|
|
else
|
|
if ( (edge == WRIGHT) || (edge == WBOTTOM))
|
|
attr.insideAbove = inside;
|
|
//If inside == true, then insideAbove attribute of the right and bottom
|
|
//half segments must be set to true, otherwise its value must be false.
|
|
//In other words, the insideAbove atribute value is the same of the
|
|
//parameter inside's value.
|
|
hs->SetAttr(attr);
|
|
cr+=(*hs);
|
|
hs->SetLeftDomPoint( !hs->IsLeftDomPoint() );
|
|
cr+=(*hs);
|
|
delete hs;
|
|
}
|
|
begin++;
|
|
//The variable ~begin~ must be incremented until exists
|
|
// points with the same coordinates
|
|
//and directions as dp
|
|
while (begin<=end)
|
|
{
|
|
dpAux = pointsOnEdge[begin];
|
|
if (!( (dpAux.GetX() == dp.GetX()) && (dpAux.GetY() == dp.GetY())
|
|
&& (dpAux.direction==dp.direction) ) )
|
|
break;
|
|
begin++;
|
|
}
|
|
}
|
|
|
|
|
|
dp = pointsOnEdge[end];
|
|
if ( !dp.direction) //dp points to right or up
|
|
{
|
|
bool rejectEndPoint=dp.rejected;
|
|
end--;
|
|
|
|
while ( end >= begin )
|
|
{
|
|
dpAux = pointsOnEdge[end];
|
|
if ( !( (dpAux.GetX() == dp.GetX() ) && ( dpAux.GetY() == dp.GetY() ) &&
|
|
(dpAux.direction==dp.direction) ) )
|
|
break;
|
|
|
|
//when a rejected point is found the rejectEndPoint
|
|
//does not change anymore.
|
|
|
|
end--;
|
|
}
|
|
|
|
if (!rejectEndPoint)
|
|
{
|
|
hs = new HalfSegment(true, dp, ePoint);
|
|
attr.partnerno = partnerno;
|
|
if ( (edge == WTOP) || (edge == WLEFT) )
|
|
attr.insideAbove = !inside;
|
|
else
|
|
if ( (edge == WRIGHT) || (edge == WBOTTOM))
|
|
attr.insideAbove = inside;
|
|
partnerno++;
|
|
hs->SetAttr(attr);
|
|
cr+=(*hs);
|
|
hs->SetLeftDomPoint( !hs->IsLeftDomPoint() );
|
|
cr+=(*hs);
|
|
|
|
delete hs;
|
|
}
|
|
}
|
|
|
|
i = begin;
|
|
while (i < end)
|
|
{
|
|
EdgePoint ep1,ep2;
|
|
if ( GetAcceptedPoint(pointsOnEdge,i,end,ep1) )
|
|
{
|
|
i++;
|
|
if (GetAcceptedPoint(pointsOnEdge,i,end, ep2) )
|
|
i++;
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
if ( ! ( (ep1.GetX() == ep2.GetX()) && (ep1.GetY() == ep2.GetY()) ) )
|
|
{ //discard degenerated edges
|
|
hs = new HalfSegment(true, ep1, ep2);
|
|
attr.partnerno = partnerno;
|
|
partnerno++;
|
|
if ( (edge == WTOP) || (edge == WLEFT) )
|
|
attr.insideAbove = !inside;
|
|
else
|
|
if ( (edge == WRIGHT) || (edge == WBOTTOM))
|
|
attr.insideAbove = inside;
|
|
hs->SetAttr(attr);
|
|
cr+=(*hs);
|
|
hs->SetLeftDomPoint( !hs->IsLeftDomPoint() );
|
|
cr+=(*hs);
|
|
delete hs;
|
|
}
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::CreateNewSegmentsWindowVertices(const Rectangle<2> &window,
|
|
std::vector<EdgePoint> pointsOnEdge[4],
|
|
RegionT<Array2> &cr,
|
|
int &partnerno,bool inside,
|
|
const Geoid* geoid/*=0*/) const
|
|
//The inside attribute indicates if the points on edge will originate
|
|
//segments that are inside the window (its values is true), or outside
|
|
//the window (its value is false)
|
|
{
|
|
Point tlPoint(true,window.MinD(0),window.MaxD(1)),
|
|
trPoint(true,window.MaxD(0),window.MaxD(1)),
|
|
blPoint(true,window.MinD(0),window.MinD(1)),
|
|
brPoint(true,window.MaxD(0),window.MinD(1));
|
|
bool tl=false, tr=false, bl=false, br=false;
|
|
|
|
AttrType attr(0);
|
|
if ( ( (pointsOnEdge[WTOP].size()==0) ||
|
|
(pointsOnEdge[WLEFT].size()==0) )
|
|
&& ( this->Contains(tlPoint,geoid) ) ){
|
|
tl = true;
|
|
}
|
|
if ( ( (pointsOnEdge[WTOP].size()==0) ||
|
|
(pointsOnEdge[WRIGHT].size()==0) )
|
|
&& ( this->Contains(trPoint,geoid) ) ){
|
|
tr = true;
|
|
}
|
|
if ( ( (pointsOnEdge[WBOTTOM].size()==0) ||
|
|
(pointsOnEdge[WLEFT].size()==0) )
|
|
&& ( this->Contains(blPoint,geoid) ) ){
|
|
bl = true;
|
|
}
|
|
if ( ( (pointsOnEdge[WBOTTOM].size()==0) ||
|
|
(pointsOnEdge[WRIGHT].size()==0) )
|
|
&& ( this->Contains(brPoint,geoid) ) ){
|
|
br = true;
|
|
}
|
|
//Create top edge
|
|
if (tl && tr && (pointsOnEdge[WTOP].size()==0)){
|
|
HalfSegment *hs;
|
|
hs = new HalfSegment(true, tlPoint, trPoint);
|
|
//If inside == true, then insideAbove attribute of the top and left
|
|
//half segments must be set to false, otherwise its value must be true.
|
|
//In other words, the insideAbove atribute value is the opposite of the
|
|
//inside function's parameter value.
|
|
attr.insideAbove = !inside;
|
|
attr.partnerno = partnerno;
|
|
partnerno++;
|
|
|
|
hs->SetAttr(attr);
|
|
cr+=(*hs);
|
|
hs->SetLeftDomPoint( !hs->IsLeftDomPoint() );
|
|
cr+=(*hs);
|
|
delete hs;
|
|
}
|
|
//Create left edge
|
|
if (tl && bl && (pointsOnEdge[WLEFT].size()==0)){
|
|
HalfSegment *hs;
|
|
hs = new HalfSegment(true, tlPoint, blPoint);
|
|
//If inside == true, then insideAbove attribute of the top and left
|
|
//half segments must be set to false, otherwise its value must be true.
|
|
//In other words, the insideAbove atribute value is the opposite of the
|
|
//parameter inside's value.
|
|
attr.insideAbove = !inside;
|
|
attr.partnerno = partnerno;
|
|
partnerno++;
|
|
hs->SetAttr(attr);
|
|
cr+=(*hs);
|
|
hs->SetLeftDomPoint( !hs->IsLeftDomPoint() );
|
|
cr+=(*hs);
|
|
delete hs;
|
|
}
|
|
//Create right edge
|
|
if (tr && br && (pointsOnEdge[WRIGHT].size()==0)){
|
|
HalfSegment *hs;
|
|
hs = new HalfSegment(true, trPoint, brPoint);
|
|
//If inside == true, then insideAbove attribute of the right and bottom
|
|
//half segments must be set to true, otherwise its value must be false.
|
|
//In other words, the insideAbove atribute value is the same of the
|
|
//parameter inside's value.
|
|
attr.insideAbove = inside;
|
|
attr.partnerno = partnerno;
|
|
partnerno++;
|
|
hs->SetAttr(attr);
|
|
cr+=(*hs);
|
|
hs->SetLeftDomPoint( !hs->IsLeftDomPoint() );
|
|
cr+=(*hs);
|
|
delete hs;
|
|
}
|
|
//Create bottom edge
|
|
if (bl && br && (pointsOnEdge[WBOTTOM].size()==0)){
|
|
HalfSegment *hs;
|
|
hs = new HalfSegment(true, blPoint, brPoint);
|
|
//If inside == true, then insideAbove attribute of the right and bottom
|
|
//half segments must be set to true, otherwise its value must be false.
|
|
//In other words, the insideAbove atribute value is the same of the
|
|
//parameter inside's value.
|
|
attr.insideAbove = inside;
|
|
attr.partnerno = partnerno;
|
|
partnerno++;
|
|
hs->SetAttr(attr);
|
|
cr+=(*hs);
|
|
hs->SetLeftDomPoint( !hs->IsLeftDomPoint() );
|
|
cr+=(*hs);
|
|
delete hs;
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::ClippedHSOnEdge(const Rectangle<2> &window,
|
|
const HalfSegment &hs,
|
|
bool clippingIn,std::vector<EdgePoint> pointsOnEdge[4],
|
|
const Geoid* geoid/*=0*/)
|
|
{
|
|
//This function returns true if the segment lies on one of the window's edge.
|
|
// The clipped half segments that lie on the edges must be rejected according to
|
|
// the kind of clipping (returning the portion of the region that is inside the
|
|
// region or the portion that is outside).
|
|
|
|
EdgePoint ep1,ep2;
|
|
AttrType attr=hs.GetAttr();
|
|
bool reject = false,
|
|
result = false;
|
|
//Returns true if the clipped hs was treated as a segment on edge
|
|
if ( hs.GetLeftPoint().GetY() == hs.GetRightPoint().GetY() )
|
|
{ //horizontal edge
|
|
if (( hs.GetLeftPoint().GetY() == window.MaxD(1) ) )
|
|
{ //top edge
|
|
// If the half segment lies on the upper edge and
|
|
// the insideAbove attribute's value
|
|
// is true then the region's area is outside the window,
|
|
// and the half segment mustn't
|
|
// be included in the clipped region (Reject).
|
|
// However, its end points maybe will have to be
|
|
// connected to the vertices of the window.
|
|
// It happens only when the vertice of the
|
|
// window is inside the region and the end point
|
|
// is the first point on the window's
|
|
// edge (for the upper-left vertice) or the last
|
|
// point on the window's vertice (for
|
|
// the upper right edge).
|
|
if ( clippingIn && attr.insideAbove )
|
|
reject = true;
|
|
else
|
|
if ( !clippingIn && !attr.insideAbove )
|
|
reject = true;
|
|
ep1.Set(hs.GetLeftPoint(),false,reject); //--> right
|
|
ep2.Set(hs.GetRightPoint(),true,reject); //<-- left
|
|
pointsOnEdge[WTOP].push_back(ep1);
|
|
pointsOnEdge[WTOP].push_back(ep2);
|
|
result = true;
|
|
}
|
|
else //bottom edge
|
|
if (( hs.GetLeftPoint().GetY() == window.MinD(1) ) )
|
|
{
|
|
if ( clippingIn && !attr.insideAbove )
|
|
reject = true;
|
|
else
|
|
if ( !clippingIn && attr.insideAbove )
|
|
reject = true;
|
|
ep1.Set(hs.GetLeftPoint(),false,reject); //--> right
|
|
ep2.Set(hs.GetRightPoint(),true,reject); //<-- left
|
|
pointsOnEdge[WBOTTOM].push_back(ep1);
|
|
pointsOnEdge[WBOTTOM].push_back(ep2);
|
|
result = true;
|
|
}
|
|
}
|
|
else //Vertical edges
|
|
if ( hs.GetLeftPoint().GetX() == hs.GetRightPoint().GetX() )
|
|
{
|
|
if ( hs.GetLeftPoint().GetX() == window.MinD(0) ) //Left edge
|
|
{
|
|
if ( clippingIn && attr.insideAbove )
|
|
reject = true;
|
|
else
|
|
if (!clippingIn && !attr.insideAbove )
|
|
reject = true;
|
|
ep1.Set(hs.GetLeftPoint(),false,reject); //^ up
|
|
ep2.Set(hs.GetRightPoint(),true,reject); //v dowb
|
|
pointsOnEdge[WLEFT].push_back(ep1);
|
|
pointsOnEdge[WLEFT].push_back(ep2);
|
|
result = true;
|
|
}
|
|
else
|
|
if ( hs.GetLeftPoint().GetX() == window.MaxD(0) ) //Right edge
|
|
{
|
|
if ( clippingIn && !attr.insideAbove )
|
|
reject = true;
|
|
else
|
|
if ( !clippingIn && attr.insideAbove )
|
|
reject = true;
|
|
ep1.Set(hs.GetLeftPoint(),false,reject); //^ up
|
|
ep2.Set(hs.GetRightPoint(),true,reject); //v dowb
|
|
pointsOnEdge[WRIGHT].push_back(ep1);
|
|
pointsOnEdge[WRIGHT].push_back(ep2);
|
|
result = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::GetCycleDirection( const Point &pA,
|
|
const Point &pP,
|
|
const Point &pB )
|
|
{
|
|
double m_p_a,m_p_b;
|
|
if (pA.GetX() == pP.GetX()){//A --> P is a vertical segment
|
|
if (pA.GetY() > pP.GetY() ) {//A --> P directed downwards (case 1)
|
|
return false; //Counterclockwise
|
|
} else {//upwards (case 2)
|
|
return true; // Clockwise
|
|
}
|
|
}
|
|
if (pB.GetX() == pP.GetX()) {//P --> B is a vertical segment
|
|
if ( pP.GetY() > pB.GetY()){ //downwords (case 3)
|
|
return false; //Conterclockwise
|
|
} else {//upwards
|
|
return true; //Clockwise
|
|
}
|
|
}
|
|
|
|
//compute the slopes of P-->A and P-->B
|
|
m_p_a = ( pA.GetY() - pP.GetY() ) / ( pA.GetX() - pP.GetX() );
|
|
m_p_b = ( pB.GetY() - pP.GetY() ) / ( pB.GetX() - pP.GetX() );
|
|
if (m_p_a > m_p_b) //case 5
|
|
return false;//counterclockwise
|
|
else //case 6
|
|
return true; //clockwise
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::GetCycleDirection() const
|
|
{
|
|
/*
|
|
Preconditions:
|
|
* The region must represent just one cycle!!!!
|
|
* It is need that the edgeno stores the order that the half segments were
|
|
typed, and the half segments must be sorted in the half segment order. In
|
|
other words if hs1.attr.edgeno is less than hs2.attr.edgeno then hs1 was
|
|
typed first than hs2.
|
|
|
|
This function has the purpose of choosing the A, P, and B points in order
|
|
to call the function that really computes the cycle direction.
|
|
As the point P is leftmost point then it is the left point of hs1 or
|
|
the left point of hs2 because in the half segment order these two points
|
|
are equal. Now the problem is to decide which of the right points are A
|
|
and B. At the first sight we could say that the point A is the right point
|
|
of the half segment with lowest partner number. However it is not true ever
|
|
because the APB connected points may be go over the
|
|
bound of the pointlist. This will be the case if the cycle is in the form
|
|
P,B,..,A and B,...,A,P. Nevertheless the segments are ordered in the half
|
|
segment order, and when the last half segment is been considered for choosing
|
|
the APB connected points, the point A will be always the right point of the
|
|
last segment.
|
|
|
|
*/
|
|
Point pA(true,0,0), pP(true,0,0), pB(true,0,0);
|
|
HalfSegment hs1, hs2;
|
|
this->Get(0,hs1);
|
|
this->Get(1,hs2);
|
|
|
|
assert( hs1.GetLeftPoint()==hs2.GetLeftPoint() );
|
|
pP = hs1.GetLeftPoint();
|
|
// If we have the last half segment connected to the first half
|
|
// segment, the difference //between their partner numbers is
|
|
// more than one.
|
|
if (abs(hs1.attr.edgeno - hs2.attr.edgeno)>1)
|
|
{
|
|
if (hs1.attr.edgeno > hs2.attr.edgeno)
|
|
{
|
|
pA = hs1.GetRightPoint();
|
|
pB = hs2.GetRightPoint();
|
|
}
|
|
else
|
|
{
|
|
pA = hs2.GetRightPoint();
|
|
pB = hs1.GetRightPoint();
|
|
}
|
|
}
|
|
else
|
|
if (hs1.attr.edgeno < hs2.attr.edgeno)
|
|
{
|
|
pA = hs1.GetRightPoint();
|
|
pB = hs2.GetRightPoint();
|
|
}
|
|
else
|
|
{
|
|
pA = hs2.GetRightPoint();
|
|
pB = hs1.GetRightPoint();
|
|
}
|
|
return GetCycleDirection(pA,pP,pB);
|
|
}
|
|
|
|
//cycleDirection: true (cycle is clockwise) / false
|
|
// (cycle is counterclockwise)
|
|
//It is need that the attribute insideAbove of the half segments represents
|
|
//the order that their points were typed: true (left point, right point) /
|
|
//false (right point, left point).
|
|
|
|
void AddPointToEdgeArray( const Point &p,
|
|
const HalfSegment &hs,
|
|
const Rectangle<2> &window,
|
|
std::vector<EdgePoint> pointsOnEdge[4] );
|
|
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::GetClippedHSIn(const Rectangle<2> &window,
|
|
RegionT<Array2> &clippedRegion,
|
|
std::vector<EdgePoint> pointsOnEdge[4],
|
|
int &partnerno,
|
|
const Geoid* geoid/*=0*/) const
|
|
{
|
|
HalfSegment hs;
|
|
HalfSegment hsInside;
|
|
bool inside, isIntersectionPoint;
|
|
|
|
SelectFirst();
|
|
for(int i=0; i < Size(); i++){
|
|
GetHs( hs );
|
|
if (hs.IsLeftDomPoint()){
|
|
Point intersectionPoint;
|
|
hs.WindowClippingIn(window, hsInside, inside,
|
|
isIntersectionPoint,intersectionPoint,geoid);
|
|
if (isIntersectionPoint){
|
|
AddPointToEdgeArray(intersectionPoint,hs,window, pointsOnEdge);
|
|
} else if ( inside ) {
|
|
bool hsOnEdge = ClippedHSOnEdge(window,hsInside,true,
|
|
pointsOnEdge,geoid);
|
|
if (!hsOnEdge){
|
|
//Add the clipped segment to the new region if it was not rejected
|
|
hsInside.attr.partnerno=partnerno;
|
|
partnerno++;
|
|
hsInside.SetAttr(hsInside.attr);
|
|
clippedRegion += hsInside;
|
|
hsInside.SetLeftDomPoint( !hsInside.IsLeftDomPoint() );
|
|
clippedRegion += hsInside;
|
|
|
|
//Add the points to the array of the points that lie on
|
|
//some of the window's edges
|
|
const Point& lp = hsInside.GetLeftPoint(),
|
|
rp = hsInside.GetRightPoint();
|
|
|
|
//If the point lies on one edge it must be added to the
|
|
//corresponding vector.
|
|
AddPointToEdgeArray(lp,hs,window, pointsOnEdge);
|
|
AddPointToEdgeArray(rp, hs,window, pointsOnEdge);
|
|
}
|
|
}
|
|
}
|
|
SelectNext();
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::AddClippedHS( const Point &pl,
|
|
const Point &pr,
|
|
AttrType &attr,
|
|
int &partnerno )
|
|
{
|
|
HalfSegment hs(true,pl,pr);
|
|
attr.partnerno = partnerno;
|
|
partnerno++;
|
|
hs.SetAttr(attr);
|
|
(*this)+=hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
(*this)+=hs;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::GetClippedHSOut(const Rectangle<2> &window,
|
|
RegionT<Array2> &clippedRegion,
|
|
std::vector<EdgePoint> pointsOnEdge[4],
|
|
int &partnerno,
|
|
const Geoid* geoid/*=0*/) const
|
|
{
|
|
for (int i=0; i < Size();i++){
|
|
HalfSegment hs;
|
|
HalfSegment hsInside;
|
|
bool inside=false,isIntersectionPoint=false;
|
|
Get(i,hs);
|
|
if (hs.IsLeftDomPoint()){
|
|
Point intersectionPoint;
|
|
hs.WindowClippingIn(window,hsInside, inside,
|
|
isIntersectionPoint,intersectionPoint,geoid);
|
|
if (inside){
|
|
bool hsOnEdge=false;
|
|
if (isIntersectionPoint){
|
|
HalfSegment aux( hs );
|
|
if (hs.GetLeftPoint()!=intersectionPoint)
|
|
clippedRegion.AddClippedHS(aux.GetLeftPoint(),
|
|
intersectionPoint,
|
|
aux.attr,partnerno) ;
|
|
if (hs.GetRightPoint()!=intersectionPoint)
|
|
clippedRegion.AddClippedHS(intersectionPoint,
|
|
aux.GetRightPoint(),
|
|
aux.attr,partnerno);
|
|
AddPointToEdgeArray(intersectionPoint,aux,
|
|
window, pointsOnEdge);
|
|
} else {
|
|
hsOnEdge = ClippedHSOnEdge(window, hsInside,
|
|
false, pointsOnEdge, geoid);
|
|
if (!hsOnEdge){
|
|
HalfSegment aux( hs );
|
|
if (hs.GetLeftPoint()!=hsInside.GetLeftPoint()){
|
|
//Add the part of the half segment composed by the left
|
|
//point of hs and the left point of hsInside.
|
|
clippedRegion.AddClippedHS(aux.GetLeftPoint(),
|
|
hsInside.GetLeftPoint(),
|
|
aux.attr,partnerno);
|
|
}
|
|
AddPointToEdgeArray(hsInside.GetLeftPoint(),aux,
|
|
window, pointsOnEdge);
|
|
if (hs.GetRightPoint()!=hsInside.GetRightPoint()){
|
|
//Add the part of the half segment composed by the right
|
|
//point of hs and the right point of hsInside.
|
|
clippedRegion.AddClippedHS(hsInside.GetRightPoint(),
|
|
aux.GetRightPoint(),
|
|
aux.attr,partnerno);
|
|
}
|
|
AddPointToEdgeArray(hsInside.GetRightPoint(),aux,
|
|
window, pointsOnEdge);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HalfSegment aux( hs );
|
|
clippedRegion.AddClippedHS(aux.GetLeftPoint(),
|
|
aux.GetRightPoint(),
|
|
aux.attr,partnerno);
|
|
}
|
|
}
|
|
SelectNext();
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::GetClippedHS(const Rectangle<2> &window,
|
|
RegionT<Array2> &clippedRegion,
|
|
bool inside,
|
|
const Geoid* geoid/*=0*/) const
|
|
{
|
|
std::vector<EdgePoint> pointsOnEdge[4];
|
|
//upper edge, right edge, bottom, left
|
|
int partnerno=0;
|
|
|
|
clippedRegion.StartBulkLoad();
|
|
|
|
if (inside){
|
|
GetClippedHSIn(window,clippedRegion,pointsOnEdge,partnerno);
|
|
} else {
|
|
GetClippedHSOut(window,clippedRegion,pointsOnEdge,partnerno);
|
|
}
|
|
|
|
Point bPoint,ePoint;
|
|
bPoint.Set(window.MinD(0),window.MaxD(1)); //left-top
|
|
ePoint.Set(window.MaxD(0),window.MaxD(1)); //right-top
|
|
CreateNewSegments(pointsOnEdge[WTOP],clippedRegion,bPoint,ePoint,
|
|
WTOP,partnerno,inside);
|
|
bPoint.Set(window.MinD(0),window.MinD(1)); //left-bottom
|
|
ePoint.Set(window.MaxD(0),window.MinD(1)); //right-bottom
|
|
CreateNewSegments(pointsOnEdge[WBOTTOM],clippedRegion,bPoint,ePoint,
|
|
WBOTTOM,partnerno,inside);
|
|
bPoint.Set(window.MinD(0),window.MinD(1)); //left-bottom
|
|
ePoint.Set(window.MinD(0),window.MaxD(1)); //left-top
|
|
CreateNewSegments(pointsOnEdge[WLEFT],clippedRegion,bPoint,ePoint,
|
|
WLEFT,partnerno,inside);
|
|
bPoint.Set(window.MaxD(0),window.MinD(1)); //right-bottom
|
|
ePoint.Set(window.MaxD(0),window.MaxD(1)); //right-top
|
|
CreateNewSegments(pointsOnEdge[WRIGHT],clippedRegion,bPoint,ePoint,
|
|
WRIGHT,partnerno,inside);
|
|
CreateNewSegmentsWindowVertices(window, pointsOnEdge,clippedRegion,
|
|
partnerno,inside);
|
|
clippedRegion.EndBulkLoad();
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::IsCriticalPoint( const Point &adjacentPoint,
|
|
const int hsPosition ) const
|
|
{
|
|
int adjPosition = hsPosition,
|
|
adjacencyNo = 0,
|
|
step = 1;
|
|
do
|
|
{
|
|
HalfSegment adjCHS;
|
|
adjPosition+=step;
|
|
if ( adjPosition<0 || adjPosition>=this->Size())
|
|
break;
|
|
Get(adjPosition,adjCHS);
|
|
if (!adjCHS.IsLeftDomPoint())
|
|
continue;
|
|
AttrType attr = adjCHS.GetAttr();
|
|
//When looking for critical points, the partner of
|
|
//the adjacent half segment found
|
|
//cannot be consired.
|
|
if (attr.partnerno == hsPosition)
|
|
continue;
|
|
if ( ( adjacentPoint==adjCHS.GetLeftPoint() ) ||
|
|
( adjacentPoint==adjCHS.GetRightPoint() ) )
|
|
adjacencyNo++;
|
|
else
|
|
{
|
|
if (step==-1)
|
|
return false;
|
|
step=-1;
|
|
adjPosition=hsPosition;
|
|
}
|
|
}
|
|
while (adjacencyNo<2);
|
|
|
|
return (adjacencyNo>1);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::GetAdjacentHS( const HalfSegment &hs,
|
|
const int hsPosition,
|
|
int &position,
|
|
const int partnerno,
|
|
const int partnernoP,
|
|
HalfSegment& adjacentCHS,
|
|
const Point &adjacentPoint,
|
|
Point &newAdjacentPoint,
|
|
bool *cycle,
|
|
int step) const
|
|
{
|
|
bool adjacencyFound=false;
|
|
do
|
|
{
|
|
position+=step;
|
|
if ( position<0 || position>=this->Size()){
|
|
// no halfsegments found in this direction
|
|
break;
|
|
}
|
|
|
|
Get(position,adjacentCHS);
|
|
if (partnernoP == position){
|
|
// back direction of the original
|
|
continue;
|
|
}
|
|
|
|
if ( adjacentPoint==adjacentCHS.GetLeftPoint() ){
|
|
if (!cycle[position]){
|
|
newAdjacentPoint = adjacentCHS.GetRightPoint();
|
|
adjacencyFound = true;
|
|
}
|
|
}
|
|
else if ( adjacentPoint==adjacentCHS.GetRightPoint() ){
|
|
if (!cycle[position]){
|
|
newAdjacentPoint = adjacentCHS.GetLeftPoint();
|
|
adjacencyFound = true;
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
while (!adjacencyFound);
|
|
|
|
// cout<<"adjacencyFound "<<adjacencyFound<<endl;
|
|
|
|
return adjacencyFound;
|
|
}
|
|
/*
|
|
The parameter ~hasCriticalPoint~ indicates that the cycle that
|
|
is been computed has a critical point.
|
|
|
|
*/
|
|
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::ComputeCycle( HalfSegment &hs,
|
|
int faceno,
|
|
int cycleno,
|
|
int &edgeno,
|
|
bool *cycle )
|
|
{
|
|
|
|
Point nextPoint = hs.GetLeftPoint(),
|
|
lastPoint = hs.GetRightPoint(),
|
|
previousPoint, currentCriticalPoint(false);
|
|
AttrType attr, attrP;
|
|
HalfSegment hsP;
|
|
std::vector<SCycle> sCycleVector;
|
|
SCycle *s=NULL;
|
|
|
|
do
|
|
{
|
|
if (s==NULL)
|
|
{
|
|
//Update attributes
|
|
attr = hs.GetAttr();
|
|
|
|
Get(attr.partnerno,hsP);
|
|
attrP = hsP.GetAttr();
|
|
|
|
attr.faceno=faceno;
|
|
attr.cycleno=cycleno;
|
|
attr.edgeno=edgeno;
|
|
|
|
UpdateAttr(attrP.partnerno,attr);
|
|
|
|
attrP.faceno=faceno;
|
|
attrP.cycleno=cycleno;
|
|
attrP.edgeno=edgeno;
|
|
UpdateAttr(attr.partnerno,attrP);
|
|
|
|
edgeno++;
|
|
|
|
cycle[attr.partnerno]=true;
|
|
cycle[attrP.partnerno]=true;
|
|
|
|
if (this->IsCriticalPoint(nextPoint,attrP.partnerno)) {
|
|
currentCriticalPoint = nextPoint;
|
|
}
|
|
|
|
s = new SCycle(hs,attr.partnerno,hsP,attrP.partnerno,
|
|
currentCriticalPoint,nextPoint);
|
|
}
|
|
HalfSegment adjacentCHS;
|
|
Point adjacentPoint;
|
|
bool adjacentPointFound=false;
|
|
previousPoint = nextPoint;
|
|
if (s->goToCHS1Right)
|
|
{
|
|
s->goToCHS1Right=GetAdjacentHS(s->hs1,
|
|
s->hs2Partnerno,
|
|
s->hs1PosRight,
|
|
s->hs1Partnerno,
|
|
s->hs2Partnerno,adjacentCHS,
|
|
previousPoint,
|
|
nextPoint,
|
|
cycle,
|
|
1);
|
|
adjacentPointFound=s->goToCHS1Right;
|
|
// cout<<"flag 1 "<<adjacentPointFound<<" p1 "<<previousPoint<<endl;
|
|
}
|
|
if ( !adjacentPointFound && s->goToCHS1Left )
|
|
{
|
|
s->goToCHS1Left=GetAdjacentHS(s->hs1,
|
|
s->hs2Partnerno,
|
|
s->hs1PosLeft,
|
|
s->hs1Partnerno,
|
|
s->hs2Partnerno,
|
|
adjacentCHS,
|
|
previousPoint,
|
|
nextPoint,
|
|
cycle,
|
|
-1);
|
|
adjacentPointFound=s->goToCHS1Left;
|
|
//cout<<"flag 2 "<<adjacentPointFound<<" p2 "<<previousPoint<<endl;
|
|
}
|
|
if (!adjacentPointFound && s->goToCHS2Right)
|
|
{
|
|
s->goToCHS2Right=GetAdjacentHS(s->hs2,
|
|
s->hs1Partnerno,
|
|
s->hs2PosRight,
|
|
s->hs2Partnerno,
|
|
s->hs1Partnerno,
|
|
adjacentCHS,
|
|
previousPoint,
|
|
nextPoint,
|
|
cycle,
|
|
1);
|
|
adjacentPointFound=s->goToCHS2Right;
|
|
//cout<<"flag 3 "<<adjacentPointFound<<" p3 "<<previousPoint<<endl;
|
|
}
|
|
if (!adjacentPointFound && s->goToCHS2Left)
|
|
{
|
|
s->goToCHS2Left=GetAdjacentHS(s->hs2,
|
|
s->hs1Partnerno,
|
|
s->hs2PosLeft,
|
|
s->hs2Partnerno,
|
|
s->hs1Partnerno,
|
|
adjacentCHS,
|
|
previousPoint,
|
|
nextPoint,
|
|
cycle,
|
|
-1);
|
|
adjacentPointFound = s->goToCHS2Left;
|
|
|
|
// cout<<"flag 4 "<<adjacentPointFound<<" p4 "<<previousPoint<<endl;
|
|
|
|
}
|
|
|
|
if(!adjacentPointFound){
|
|
std::cerr<<"previousPoint "<<previousPoint<<std::endl;
|
|
std::cerr << "Problem in rebuilding cycle in a region " << std::endl;
|
|
std::cerr << "no adjacent point found" << std::endl;
|
|
std::cerr << "Halfsegments : --------------- " << std::endl;
|
|
HalfSegment hs;
|
|
for(int i=0;i<Size();i++){
|
|
Get(i,hs);
|
|
std::cerr << i << " : " << (hs) << std::endl;
|
|
}
|
|
throw(0);
|
|
}
|
|
sCycleVector.push_back(*s);
|
|
|
|
if ((currentCriticalPoint.IsDefined()) && currentCriticalPoint==nextPoint)
|
|
{
|
|
//The critical point defines a cycle, so it is need to
|
|
//remove the segments
|
|
//from the vector, and set the segment as not visited in thei
|
|
// cycle array.
|
|
//FirsAux is the first half segment with the critical point equals to
|
|
//criticalPoint.
|
|
SCycle sAux,firstSCycle;
|
|
|
|
do
|
|
{
|
|
sAux=sCycleVector.back();
|
|
sCycleVector.pop_back();
|
|
firstSCycle=sCycleVector.back();
|
|
if (!firstSCycle.criticalPoint.IsDefined())
|
|
break;
|
|
if (firstSCycle.criticalPoint!=currentCriticalPoint)
|
|
break;
|
|
cycle[sAux.hs1Partnerno]=false;
|
|
cycle[sAux.hs2Partnerno]=false;
|
|
edgeno--;
|
|
}while(sCycleVector.size()>1);
|
|
delete s; //when s is deleted, the critical point is also deleted.
|
|
s = 0;
|
|
if (sCycleVector.size()==1)
|
|
{
|
|
sCycleVector.pop_back();
|
|
if(s){
|
|
delete s;
|
|
}
|
|
s = new SCycle(firstSCycle);
|
|
}
|
|
else{
|
|
if(s){
|
|
delete s;
|
|
}
|
|
s= new SCycle(sAux);
|
|
}
|
|
hs = s->hs1;
|
|
currentCriticalPoint=s->criticalPoint;
|
|
nextPoint=s->nextPoint;
|
|
continue;
|
|
}
|
|
|
|
if ( nextPoint==lastPoint )
|
|
{
|
|
//Update attributes
|
|
attr = adjacentCHS.GetAttr();
|
|
|
|
Get(attr.partnerno,hsP);
|
|
attrP = hsP.GetAttr();
|
|
|
|
attr.faceno=faceno;
|
|
attr.cycleno=cycleno;
|
|
attr.edgeno=edgeno;
|
|
|
|
UpdateAttr(attrP.partnerno,attr);
|
|
|
|
attrP.faceno=faceno;
|
|
attrP.cycleno=cycleno;
|
|
attrP.edgeno=edgeno;
|
|
UpdateAttr(attr.partnerno,attrP);
|
|
|
|
edgeno++;
|
|
|
|
cycle[attr.partnerno]=true;
|
|
cycle[attrP.partnerno]=true;
|
|
|
|
break;
|
|
}
|
|
hs = adjacentCHS;
|
|
delete s;
|
|
s=NULL;
|
|
}
|
|
while(1);
|
|
if(s){
|
|
delete s;
|
|
s = 0;
|
|
}
|
|
|
|
}
|
|
|
|
//This function returns the value of the attribute inside above of
|
|
//the first half segment under the half segment hsS.
|
|
template<template<typename T>class Array>
|
|
int RegionT<Array>::GetNewFaceNo(HalfSegment &hsS, bool *cycle)
|
|
{
|
|
int coverno=0;
|
|
int startpos=0;
|
|
double y0;
|
|
AttrType attr;
|
|
std::vector<HalfSegment> v;
|
|
|
|
//1. find the right place by binary search
|
|
Find( hsS, startpos );
|
|
|
|
int hsVisiteds=0;
|
|
|
|
//2. deal with equal-x hs's
|
|
//To verify if it is need to deal with this
|
|
|
|
attr = hsS.GetAttr();
|
|
coverno = attr.coverageno;
|
|
|
|
//search the region value for coverageno steps
|
|
int touchedNo=0;
|
|
HalfSegment hs;
|
|
const Point& p = hsS.GetLeftPoint();
|
|
|
|
int i=startpos;
|
|
while (( i>=0)&&(touchedNo<coverno))
|
|
{
|
|
this->Get(i, hs);
|
|
hsVisiteds++;
|
|
|
|
if ( (cycle[i]) && (hs.IsLeftDomPoint()) &&
|
|
( (hs.GetLeftPoint().GetX() <= p.GetX()) &&
|
|
(p.GetX() <= hs.GetRightPoint().GetX()) ))
|
|
{
|
|
touchedNo++;
|
|
if (!hs.RayAbove(p, y0))
|
|
v.push_back(hs);
|
|
}
|
|
i--; //the iterator
|
|
}
|
|
if (v.size()==0)
|
|
return -1; //the new face number will be the last face number +1
|
|
else
|
|
{
|
|
sort(v.begin(),v.end());
|
|
//The first half segment is the next half segment above hsS
|
|
HalfSegment hs = v[v.size()-1];
|
|
attr = hs.GetAttr();
|
|
if (attr.insideAbove)
|
|
return attr.faceno; //the new cycle is a cycle of the face ~attr.faceno~
|
|
else
|
|
return -1; //new face
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
int RegionT<Array>::GetNewFaceNo(const HalfSegment& hsIn,
|
|
const int startpos) const {
|
|
|
|
// Precondition:
|
|
// hsIn is the smallest (in halfsegment-order) segment of a cycle.
|
|
// startpos is the index of hsIn in the DBArray.
|
|
|
|
if (hsIn.GetAttr().insideAbove) {
|
|
|
|
// hsIn belongs to a new face:
|
|
return -1;
|
|
}
|
|
|
|
// Now we know hsIn belongs to a new hole and we
|
|
// have to encounter the enclosing face.
|
|
// This is done by searching the next halfsegment maxHS 'under' hsIn.
|
|
// Since we go downwards, the facenumber of maxHS must be already known
|
|
// and is equal to the facenumber of hsIn.
|
|
|
|
double y0;
|
|
double maxY0;
|
|
HalfSegment hs;
|
|
HalfSegment maxHS;
|
|
bool hasMax = false;
|
|
const Point& p = hsIn.GetLeftPoint();
|
|
const int coverno = hsIn.GetAttr().coverageno;
|
|
int touchedNo = 0;
|
|
int i = startpos - 1;
|
|
bool first = true;
|
|
|
|
while (i >=0 && touchedNo < coverno) {
|
|
|
|
Get(i, hs);
|
|
|
|
if (!hs.IsLeftDomPoint()) {
|
|
|
|
i--;
|
|
continue;
|
|
}
|
|
|
|
if (hs.GetLeftPoint().GetX() <= p.GetX() &&
|
|
p.GetX() <= hs.GetRightPoint().GetX()) {
|
|
|
|
touchedNo++;
|
|
}
|
|
|
|
if (!AlmostEqual(hs.GetRightPoint().GetX(), p.GetX()) &&
|
|
hs.RayDown(p, y0)) {
|
|
|
|
if (first ||
|
|
y0 > maxY0 ||
|
|
(AlmostEqual(y0, maxY0) && hs > maxHS)) {
|
|
|
|
// To find the first halfsegment 'under' hsIn
|
|
// we compare them as follows:
|
|
// (1) y-value of the intersection point between a ray down
|
|
// from the left point of hsIn and hs.
|
|
// (2) halfsegment order.
|
|
|
|
maxY0 = y0;
|
|
maxHS = hs;
|
|
first = false;
|
|
hasMax = true;
|
|
}
|
|
}
|
|
|
|
i--;
|
|
}
|
|
|
|
if (!hasMax) {
|
|
|
|
std::cerr << "Problem in rebuilding cycle in a region " << std::endl;
|
|
std::cerr << "No outer cycle found" << std::endl;
|
|
std::cerr << "hsIn: " << hsIn << std::endl;
|
|
std::cerr << "Halfsegments : --------------- " << std::endl;
|
|
HalfSegment hs;
|
|
|
|
for(int i=0;i<Size();i++) {
|
|
|
|
Get(i,hs);
|
|
std::cerr << i << " : " << (hs) << std::endl;
|
|
}
|
|
|
|
throw 0;
|
|
}
|
|
|
|
//the new cycle is a holecycle of the face ~maxHS.attr.faceno~
|
|
return maxHS.GetAttr().faceno;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::ComputeRegion()
|
|
{
|
|
if( !IsDefined() )
|
|
return;
|
|
//array that stores in position i the last cycle number of the face i
|
|
std::vector<int> face;
|
|
//array that stores in the position ~i~ if the half
|
|
//segment hi had already the face
|
|
//number, the cycle number and the edge number
|
|
//attributes set properly, in other words,
|
|
//it means that hi is already part of a cycle
|
|
bool *cycle;
|
|
int lastfaceno=0,
|
|
faceno=0,
|
|
cycleno = 0,
|
|
edgeno = 0;
|
|
bool isFirstCHS=true;
|
|
HalfSegment hs;
|
|
|
|
if (Size()==0)
|
|
return;
|
|
//Insert in the vector the first cycle of the first face
|
|
face.push_back(0);
|
|
cycle = new bool[Size()];
|
|
#ifdef SECONDO_MAC_OSX
|
|
// something goes wrong at mac osx and the memset function
|
|
int size = Size();
|
|
for(int i=0;i<size;i++){
|
|
cycle[i] = false;
|
|
}
|
|
#else
|
|
memset( cycle, 0, Size()*sizeof(bool) );
|
|
#endif
|
|
for ( int i=0; i<Size(); i++)
|
|
{
|
|
Get(i,hs);
|
|
HalfSegment aux(hs);
|
|
if ( aux.IsLeftDomPoint() && !cycle[i])
|
|
{
|
|
if(!isFirstCHS)
|
|
{
|
|
int facenoAux = GetNewFaceNo(aux,i);
|
|
if (facenoAux==-1)
|
|
{/*The lhs half segment will start a new face*/
|
|
lastfaceno++;
|
|
faceno = lastfaceno;
|
|
/*to store the first cycle number of the face lastFace*/
|
|
face.push_back(0);
|
|
cycleno = 0;
|
|
edgeno = 0;
|
|
}
|
|
else
|
|
{ /*The half segment ~hs~ belongs to an existing face*/
|
|
faceno = facenoAux;
|
|
face[faceno]++;
|
|
cycleno = face[faceno];
|
|
edgeno = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
isFirstCHS = false;
|
|
}
|
|
ComputeCycle(aux, faceno,cycleno, edgeno, cycle);
|
|
}
|
|
}
|
|
delete [] cycle;
|
|
noComponents = lastfaceno + 1;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::WindowClippingIn(const Rectangle<2> &window,
|
|
RegionT<Array2> &clippedRegion,
|
|
const Geoid* geoid/*=0*/) const
|
|
{
|
|
clippedRegion.Clear();
|
|
if( !IsDefined() || !window.IsDefined() || (geoid && !geoid->IsDefined()) ) {
|
|
clippedRegion.SetDefined( false );
|
|
return;
|
|
}
|
|
clippedRegion.SetDefined( true );
|
|
//cout<<endl<<"Original: "<<*this<<endl;
|
|
if (!this->bbox.Intersects(window,geoid)){
|
|
return;
|
|
}
|
|
//If the bounding box of the region is inside the window,
|
|
//then the clippedRegion
|
|
//is equal to the region been clipped.
|
|
|
|
if (window.Contains(this->bbox,geoid)){
|
|
clippedRegion = *this;
|
|
} else {
|
|
//cout<<endl<<this->bbox<<endl;
|
|
this->GetClippedHS(window,clippedRegion,true, geoid);
|
|
//cout<<endl<<"Clipped HS:"<<endl<<clippedRegion;
|
|
clippedRegion.ComputeRegion();
|
|
}
|
|
//cout<<endl<<"Clipped;"<<clippedRegion;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::WindowClippingOut(const Rectangle<2> &window,
|
|
RegionT<Array2> &clippedRegion,
|
|
const Geoid* geoid/*=0*/) const
|
|
{
|
|
clippedRegion.Clear();
|
|
if( !IsDefined() || !window.IsDefined() || (geoid && !geoid->IsDefined()) ) {
|
|
clippedRegion.SetDefined( false );
|
|
return;
|
|
}
|
|
clippedRegion.SetDefined( true );
|
|
//If the bounding box of the region is inside the window,
|
|
//then the clipped region is empty
|
|
//cout<<"region: "<<*this<<endl;
|
|
if (window.Contains(this->bbox,geoid)){
|
|
return;
|
|
}
|
|
if (!window.Intersects(this->bbox,geoid)){
|
|
clippedRegion = *this;
|
|
} else {
|
|
this->GetClippedHS(window,clippedRegion,false,geoid);
|
|
// cout<<endl<<"Clipped HS:"<<endl<<clippedRegion;
|
|
clippedRegion.ComputeRegion();
|
|
}
|
|
// cout<<endl<<"clippedRegion: "<<clippedRegion;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
size_t RegionT<Array>::HashValue() const
|
|
{
|
|
//cout<<"cregion hashvalue1*******"<<endl;
|
|
if(IsEmpty()) // subsumes !IsDefined()
|
|
return 0;
|
|
|
|
unsigned long h=0;
|
|
HalfSegment hs;
|
|
Coord x1, y1;
|
|
Coord x2, y2;
|
|
|
|
for( int i = 0; ((i < Size())&&(i<5)); i++ )
|
|
{
|
|
Get( i, hs );
|
|
x1=hs.GetLeftPoint().GetX();
|
|
y1=hs.GetLeftPoint().GetY();
|
|
|
|
x2=hs.GetRightPoint().GetX();
|
|
y2=hs.GetRightPoint().GetY();
|
|
h=h+(unsigned long)((5*x1 + y1)+ (5*x2 + y2));
|
|
}
|
|
return size_t(h);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::Clear()
|
|
{
|
|
region.clean();
|
|
pos = -1;
|
|
ordered = true;
|
|
bbox.SetDefined(false);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::SetDefined(bool defined) {
|
|
if(!defined){
|
|
Clear();
|
|
}
|
|
Attribute::SetDefined( defined );
|
|
}
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::SetEmpty()
|
|
{
|
|
region.clean();
|
|
pos = -1;
|
|
ordered = true;
|
|
bbox.SetDefined(false);
|
|
SetDefined(true);
|
|
}
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::CopyFrom( const Attribute* right )
|
|
{
|
|
*this = *(const RegionT<Array> *)right;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
void RegionT<Array>::CopyFrom( const RegionT<Array2>* right )
|
|
{
|
|
*this = *right;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<template<typename T2>class Array2>
|
|
int RegionT<Array>::Compare( const RegionT<Array2>* cr ) const{
|
|
if ( !cr )
|
|
return -2;
|
|
|
|
if (!IsDefined() && (!cr->IsDefined())){
|
|
return 0;
|
|
}
|
|
|
|
if(!IsDefined()){
|
|
return -1;
|
|
}
|
|
if(!cr->IsDefined()){
|
|
return 1;
|
|
}
|
|
if(Size()<cr->Size()){
|
|
return -1;
|
|
}
|
|
if(Size()>cr->Size()){
|
|
return 1;
|
|
}
|
|
if(Size()==0){ // two empty regions
|
|
return 0;
|
|
}
|
|
|
|
int bboxCmp = bbox.Compare( &cr->bbox );
|
|
if(bboxCmp!=0){
|
|
return bboxCmp;
|
|
}
|
|
|
|
HalfSegment hs1, hs2;
|
|
for( int i = 0; i < Size(); i++ ) {
|
|
Get( i, hs1);
|
|
cr->Get( i, hs2 );
|
|
int hsCmp = hs1.Compare(hs2);
|
|
if(hsCmp!=0){
|
|
return hsCmp;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
template<template<typename T>class Array>
|
|
int RegionT<Array>::Compare( const Attribute* arg ) const
|
|
{
|
|
RegionT<Array>* cr = (RegionT<Array>* )(arg);
|
|
return Compare(cr);
|
|
}
|
|
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
std::ostream& RegionT<Array>::Print( std::ostream &os ) const
|
|
{
|
|
os << "<";
|
|
if( !IsDefined() ) {
|
|
os << " undefined ";
|
|
} else {
|
|
HalfSegment hs;
|
|
for( int i = 0; i < Size(); i++ )
|
|
{
|
|
Get( i, hs );
|
|
os << " " << hs;
|
|
}
|
|
}
|
|
os << ">";
|
|
return os;
|
|
}
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
RegionT<Array> *RegionT<Array>::Clone() const
|
|
{
|
|
return new RegionT( *this);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
bool RegionT<Array>::InsertOk( const HalfSegment& hs ) const
|
|
{
|
|
HalfSegment auxhs;
|
|
double dummyy0;
|
|
|
|
// Uncommenting out the following line will increase performance when
|
|
// bulk importing correct data:
|
|
// CD: Problem: Database BerlinTest is currently faulty and cannot be
|
|
// restored when checking is enabled
|
|
return true;
|
|
|
|
int prevcycleMeet[50];
|
|
|
|
int prevcyclenum=0;
|
|
for( int i = 0; i < 50; i++ )
|
|
prevcycleMeet[i]=0;
|
|
|
|
for( int i = 0; i<= region.Size()-1; i++ )
|
|
{
|
|
region.Get( i, auxhs );
|
|
|
|
if (auxhs.IsLeftDomPoint())
|
|
{
|
|
if (hs.Intersects(auxhs))
|
|
{
|
|
if ((hs.attr.faceno!=auxhs.attr.faceno)||
|
|
(hs.attr.cycleno!=auxhs.attr.cycleno))
|
|
{
|
|
cout<<"two cycles intersect with the ";
|
|
cout<<"following edges:";
|
|
cout<<auxhs<<" :: "<<hs<<endl;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if ((auxhs.GetLeftPoint()!=hs.GetLeftPoint()) &&
|
|
(auxhs.GetLeftPoint()!=hs.GetRightPoint()) &&
|
|
(auxhs.GetRightPoint()!=hs.GetLeftPoint()) &&
|
|
(auxhs.GetRightPoint()!=hs.GetRightPoint()))
|
|
{
|
|
cout<<"two edges: " <<auxhs<<" :: "<< hs
|
|
<<" of the same cycle intersect in middle!"
|
|
<<endl;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((hs.attr.cycleno>0) &&
|
|
(auxhs.attr.faceno==hs.attr.faceno) &&
|
|
(auxhs.attr.cycleno!=hs.attr.cycleno))
|
|
{
|
|
if (auxhs.RayAbove(hs.GetLeftPoint(), dummyy0))
|
|
{
|
|
prevcycleMeet[auxhs.attr.cycleno]++;
|
|
if (prevcyclenum < auxhs.attr.cycleno)
|
|
prevcyclenum=auxhs.attr.cycleno;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((hs.attr.cycleno>0))
|
|
{
|
|
if (prevcycleMeet[0] % 2 ==0)
|
|
{
|
|
cout<<"hole(s) is not inside the outer cycle! "<<endl;
|
|
return false;
|
|
}
|
|
for (int i=1; i<=prevcyclenum; i++)
|
|
{
|
|
if (prevcycleMeet[i] % 2 !=0)
|
|
{
|
|
cout<<"one hole is inside another! "<<endl;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
Now we know that the new half segment is not inside any other previous holes
|
|
of the same face. However, whether this new hole contains any previous hole
|
|
of the same face is not clear. In the following we do this kind of check.
|
|
|
|
*/
|
|
|
|
if (((hs.attr.faceno>0) || (hs.attr.cycleno>2)))
|
|
{
|
|
HalfSegment hsHoleNEnd, hsHoleNStart;
|
|
|
|
if (region.Size() ==0) return true;
|
|
|
|
int holeNEnd=region.Size()-1;
|
|
region.Get(holeNEnd, hsHoleNEnd );
|
|
|
|
if ((hsHoleNEnd.attr.cycleno>1) &&
|
|
((hs.attr.faceno!=hsHoleNEnd.attr.faceno)||
|
|
(hs.attr.cycleno!=hsHoleNEnd.attr.cycleno)))
|
|
{
|
|
if (hsHoleNEnd.attr.cycleno>1)
|
|
{
|
|
int holeNStart=holeNEnd - 1;
|
|
region.Get(holeNStart, hsHoleNStart );
|
|
|
|
while ((hsHoleNStart.attr.faceno==hsHoleNEnd.attr.faceno) &&
|
|
(hsHoleNStart.attr.cycleno==hsHoleNEnd.attr.cycleno)&&
|
|
(holeNStart>0))
|
|
{
|
|
holeNStart--;
|
|
region.Get(holeNStart, hsHoleNStart );
|
|
}
|
|
holeNStart++;
|
|
|
|
int prevHolePnt=holeNStart-1;
|
|
HalfSegment hsPrevHole, hsLastHole;
|
|
|
|
bool stillPrevHole = true;
|
|
while ((stillPrevHole) && (prevHolePnt>=0))
|
|
{
|
|
region.Get(prevHolePnt, hsPrevHole );
|
|
prevHolePnt--;
|
|
|
|
if ((hsPrevHole.attr.faceno!= hsHoleNEnd.attr.faceno)||
|
|
(hsPrevHole.attr.cycleno<=0))
|
|
{
|
|
stillPrevHole=false;
|
|
}
|
|
|
|
if (hsPrevHole.IsLeftDomPoint())
|
|
{
|
|
int holeNMeent=0;
|
|
for (int i=holeNStart; i<=holeNEnd; i++)
|
|
{
|
|
region.Get(i, hsLastHole );
|
|
if ((hsLastHole.IsLeftDomPoint())&&
|
|
(hsLastHole.RayAbove
|
|
(hsPrevHole.GetLeftPoint(), dummyy0)))
|
|
holeNMeent++;
|
|
}
|
|
if (holeNMeent % 2 !=0)
|
|
{
|
|
cout<<"one hole is inside another!!! "<<endl;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::vector<Point> getCycle(const bool isHole,
|
|
const std::vector<HalfSegment>& vhs);
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
std::vector< std::vector <Point> > getCycles(const RegionT<Array>& reg){
|
|
// first step , map halsfsegment according to faceno and cycleno
|
|
|
|
std::map< std::pair<int, int> , std::vector<HalfSegment> > m;
|
|
|
|
HalfSegment hs;
|
|
for(int i=0;i<reg.Size(); i++){
|
|
reg.Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
int faceno = hs.attr.faceno;
|
|
int cycleno = hs.attr.cycleno;
|
|
m[std::make_pair(faceno, cycleno)].push_back(hs);
|
|
}
|
|
}
|
|
|
|
std::vector< std::vector <Point> > result;
|
|
std::map< std::pair<int, int> , std::vector<HalfSegment> >::iterator it;
|
|
for(it=m.begin(); it!=m.end(); it++){
|
|
std::pair< std::pair<int, int> ,
|
|
std::vector<HalfSegment> > cycleDesc = *it;
|
|
bool isHole = cycleDesc.first.second > 0;
|
|
result.push_back(getCycle(isHole, cycleDesc.second));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::saveShape(std::ostream& o, uint32_t RecNo) const
|
|
{
|
|
// first, write the record header
|
|
WinUnix::writeBigEndian(o,RecNo);
|
|
// an empty region
|
|
if(!IsDefined() || IsEmpty()){
|
|
uint32_t length = 2;
|
|
WinUnix::writeBigEndian(o,length);
|
|
uint32_t type = 0;
|
|
WinUnix::writeLittleEndian(o,type);
|
|
} else {
|
|
|
|
std::vector<std::vector < Point> > cycles = getCycles(*this);
|
|
|
|
uint32_t numParts = cycles.size();
|
|
|
|
|
|
uint32_t numPoints = 0;
|
|
|
|
std::vector<std::vector < Point> >::iterator it;
|
|
for(it = cycles.begin(); it!=cycles.end(); it++){
|
|
numPoints += it->size();
|
|
}
|
|
|
|
uint32_t numBytes = 44 + 4 * numParts + 16*numPoints;
|
|
|
|
uint32_t length = numBytes / 2;
|
|
|
|
|
|
WinUnix::writeBigEndian(o,length);
|
|
WinUnix::writeLittleEndian(o,getshpType()); // 4
|
|
double minX = getMinX();
|
|
double maxX = getMaxX();
|
|
double minY = getMinY();
|
|
double maxY = getMaxY();
|
|
// write the boundig box
|
|
WinUnix::writeLittle64(o,minX); // 8 * 4
|
|
WinUnix::writeLittle64(o,minY);
|
|
WinUnix::writeLittle64(o,maxX);
|
|
WinUnix::writeLittle64(o,maxY);
|
|
|
|
WinUnix::writeLittleEndian(o,numParts);
|
|
WinUnix::writeLittleEndian(o,numPoints);
|
|
|
|
// write the parts
|
|
uint32_t pos = 0;
|
|
for(unsigned int i=0; i<numParts; i++){
|
|
WinUnix::writeLittleEndian(o,pos);
|
|
pos += cycles[i].size();
|
|
}
|
|
// write the points
|
|
for(unsigned int i=0;i<numParts; i++){
|
|
std::vector<Point> cycle = cycles[i];
|
|
for(unsigned int j=0;j<cycle.size(); j++){
|
|
Point p = cycle[j];
|
|
double x = p.GetX();
|
|
double y = p.GetY();
|
|
WinUnix::writeLittle64(o,x);
|
|
WinUnix::writeLittle64(o,y);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
void RegionT<Array>::writeShape(std::ostream& o, uint32_t RecNo) const
|
|
{
|
|
saveShape(o,RecNo);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
template<class LineType>
|
|
double RegionT<Array>::Distance( const LineType &l,
|
|
const Geoid* geoid /*=0*/ ) const
|
|
{
|
|
assert( !IsEmpty() ); // subsumes IsDefined()
|
|
assert( !l.IsEmpty() ); // subsumes IsDefined()
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(geoid){
|
|
cout << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<< std::endl;
|
|
assert(false); // TODO: Implement spherical geometry case.
|
|
}
|
|
if( IsEmpty() || l.IsEmpty() || (geoid && !geoid->IsDefined()) ) {
|
|
return -1;
|
|
}
|
|
double result = std::numeric_limits<double>::max();
|
|
HalfSegment hs1, hs2;
|
|
for(int i=0; i<l.Size();i++){
|
|
l.Get(i,hs2);
|
|
if(hs2.IsLeftDomPoint()){
|
|
if(Contains(hs2.GetDomPoint(),geoid)){
|
|
return 0.0;
|
|
}
|
|
if(Contains(hs2.GetSecPoint(),geoid)){
|
|
return 0.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < Size(); i++ ){
|
|
Get( i, hs1 );
|
|
if( hs1.IsLeftDomPoint() ){
|
|
for( int j = 0; j < l.Size(); j++ ){
|
|
l.Get( j, hs2 );
|
|
if( hs2.IsLeftDomPoint() ){
|
|
if( hs1.Intersects( hs2 ) ){
|
|
return 0.0;
|
|
}
|
|
result = MIN( result, hs1.Distance( hs2, geoid ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|