3429 lines
98 KiB
C++
3429 lines
98 KiB
C++
/*
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2007,
|
|
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
|
|
----
|
|
|
|
//[_] [\_]
|
|
//[toc] [\setcounter{page}{1} \renewcommand{\thepage}{\Roman{page}}
|
|
\tableofcontents]
|
|
//[etoc][\clearpage \setcounter{page}{1} \renewcommand{\thepage}{\arabic{page}}]
|
|
|
|
//[title] [ \thispagestyle{empty} \title{TopOps-Algebra} \author{Thomas Behr} \maketitle]
|
|
//[times] [\ensuremath{\times}]
|
|
//[x] [\ensuremath{\times}]
|
|
//[->] [\ensuremath{\rightarrow}]
|
|
//[<=] [\ensuremath{\leq{}}]
|
|
//[>=] [\ensuremath{\ge{}}]
|
|
//[secondo] [\textsc{Secondo}]
|
|
//[{] [\}]
|
|
//[}] [\}]
|
|
|
|
[title]
|
|
[toc]
|
|
[etoc]
|
|
|
|
1 General Description
|
|
|
|
This algebra connects the TopRelAlgebra and the SpatialAlgebra.
|
|
It implements functions computing the topological
|
|
relationships of spatial values. Furthermore it provides some
|
|
functions checking whether two objects are part of a cluster which
|
|
is given by a name together with a predicategroup. Basically, this
|
|
can also be implemented by computing the topological relationship and
|
|
check whether the result is contained in the given cluster. The advantage of a
|
|
separate implementation is that we can exit the computation early in
|
|
many cases.
|
|
Additionally, this algebra provides some topological predicates using the
|
|
standard predicate group.
|
|
|
|
Moreover, in this algebra set operations for spatial types (union2,
|
|
intersection2, difference2, commonborder2) are implemented.
|
|
|
|
|
|
2 Includes, Constants, and Definitions
|
|
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <queue>
|
|
#include <iterator>
|
|
|
|
#include "NestedList.h"
|
|
#include "Algebra.h"
|
|
#include "QueryProcessor.h"
|
|
#include "LogMsg.h"
|
|
#include "Symbols.h"
|
|
|
|
#include "TopOpsAlgebra.h"
|
|
#include "Algebras/Spatial/SpatialAlgebra.h"
|
|
#include "AvlTree.h"
|
|
#include "TopRel.h"
|
|
#include "StandardTypes.h"
|
|
#include "Algebras/Spatial/AVLSegment.h"
|
|
|
|
#include "MMRTree.h"
|
|
|
|
|
|
|
|
|
|
/*
|
|
~A Macro useful for debugging ~
|
|
|
|
*/
|
|
|
|
//#define __TRACE__ cout << __FILE__ << "@" << __LINE__ << endl;
|
|
#define __TRACE__
|
|
|
|
|
|
|
|
extern NestedList* nl;
|
|
extern QueryProcessor* qp;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using namespace toprel;
|
|
using namespace std;
|
|
|
|
namespace topops{
|
|
|
|
/*
|
|
5 Some Auxiliary Functions
|
|
|
|
|
|
*/
|
|
|
|
inline void SetII(Int9M& m, const bool useCluster,
|
|
Cluster& cluster,bool& done){
|
|
if(!m.GetII()){
|
|
m.SetII(true);
|
|
if(useCluster){
|
|
cluster.Restrict(II,true,false);
|
|
done = done || cluster.isExtension(m) || cluster.IsEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void SetIB(Int9M& m, const bool useCluster,
|
|
Cluster& cluster, bool& done){
|
|
if(!m.GetIB()){
|
|
m.SetIB(true);
|
|
if(useCluster){
|
|
cluster.Restrict(IB,true,false);
|
|
done = done || cluster.isExtension(m)|| cluster.IsEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void SetIE(Int9M& m, const bool useCluster,
|
|
Cluster& cluster, bool& done){
|
|
if(!m.GetIE()){
|
|
m.SetIE(true);
|
|
if(useCluster){
|
|
cluster.Restrict(IE,true,false);
|
|
done = done || cluster.isExtension(m)|| cluster.IsEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void SetBI(Int9M& m, const bool useCluster,
|
|
Cluster& cluster, bool& done){
|
|
if(!m.GetBI()){
|
|
m.SetBI(true);
|
|
if(useCluster){
|
|
cluster.Restrict(BI,true,false);
|
|
done = done || cluster.isExtension(m)|| cluster.IsEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void SetBB(Int9M& m, const bool useCluster,
|
|
Cluster& cluster, bool& done){
|
|
if(!m.GetBB()){
|
|
m.SetBB(true);
|
|
if(useCluster){
|
|
cluster.Restrict(BB,true,false);
|
|
done = done || cluster.isExtension(m)|| cluster.IsEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void SetBE(Int9M& m, const bool useCluster,
|
|
Cluster& cluster, bool& done){
|
|
if(!m.GetBE()){
|
|
m.SetBE(true);
|
|
if(useCluster){
|
|
cluster.Restrict(BE,true,false);
|
|
done = done || cluster.isExtension(m)|| cluster.IsEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void SetEI(Int9M& m, const bool useCluster,
|
|
Cluster& cluster, bool& done){
|
|
if(!m.GetEI()){
|
|
m.SetEI(true);
|
|
if(useCluster){
|
|
cluster.Restrict(EI,true,false);
|
|
done = done || cluster.isExtension(m)|| cluster.IsEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void SetEB(Int9M& m, const bool useCluster,
|
|
Cluster& cluster, bool& done){
|
|
if(!m.GetEB()){
|
|
m.SetEB(true);
|
|
if(useCluster){
|
|
cluster.Restrict(EB,true,false);
|
|
done = done || cluster.isExtension(m)|| cluster.IsEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void SetEE(Int9M& m, const bool useCluster,
|
|
Cluster& cluster, bool& done){
|
|
if(!m.GetEE()){
|
|
m.SetEE(true);
|
|
if(useCluster){
|
|
cluster.Restrict(EE,true,false);
|
|
done = done || cluster.isExtension(m)|| cluster.IsEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
7 Computation of the 9 intersection matrices
|
|
|
|
The following functions compute the 9 intersection matrices for
|
|
different combinations of spatial data types.
|
|
|
|
|
|
7.1 ~point~ [x] ~point~
|
|
|
|
|
|
This function computes the 9-intersection matrix between two point values.
|
|
Because a single point is very simple, no bounding box tests are
|
|
performed.
|
|
|
|
Complexity: O(1)
|
|
|
|
*/
|
|
|
|
bool GetInt9M(Point* p1 , Point* p2,Int9M& res,
|
|
const bool useCluster= false,
|
|
Cluster cluster = Cluster()){
|
|
res.SetValue(0);
|
|
// in each case, the exteriors intersect
|
|
res.SetEE(true);
|
|
if(AlmostEqual(*p1,*p2)){
|
|
res.SetII(true);
|
|
}else{
|
|
res.SetIE(true);
|
|
res.SetEI(true);
|
|
}
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
7.2 ~point~ [x] ~points~
|
|
|
|
|
|
The next function computes the 9 intersection matrix between a
|
|
point value and a points value.
|
|
|
|
Complexity: O(log(n) + 1) where ~n~ is the number of points in the __ps__
|
|
value.
|
|
|
|
*/
|
|
bool GetInt9M(Points* ps, Point* p,Int9M& res,
|
|
const bool useCluster=false,
|
|
Cluster cluster = Cluster()){
|
|
|
|
// initialization
|
|
res.SetValue(0);
|
|
res.SetEE(true); // holds always for bounded objects
|
|
|
|
// check for emptyness
|
|
if(ps->IsEmpty()){ // the simples case
|
|
res.SetEI(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// bounding box check
|
|
Rectangle<2> box_ps = ps->BoundingBox();
|
|
Rectangle<2> box_p = p->BoundingBox();
|
|
if(!box_p.Intersects(box_ps)){ // disjointness of the bounding boxes
|
|
res.SetIE(true);
|
|
res.SetEI(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
int size = ps->Size();
|
|
if(size>1){
|
|
res.SetIE(true);
|
|
if(useCluster){
|
|
cluster.Restrict(IE,true,false);
|
|
if(cluster.IsEmpty()){
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if(!(ps->Contains(*p))){ // Contains uses binary search
|
|
res.SetEI(true);
|
|
res.SetIE(true);
|
|
} else{
|
|
res.SetII(true);
|
|
}
|
|
|
|
|
|
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
7.3 ~points~ [x] ~points~
|
|
|
|
This function computes the 9 intersection matrix describing the
|
|
topological relationship between two __points__ values.
|
|
|
|
Complexity: O(~n~+~m~ + 1) , where ~n~ and ~m~ is the size of ~ps~1 and
|
|
~ps~2 respectively.
|
|
|
|
If ~useCluster~ is set to be __false__, the return value is always
|
|
__true__. In this case, the parameter __res__ will contain the
|
|
9 intersection matrix describing the topological relationship between
|
|
~p1~1 and ~ps~2. In the other case, the matrix is not computed
|
|
completely. Instead, the return value will indicate whether the
|
|
topological relationship is part of ~cluster~.
|
|
|
|
*/
|
|
bool GetInt9M(Points* ps1, Points* ps2,
|
|
Int9M& res,
|
|
const bool useCluster = false,
|
|
Cluster cluster = Cluster(false)){
|
|
|
|
if(useCluster && cluster.IsEmpty()){
|
|
return false;
|
|
}
|
|
int n1 = ps1->Size();
|
|
int n2 = ps2->Size();
|
|
|
|
if(useCluster){ // restrict cluster to matrices which are realizable
|
|
// for the points/points combination
|
|
|
|
int cb = cluster.checkBoxes(ps1->BoundingBox(),n1,
|
|
ps2->BoundingBox(),n2);
|
|
if(cb==1) {
|
|
return true;
|
|
}
|
|
if(cb==2){
|
|
return false;
|
|
}
|
|
|
|
cluster.Restrict(EE,true,false); // the extreiors always intersect
|
|
|
|
// the boundary of a points value is empty, thereby no intersections
|
|
// of this part with any other part may exist
|
|
Int9M r(true, false, true, false, false, false, true,false,true);
|
|
cluster.Restrict(r,false,false);
|
|
|
|
if(cluster.IsEmpty()){
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
res.SetValue(0);
|
|
res.SetEE(true);
|
|
|
|
if(n1<=0 && n2<=0){
|
|
// there are no inner parts which can intersect any part
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
if(n1<=0){
|
|
// some points of ps2 are in the exterior of ps1
|
|
res.SetEI(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
if(n2<=0){
|
|
// symmetrically to the previous case
|
|
res.SetIE(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
// bounding box check
|
|
Rectangle<2> bbox1 = ps1->BoundingBox();
|
|
Rectangle<2> bbox2 = ps2->BoundingBox();
|
|
if(!bbox1.IntersectsUD(bbox2)){
|
|
// non empty disjoint points values
|
|
res.SetIE(true);
|
|
res.SetEI(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// bounding box check failed, perform an parallel scan
|
|
|
|
Point p1;
|
|
Point p2;
|
|
|
|
int i1=0;
|
|
int i2=0;
|
|
|
|
bool done = false;
|
|
do{
|
|
ps1->Get(i1,p1);
|
|
ps2->Get(i2,p2);
|
|
int cmp = p1.Compare(&p2);
|
|
if(cmp==0){ //p1==p2
|
|
SetII(res,useCluster,cluster,done);
|
|
i1++;
|
|
i2++;
|
|
} else if (cmp<0){
|
|
// p1 in the exterior of p2
|
|
SetIE(res,useCluster,cluster,done);
|
|
i1++;
|
|
} else{ // p1 > p2
|
|
SetEI(res,useCluster,cluster,done);
|
|
i2++;
|
|
}
|
|
done= done ||
|
|
((i1>=n1-1) || (i2>=n2-1)) || // end of one points value reached
|
|
(res.GetII() && res.GetIE() && res.GetEI());
|
|
if(useCluster){
|
|
done = done || cluster.IsEmpty();
|
|
}
|
|
}while(!done); // end of scan
|
|
|
|
if(res.GetII() && res.GetIE() && res.GetEI()){
|
|
// maximum count of intersections
|
|
if(!useCluster){
|
|
return true;
|
|
} else {
|
|
return cluster.Contains(res);
|
|
}
|
|
}
|
|
if((i1<n1-1)){ // ps1 has further points
|
|
SetIE(res,useCluster,cluster,done);
|
|
}
|
|
|
|
if( (i2<n2-1)){ // ps2 has further points
|
|
SetEI(res,useCluster,cluster,done);
|
|
}
|
|
if(!useCluster){
|
|
return true;
|
|
} else {
|
|
return cluster.Contains(res);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
7.4 ~line~ [x] ~point~ and ~line~ [x] ~points~
|
|
|
|
The next functions compute the topological relationship between a
|
|
line and a point or points value.
|
|
|
|
*/
|
|
|
|
|
|
/*
|
|
~initBox~
|
|
|
|
Initializes the result and performs a bounding box check.
|
|
If the initialization is sufficient to determine the result,
|
|
i.e. if the line is empty, the result will be __true__. Otherwise,
|
|
the result will be false.
|
|
|
|
*/
|
|
bool initBox(Line const* const line,
|
|
Point const* const point,
|
|
Int9M& res,
|
|
bool & pointdone){
|
|
res.SetValue(0);
|
|
res.SetEE(true);
|
|
|
|
if(line->IsEmpty()){
|
|
res.SetEI(true);
|
|
return true;
|
|
}
|
|
|
|
// the interior of a non-empty line has always
|
|
// an intersection with the exterior of a single point
|
|
// because of the difference in the dimension
|
|
res.SetIE(true);
|
|
|
|
pointdone = false;
|
|
// the line contains at least one halfsegment
|
|
Rectangle<2> bbox_line = line->BoundingBox();
|
|
Rectangle<2> bbox_point = point->BoundingBox();
|
|
// both objects are disjoint
|
|
if(!bbox_line.Intersects(bbox_point)){
|
|
res.SetIE(true);
|
|
res.SetEI(true);
|
|
pointdone = true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool initBox(Line const* const line,
|
|
Points const* const point,
|
|
Int9M& res,
|
|
bool & pointdone){
|
|
res.SetValue(0);
|
|
res.SetEE(true);
|
|
|
|
pointdone = false;
|
|
if(line->IsEmpty()){
|
|
if(point->IsEmpty()){
|
|
pointdone = true;
|
|
return true;
|
|
}
|
|
else {
|
|
res.SetEI(true);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
// the interior of a non-empty line has always
|
|
// an intersection with the exterior of a single point
|
|
// because of the difference in the dimension
|
|
res.SetIE(true);
|
|
|
|
if(point->IsEmpty()){
|
|
pointdone = true;
|
|
return false; // search for boundary points
|
|
}
|
|
|
|
|
|
// line and point are non empty
|
|
Rectangle<2> bbox_line = line->BoundingBox();
|
|
Rectangle<2> bbox_point = point->BoundingBox();
|
|
// both objects are disjoint
|
|
if(!bbox_line.Intersects(bbox_point)){
|
|
res.SetIE(true);
|
|
res.SetEI(true);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
~isDone~
|
|
|
|
This function checks whether all possible intersections for the
|
|
parameter combination of the first two parameters are set in in the
|
|
matrix.
|
|
|
|
*/
|
|
|
|
bool isDone(Line const* const line, Point const* const point, Int9M m,
|
|
const bool useCluster, const Cluster& cluster){
|
|
|
|
bool res = m.GetBE() && // endpoint of the line has been found
|
|
(m.GetII() || m.GetBI() || m.GetEI());
|
|
if(useCluster){
|
|
return res || cluster.IsEmpty() || cluster.isExtension(m);
|
|
} else {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
|
|
bool isDone(Line const* const line, Points const* const point, Int9M m,
|
|
const bool useCluster, const Cluster& cluster){
|
|
|
|
bool res = m.GetBE() && // endpoint of the line has been found
|
|
(m.GetII() && m.GetBI() && m.GetEI());
|
|
if(useCluster){
|
|
return res || cluster.IsEmpty() || cluster.isExtension(m);
|
|
} else {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
bool isEmpty(const Point& p){
|
|
return false;
|
|
}
|
|
|
|
bool isEmpty(const Points& ps){
|
|
return ps.IsEmpty();
|
|
}
|
|
|
|
|
|
/*
|
|
~GetInt9M~
|
|
|
|
The template parameter can be instantiated with ~Point~ or ~Points~.
|
|
|
|
*/
|
|
|
|
template<class Pclass>
|
|
bool GetInt9M(Line const* const line,
|
|
Pclass const* const point,
|
|
Int9M& res,
|
|
const bool useCluster,
|
|
Cluster& cluster,
|
|
const bool forceThrow/*=false*/){
|
|
|
|
bool pointdone1 = false;
|
|
if(initBox(line,point,res,pointdone1)){
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(useCluster){
|
|
// line is not empty
|
|
int cb = cluster.checkBoxes(line->BoundingBox(),line->Size()==0,
|
|
point->BoundingBox(),
|
|
isEmpty(*point));
|
|
if(cb==1) {
|
|
return true;
|
|
}
|
|
if(cb==2){
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// restrict the cluster to valid matrices
|
|
if(useCluster){
|
|
cluster.Restrict(EE,true,false); // hold in each case
|
|
cluster.Restrict(IE,true,false); // nonempty line
|
|
|
|
// boundary of a point is empty
|
|
Int9M m(true, false, true, true,false,true, true,false,true);
|
|
cluster.Restrict(m,false,false);
|
|
if(cluster.IsEmpty()){
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// prefilter unsuccessful -> scan the halfsegments
|
|
|
|
avltree::AVLTree<avlseg::AVLSegment> sss;
|
|
priority_queue<avlseg::ExtendedHalfSegment,
|
|
vector<avlseg::ExtendedHalfSegment>,
|
|
greater<avlseg::ExtendedHalfSegment> > q;
|
|
|
|
|
|
bool done = false;
|
|
int posline = 0; // position within the line array
|
|
avlseg::ownertype owner;
|
|
|
|
int posPoint = pointdone1?1:0;
|
|
|
|
avlseg::ExtendedHalfSegment resHs;
|
|
Point resPoi;
|
|
Point lastDomPoint;
|
|
int lastDomPointCount = 0;
|
|
// avoid unneeded expensive restrictions of the cluster
|
|
avlseg::AVLSegment tmpL,tmpR;
|
|
|
|
while(!done &&
|
|
((owner=selectNext(*line,q,posline,*point,
|
|
posPoint,resHs,resPoi))!=avlseg::none)){
|
|
|
|
if(owner==avlseg::second){ // event comes from the point(s) value
|
|
avlseg::AVLSegment current(resPoi,avlseg::second);
|
|
|
|
avlseg::AVLSegment* leftN=0;
|
|
avlseg::AVLSegment* rightN=0;
|
|
avlseg::AVLSegment* member= sss.getMember(current,leftN,rightN);
|
|
if(leftN){
|
|
tmpL = *leftN;
|
|
leftN = &tmpL;
|
|
}
|
|
if(rightN){
|
|
tmpR = *rightN;
|
|
rightN = &tmpR;
|
|
}
|
|
|
|
|
|
if(!member){ // point outside current, check lastdompoint
|
|
if(lastDomPointCount>0 && AlmostEqual(lastDomPoint,resPoi)){
|
|
// point located in the last dominating point
|
|
if(lastDomPointCount==1){ // on boundary
|
|
SetBI(res,useCluster,cluster,done);
|
|
} else {
|
|
SetII(res,useCluster,cluster,done);
|
|
}
|
|
lastDomPointCount++;
|
|
} else { // point outside the line
|
|
SetEI(res,useCluster,cluster,done);
|
|
// update dompoint
|
|
if(lastDomPointCount==1){ // last point was boundary
|
|
SetBE(res,useCluster,cluster,done);
|
|
}
|
|
lastDomPoint = resPoi;
|
|
lastDomPointCount = 0;
|
|
}
|
|
} else {
|
|
double x = resPoi.GetX();
|
|
double y = resPoi.GetY();
|
|
if((leftN && leftN->contains(x,y)) ||
|
|
(rightN && rightN->contains(x,y))||
|
|
( member->ininterior(x,y))){
|
|
SetII(res,useCluster,cluster,done);
|
|
} else { // point located on an endpoint of member
|
|
if(lastDomPointCount>0 && AlmostEqual(resPoi,lastDomPoint)){
|
|
if(lastDomPointCount==1){
|
|
SetBI(res,useCluster,cluster,done);
|
|
} else {
|
|
SetII(res,useCluster,cluster,done);
|
|
}
|
|
lastDomPointCount++;
|
|
} else {
|
|
SetEI(res,useCluster,cluster,done);
|
|
lastDomPointCount = 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
done = done || isDone(line,point,res,useCluster,cluster);
|
|
lastDomPoint = resPoi;
|
|
} else { // an halfsegment
|
|
assert(owner==avlseg::first);
|
|
|
|
// check for endpoints
|
|
Point domPoint = resHs.GetDomPoint();
|
|
|
|
// only check for dompoints if the segment is a new one (left)
|
|
// or its actually stored in the tree
|
|
avlseg::AVLSegment current(resHs, avlseg::first);
|
|
avlseg::AVLSegment* leftN=0;
|
|
avlseg::AVLSegment* rightN=0;
|
|
avlseg::AVLSegment* member= sss.getMember(current,leftN,rightN);
|
|
if(leftN){
|
|
tmpL = *leftN;
|
|
leftN = &tmpL;
|
|
}
|
|
if(rightN){
|
|
tmpR = *rightN;
|
|
rightN = &tmpR;
|
|
}
|
|
|
|
if(resHs.IsLeftDomPoint() ||
|
|
(member && member->exactEqualsTo(current))){
|
|
if(lastDomPointCount==0 || !AlmostEqual(domPoint,lastDomPoint)){
|
|
if(lastDomPointCount==1){
|
|
SetBE(res,useCluster,cluster,done);
|
|
}
|
|
lastDomPoint = domPoint;
|
|
lastDomPointCount = 1;
|
|
} else{
|
|
lastDomPointCount++;
|
|
lastDomPoint = domPoint;
|
|
}
|
|
}
|
|
|
|
if(resHs.IsLeftDomPoint()){ // left event
|
|
avlseg::AVLSegment left1, left2, right1, right2;
|
|
if(member){ //overlapping segment found
|
|
if((!AlmostEqual(member->getX2(),current.getX2())) &&
|
|
(member->getX2() < current.getX2() )){
|
|
// current is an extension of member
|
|
current.splitAt(member->getX2(), member->getY2(),left1,right1);
|
|
// create events for the remaining parts
|
|
q.push(right1.convertToExtendedHs(true,avlseg::first));
|
|
q.push(right1.convertToExtendedHs(false,avlseg::first));
|
|
}
|
|
} else { // there is no overlapping segment
|
|
splitByNeighbour(sss,current,leftN,q,q, forceThrow);
|
|
splitByNeighbour(sss,current,rightN,q,q, forceThrow);
|
|
sss.insert(current);
|
|
} // no overlapping segment
|
|
} else { // right event
|
|
|
|
avlseg::AVLSegment left1, left2, right1, right2;
|
|
|
|
if(member && member->exactEqualsTo(current)){ // segment found
|
|
|
|
sss.remove(current);
|
|
splitNeighbours(sss,leftN,rightN,q,q, forceThrow);
|
|
}
|
|
}
|
|
}
|
|
done = done || isDone(line,point,res,useCluster, cluster);
|
|
} // end sweep
|
|
|
|
if(lastDomPointCount==1) {
|
|
// last Point of the line is an endpoint
|
|
SetBE(res,useCluster,cluster,done);
|
|
}
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
}else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Instantiations of the template function
|
|
|
|
*/
|
|
|
|
bool GetInt9M(Line const* const line, Point const* const point,Int9M& res,
|
|
const bool useCluster=false, Cluster cluster = Cluster(),
|
|
const bool forceThrow = false){
|
|
return GetInt9M<Point>(line,point,res, useCluster, cluster, forceThrow);
|
|
}
|
|
|
|
bool GetInt9M(Line const* const line, Points const* const point,Int9M& res,
|
|
const bool useCluster=false, Cluster cluster = Cluster(),
|
|
const bool forceThrow= false){
|
|
return GetInt9M<Points>(line,point,res, useCluster, cluster, forceThrow);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
7.5 ~region~ [x] ~point~
|
|
|
|
Computation of the 9 intersection matrix for a single point and a
|
|
region value.
|
|
|
|
|
|
~pointAbove~
|
|
|
|
Auxiliary function checking if ~p~ is located above ~hs~.
|
|
|
|
*/
|
|
|
|
|
|
bool pointAbove(const HalfSegment* hs, const Point* p) {
|
|
double x1 = hs->GetLeftPoint().GetX();
|
|
double y1 = hs->GetLeftPoint().GetY();
|
|
double x2 = hs->GetRightPoint().GetX();
|
|
double y2 = hs->GetRightPoint().GetY();
|
|
|
|
double x = p->GetX();
|
|
double y = p->GetY();
|
|
|
|
if(AlmostEqual(x1,x2)){ // vertical segment
|
|
return y > max(y1,y2);
|
|
}
|
|
double d = (x-x1)/(x2-x1);
|
|
double ys = y1 + d*(y2-y1);
|
|
return (!AlmostEqual(y,ys) && y > ys);
|
|
}
|
|
|
|
|
|
bool GetInt9M(Region const* const reg, Point const* const p, Int9M& res,
|
|
const bool useCluster= false,
|
|
Cluster cluster = Cluster()){
|
|
|
|
res.SetValue(0);
|
|
res.SetEE(true);
|
|
if(reg->IsEmpty()){
|
|
res.SetEI(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
res.SetIE(true); // the point can't cover an infinite set of points
|
|
res.SetBE(true);
|
|
|
|
Rectangle<2> bboxreg = reg->BoundingBox();
|
|
Rectangle<2> bboxp = p->BoundingBox();
|
|
|
|
if(!bboxreg.Intersects(bboxp)){
|
|
res.SetEI(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(useCluster){
|
|
int cb = cluster.checkBoxes(reg->BoundingBox(),false,
|
|
p->BoundingBox(),
|
|
false);
|
|
if(cb==1) {
|
|
return true;
|
|
}
|
|
if(cb==2){
|
|
return false;
|
|
}
|
|
}
|
|
// bbox check failed, we have to compute the result
|
|
|
|
int size = reg->Size();
|
|
double x = p->GetX();
|
|
|
|
avlseg::ExtendedHalfSegment hs;
|
|
|
|
int number = 0;
|
|
|
|
for(int i=0;i<size;i++){
|
|
HalfSegment hs1;
|
|
reg->Get(i,hs1);
|
|
hs = hs1;
|
|
if(hs.IsLeftDomPoint()){
|
|
if(hs.Contains(*p)){
|
|
res.SetBI(true); //point on boundary
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
if(!hs.IsVertical()){
|
|
if(pointAbove(&hs,p)){
|
|
if((AlmostEqual(hs.GetRightPoint().GetX(),x)) ||
|
|
( !AlmostEqual(hs.GetLeftPoint().GetX(),x) &&
|
|
hs.GetLeftPoint().GetX()<x &&
|
|
hs.GetRightPoint().GetX()>=x)){
|
|
number++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(number % 2 == 0){
|
|
res.SetEI(true);
|
|
} else{
|
|
res.SetII(true);
|
|
}
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
7.6 ~region~ [x] ~points~
|
|
|
|
Computation of the 9 intersection matrix for a set of points and a
|
|
region value.
|
|
|
|
|
|
~selectNext~
|
|
|
|
If the event caused by the region is smaller then the one caused by the
|
|
points value, the result of the function will be ~first~. Otherwise, the
|
|
result will be ~second~. The region itself may consist of the original
|
|
halfsegments and halfsegments produced by dividing original halfsegments.
|
|
The positions indicate the current elements of the halfsegment arrays.
|
|
They are updated automatically within this function.
|
|
Depending on the return value, one of the parameter ~resultHs~ or
|
|
~resultPoint~ is set to the value of the next event.
|
|
|
|
If the region and the points value are already processed, the return
|
|
value will be ~none~.
|
|
|
|
*/
|
|
|
|
avlseg::ownertype selectNext(const Region* reg,
|
|
priority_queue<avlseg::ExtendedHalfSegment,
|
|
vector<avlseg::ExtendedHalfSegment>,
|
|
greater<avlseg::ExtendedHalfSegment> >& q1,
|
|
int& pos1,
|
|
Points const* const p,
|
|
int& pos2,
|
|
avlseg::ExtendedHalfSegment& resultHS,
|
|
Point& resultPoint){
|
|
|
|
assert(pos1>=0);
|
|
assert(pos2>=0);
|
|
|
|
int sizereg = reg->Size();
|
|
int sizepoint = p->Size();
|
|
|
|
const avlseg::ExtendedHalfSegment* rhs=0;
|
|
const avlseg::ExtendedHalfSegment* qhs = 0;
|
|
avlseg::ExtendedHalfSegment qhsc;
|
|
const avlseg::ExtendedHalfSegment* minhs=0;
|
|
const Point* cp=0;
|
|
|
|
avlseg::ExtendedHalfSegment rhs1;
|
|
if(pos1<sizereg){
|
|
HalfSegment hstmp;
|
|
reg->Get(pos1, hstmp);
|
|
rhs1 = hstmp;
|
|
rhs = &rhs1;
|
|
}
|
|
if(!q1.empty()){
|
|
qhsc = q1.top();
|
|
qhs = &qhsc;
|
|
}
|
|
|
|
Point cp1;
|
|
if(pos2<sizepoint){
|
|
p->Get(pos2,cp1);
|
|
cp = &cp1;
|
|
}
|
|
|
|
int src = 0; // none
|
|
if(rhs){
|
|
src = 1;
|
|
minhs = rhs;
|
|
}
|
|
if(qhs){
|
|
if(src==0) {
|
|
src = 1;
|
|
minhs = qhs;
|
|
} else { // rhs and qhs exist
|
|
if(*qhs < *rhs){
|
|
src = 2;
|
|
minhs = qhs;
|
|
}
|
|
}
|
|
}
|
|
if(cp){
|
|
if(!minhs){
|
|
src = 3;
|
|
} else {
|
|
double px = cp->GetX();
|
|
double hx = minhs->GetDomPoint().GetX();
|
|
if(AlmostEqual(px,hx)){
|
|
double py = cp->GetY();
|
|
double hy = minhs->GetDomPoint().GetY();
|
|
if(AlmostEqual(py,hy)){
|
|
if(!minhs->IsLeftDomPoint()){
|
|
// left < point < right
|
|
src = 3;
|
|
}
|
|
} else if(py<hy){
|
|
src = 3;
|
|
} // else don't change src
|
|
} else if(px<hx){
|
|
src = 3;
|
|
} // else do not change src
|
|
}
|
|
}
|
|
|
|
switch(src){
|
|
case 0: {
|
|
return avlseg::none;
|
|
} case 1: { // region
|
|
pos1++;
|
|
resultHS = *minhs;
|
|
return avlseg::first;
|
|
} case 2: { // queue
|
|
q1.pop();
|
|
resultHS = *minhs;
|
|
return avlseg::first;
|
|
} case 3: { // point
|
|
pos2++;
|
|
resultPoint = *cp;
|
|
return avlseg::second;
|
|
} default: {
|
|
assert(false);
|
|
return avlseg::none;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool GetInt9MRob(const Region* reg, const Points* ps, Int9M& res,
|
|
const bool useCluster, const Cluster& cluster){
|
|
|
|
bool* puses= new bool[ps->Size()];
|
|
memset(puses,0,ps->Size() * sizeof(bool));
|
|
|
|
res.SetValue(0);
|
|
res.SetEE(true);
|
|
|
|
|
|
mmrtree::RtreeT<2,int> tree(4,8);
|
|
Point p;
|
|
for(int i=0;i<ps->Size();i++){
|
|
ps->Get(i,p);
|
|
tree.insert(p.BoundingBox(),i);
|
|
}
|
|
// find points of boundary of the region
|
|
HalfSegment hs;
|
|
for(int i=0;i<reg->Size();i++){
|
|
reg->Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
mmrtree::RtreeT<2,int>::iterator* it = tree.find(hs.BoundingBox());
|
|
int const* pos;
|
|
while( (pos = it->next()) != 0){
|
|
ps->Get(*pos,p);
|
|
if(hs.Contains(p)){
|
|
puses[*pos] = true;
|
|
res.SetBI(true);
|
|
}
|
|
}
|
|
delete it;
|
|
}
|
|
}
|
|
// the unused points are inside or outside the region
|
|
bool ii=false;
|
|
bool ei = false;
|
|
for(int i=0;i<ps->Size();i++){
|
|
if(!puses[i]){
|
|
ps->Get(i,p);
|
|
if(reg->InnerContains(p)){
|
|
ii = true;
|
|
res.SetII(true);
|
|
} else {
|
|
ei = true;
|
|
res.SetEI(true);
|
|
}
|
|
if(ii && ei){
|
|
delete[] puses;
|
|
return cluster.Contains(res);
|
|
}
|
|
}
|
|
}
|
|
delete[] puses;
|
|
return cluster.Contains(res);
|
|
|
|
|
|
}
|
|
|
|
|
|
bool GetInt9M(Region const* const reg, Points const* const ps, Int9M& res,
|
|
const bool useCluster=false,
|
|
Cluster cluster = Cluster(),
|
|
const bool forceThrow = false){
|
|
|
|
|
|
try{
|
|
res.SetValue(0);
|
|
// test for emptyness
|
|
res.SetEE(true);
|
|
if(reg->IsEmpty()){
|
|
if(ps->IsEmpty()){
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
res.SetEI(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
res.SetIE(true);
|
|
res.SetBE(true);
|
|
|
|
if(ps->IsEmpty()){ // no more intersections can be found
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
// bounding box test
|
|
Rectangle<2> regbox = reg->BoundingBox();
|
|
Rectangle<2> pbox = ps->BoundingBox();
|
|
if(!regbox.Intersects(pbox)){ // disjoint objects
|
|
res.SetEI(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
if(useCluster){
|
|
int cb = cluster.checkBoxes(regbox, false, pbox, false);
|
|
if(cb==1) {
|
|
return true;
|
|
}
|
|
if(cb==2){
|
|
return false;
|
|
}
|
|
}
|
|
// bbox failed, perform a plane sweep
|
|
|
|
if(useCluster){
|
|
// restrict the cluster to possible values
|
|
|
|
// boundary of an points value is empty
|
|
Int9M m1(true,false,true, true,false,true, true,false,true);
|
|
cluster.Restrict(m1,false,false);
|
|
|
|
cluster.Restrict(res,true,false);
|
|
|
|
if(cluster.IsEmpty()){
|
|
return false;
|
|
}
|
|
if(cluster.isExtension(res)){
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// queue for splitted segments of the region
|
|
priority_queue<avlseg::ExtendedHalfSegment,
|
|
vector<avlseg::ExtendedHalfSegment>,
|
|
greater<avlseg::ExtendedHalfSegment> > q1;
|
|
avltree::AVLTree<avlseg::AVLSegment> sss;
|
|
|
|
avlseg::ownertype owner;
|
|
bool done = false;
|
|
int pos1 =0;
|
|
int pos2 =0;
|
|
Point CP;
|
|
avlseg::ExtendedHalfSegment CH;
|
|
avlseg::AVLSegment tmpL,tmpR;
|
|
|
|
while (!done &&
|
|
( (owner= selectNext(reg,q1,pos1, ps,pos2,CH,CP))!=avlseg::none)){
|
|
if(owner==avlseg::second){ // the point
|
|
avlseg::AVLSegment current(CP,avlseg::second);
|
|
avlseg::AVLSegment* left=0;
|
|
avlseg::AVLSegment* right=0;
|
|
avlseg::AVLSegment* member = sss.getMember(current, left, right);
|
|
if(left){
|
|
tmpL = *left;
|
|
left = &tmpL;
|
|
}
|
|
if(right){
|
|
tmpR = *right;
|
|
right = &tmpR;
|
|
}
|
|
if(member){ // point located on boundary
|
|
SetBI(res,useCluster,cluster,done);
|
|
} else if(left){
|
|
if(left->getInsideAbove_first()){
|
|
SetII(res,useCluster,cluster,done);
|
|
} else {
|
|
SetEI(res,useCluster,cluster,done);
|
|
}
|
|
} else { // there is nothing under cp
|
|
SetEI(res,useCluster,cluster,done);
|
|
}
|
|
done = done || (res.GetII() && res.GetBI() && res.GetEI());
|
|
} else { // the next element comes from the region
|
|
avlseg::AVLSegment current(CH,avlseg::first);
|
|
|
|
avlseg::AVLSegment* leftN = 0;
|
|
avlseg::AVLSegment* rightN = 0;
|
|
avlseg::AVLSegment* member = sss.getMember(current,leftN,rightN);
|
|
if(leftN){
|
|
tmpL = *leftN;
|
|
leftN = &tmpL;
|
|
}
|
|
if(rightN){
|
|
tmpR = *rightN;
|
|
rightN = &tmpR;
|
|
}
|
|
if(CH.IsLeftDomPoint()){ // left Event
|
|
if(member){
|
|
cout << "found overlapping segments"
|
|
" within a single region" << endl;
|
|
cout << "Segment1 " << setprecision(16) << current << endl;
|
|
cout << "Segment2 " << setprecision(16) << (*member) << endl;
|
|
throw myexception("found overlapping segments") ;
|
|
}
|
|
assert(!member); // a single region can't
|
|
//contain overlapping segments
|
|
splitByNeighbour(sss,current,leftN,q1,q1, forceThrow);
|
|
splitByNeighbour(sss,current,rightN,q1,q1, forceThrow);
|
|
sss.insert(current);
|
|
} else { // right event
|
|
if(member && member->exactEqualsTo(current)){
|
|
sss.remove(current);
|
|
splitNeighbours(sss,leftN,rightN,q1,q1, forceThrow);
|
|
}
|
|
}
|
|
} // element from region
|
|
} // while
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else{
|
|
return true;
|
|
}
|
|
} catch(...){
|
|
return GetInt9MRob(reg, ps, res,
|
|
useCluster,
|
|
cluster );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
The Class ~OwnedPoint~
|
|
|
|
This class contains a point together with its owner.
|
|
|
|
|
|
*/
|
|
class OwnedPoint{
|
|
|
|
public:
|
|
OwnedPoint(){
|
|
defined = false;
|
|
}
|
|
|
|
OwnedPoint(Point p0, avlseg::ownertype o){
|
|
p = p0;
|
|
owner = o;
|
|
defined = true;
|
|
}
|
|
|
|
OwnedPoint(const OwnedPoint& src){
|
|
p = src.p;
|
|
owner = src.owner;
|
|
defined = src.defined;
|
|
}
|
|
|
|
OwnedPoint& operator=(const OwnedPoint& src){
|
|
p = src.p;
|
|
owner = src.owner;
|
|
defined = src.defined;
|
|
return *this;
|
|
}
|
|
|
|
Point p;
|
|
avlseg::ownertype owner;
|
|
bool defined;
|
|
};
|
|
|
|
/*
|
|
7.7 ~region~ [x] ~region~
|
|
|
|
*/
|
|
|
|
bool GetInt9M(Region const* const reg1, Region const* const reg2, Int9M& res,
|
|
const bool useCluster =false,
|
|
Cluster cluster = Cluster(),
|
|
const bool forceThrow = false){
|
|
|
|
res.SetValue(0);;
|
|
res.SetEE(true);
|
|
// check for emptyness
|
|
if(reg1->IsEmpty()){
|
|
if(reg2->IsEmpty()){ // no more intersection possible
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}else{
|
|
res.SetEI(true);
|
|
res.SetEB(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(reg2->IsEmpty()){
|
|
res.SetIE(true);
|
|
res.SetBE(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
// bounding box check
|
|
Rectangle<2> bbox1 = reg1->BoundingBox();
|
|
Rectangle<2> bbox2 = reg2->BoundingBox();
|
|
if(!bbox1.IntersectsUD(bbox2)){
|
|
res.SetIE(true);
|
|
res.SetEI(true);
|
|
res.SetBE(true);
|
|
res.SetEB(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(useCluster){
|
|
int cb = cluster.checkBoxes(bbox1, false, bbox2, false);
|
|
if(cb==1) {
|
|
return true;
|
|
}
|
|
if(cb==2){
|
|
return false;
|
|
}
|
|
cluster.Restrict(res,true,false);
|
|
if(cluster.IsEmpty() ){
|
|
return false;
|
|
}
|
|
if(cluster.isExtension(res)){
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
avltree::AVLTree<avlseg::AVLSegment> sss; // sweep state structure
|
|
// dynamic parts of the sweep event structure
|
|
priority_queue<avlseg::ExtendedHalfSegment,
|
|
vector<avlseg::ExtendedHalfSegment>,
|
|
greater<avlseg::ExtendedHalfSegment> > q1;
|
|
priority_queue<avlseg::ExtendedHalfSegment,
|
|
vector<avlseg::ExtendedHalfSegment>,
|
|
greater<avlseg::ExtendedHalfSegment> > q2;
|
|
|
|
int pos1=0; // current position in the halfsegment array of reg1
|
|
int pos2=0; // current position in the halfsegment array of reg2
|
|
int size1 = reg1->Size();
|
|
int size2 = reg2->Size();
|
|
|
|
bool done = false;
|
|
avlseg::ExtendedHalfSegment nextSeg;
|
|
|
|
avlseg::AVLSegment* member=0; // current member stored in the tree
|
|
avlseg::AVLSegment* leftN=0; // the left neightboor of member
|
|
avlseg::AVLSegment* rightN=0; // the right neighbour of member
|
|
avlseg::ownertype owner;
|
|
OwnedPoint lastDomPoint; // initialized to be undefined
|
|
int src =0;
|
|
avlseg::AVLSegment tmpL,tmpR,tmpM;
|
|
bool empty1 = false;
|
|
bool empty2 = false;
|
|
|
|
while(((owner=selectNext(*reg1, pos1,
|
|
*reg2, pos2,
|
|
q1,q2,
|
|
nextSeg,src))!=avlseg::none) &&
|
|
!done){
|
|
|
|
avlseg::AVLSegment current(nextSeg,owner);
|
|
|
|
member = sss.getMember(current,leftN,rightN);
|
|
if(leftN){
|
|
tmpL = *leftN;
|
|
leftN = &tmpL;
|
|
}
|
|
if(rightN){
|
|
tmpR = *rightN;
|
|
rightN = &tmpR;
|
|
}
|
|
if(member){
|
|
tmpM = *member;
|
|
member = &tmpM;
|
|
}
|
|
|
|
/*
|
|
Because right events are processed before
|
|
left events, we have to store the last dominating point
|
|
to detect intersections of the boundaries within a single point.
|
|
*/
|
|
|
|
Point p = nextSeg.GetDomPoint();
|
|
|
|
empty1 = (pos1>=size1) && q1.empty() && (owner!=avlseg::first) &&
|
|
( ((p.GetX()>lastDomPoint.p.GetX() &&
|
|
!AlmostEqual(p.GetX(),lastDomPoint.p.GetX()))
|
|
|| lastDomPoint.owner==avlseg::second));
|
|
|
|
empty2 = (pos2>=size2) && q2.empty() && (owner!=avlseg::second) &&
|
|
(( (p.GetX()>lastDomPoint.p.GetX() &&
|
|
!AlmostEqual(p.GetX(),lastDomPoint.p.GetX()))
|
|
|| lastDomPoint.owner==avlseg::first));
|
|
|
|
if(!lastDomPoint.defined || !AlmostEqual(lastDomPoint.p,p)){
|
|
lastDomPoint.defined = true;
|
|
lastDomPoint.p = p;
|
|
lastDomPoint.owner = owner;
|
|
} else { // same point as before
|
|
if(lastDomPoint.owner != owner){
|
|
SetBB(res,useCluster,cluster,done);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if(nextSeg.IsLeftDomPoint()){
|
|
avlseg::AVLSegment left, common, right;
|
|
if(member){ // there is an overlapping segment in the tree
|
|
// check for valid region representation
|
|
if(forceThrow){
|
|
if(member->getOwner() != current.getOwner()){
|
|
cerr << "overlapping segments within one region detected"
|
|
<< endl;
|
|
cerr << "Segment 1 : " << member << endl;
|
|
cerr << "Segment 2 : " << current << endl;
|
|
cerr << "owner = " << member->getOwner() << endl;
|
|
throw myexception("Overlapping segments "
|
|
"within one region detected");
|
|
}
|
|
if(member->getOwner() == avlseg::both){
|
|
cerr << "overlapping segments within one region detected"
|
|
<< endl;
|
|
cerr << "Segment 1 : " << member << endl;
|
|
cerr << "Segment 2 : " << current << endl;
|
|
cerr << "owner = " << current.getOwner() << endl;
|
|
throw myexception("Overlapping segments "
|
|
"within one region detected");
|
|
}
|
|
}
|
|
assert(member->getOwner() != current.getOwner());
|
|
assert(member->getOwner() != avlseg::both);
|
|
|
|
SetBB(res,useCluster,cluster,done); // common boundary found
|
|
bool iac = current.getOwner()==avlseg::first
|
|
?current.getInsideAbove_first()
|
|
:current.getInsideAbove_second();
|
|
bool iam = member->getOwner()==avlseg::first
|
|
?member->getInsideAbove_first()
|
|
:member->getInsideAbove_second();
|
|
|
|
if(iac!=iam){
|
|
SetIE(res,useCluster,cluster,done);
|
|
SetEI(res,useCluster,cluster,done);
|
|
} else {
|
|
SetII(res,useCluster,cluster,done);
|
|
// res.setEE(true); // already done in initialization
|
|
}
|
|
|
|
int parts = member->split(current,left,common,right);
|
|
|
|
|
|
sss.remove(*member);
|
|
|
|
if(parts & avlseg::LEFT){ // there is a left part
|
|
sss.insert(left); // simulates a left event.
|
|
insertEvents(left,false,true,q1,q2);
|
|
}
|
|
|
|
|
|
assert(parts & avlseg::COMMON); // there must exist a common part
|
|
|
|
// update con_above
|
|
if(iac){
|
|
common.con_above++;
|
|
} else {
|
|
common.con_above--;
|
|
}
|
|
sss.insert(common);
|
|
|
|
// insert the corresponding right event into one of the queues
|
|
insertEvents(common,false,true,q1,q2);
|
|
|
|
if(parts & avlseg::RIGHT) { // there is an exclusive right part
|
|
// create the events for the remainder
|
|
insertEvents(right,true,true,q1,q2);
|
|
}
|
|
|
|
/*
|
|
|
|
Note:
|
|
Here no check again the neighbours is performed because all
|
|
parts inserted into sss here are part of this structure
|
|
before inserting the removed element 'member'.
|
|
*/
|
|
} else{ // there is no overlapping segment
|
|
|
|
// check crossing left
|
|
if(leftN && leftN->crosses(current)){ // a inner intersection
|
|
assert(leftN->getOwner()!=current.getOwner());
|
|
// computation of the intersections
|
|
res.Fill();
|
|
done = true;
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
// check crossing right
|
|
if(rightN && rightN->crosses(current)){
|
|
assert(rightN->getOwner()!=current.getOwner());
|
|
// computation of the intersections
|
|
res.Fill();
|
|
done = true;
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
// check for disjointness with both neighbours
|
|
if( ( !leftN || leftN->innerDisjoint(current)) &&
|
|
( !rightN || rightN->innerDisjoint(current))){
|
|
|
|
// update coverage numbers
|
|
bool iac = current.getOwner()==avlseg::first
|
|
?current.getInsideAbove_first()
|
|
:current.getInsideAbove_second();
|
|
|
|
iac = current.getOwner()==avlseg::first
|
|
?current.getInsideAbove_first()
|
|
:current.getInsideAbove_second();
|
|
|
|
if(leftN && current.extends(*leftN)){
|
|
current.con_below = leftN->con_below;
|
|
current.con_above = leftN->con_above;
|
|
}else{
|
|
if(leftN && leftN->isVertical()){
|
|
current.con_below = leftN->con_below;
|
|
} else if(leftN){
|
|
current.con_below = leftN->con_above;
|
|
} else {
|
|
current.con_below = 0;
|
|
}
|
|
if(iac){
|
|
current.con_above = current.con_below+1;
|
|
} else {
|
|
current.con_above = current.con_below-1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// derive intersections from the coverage numbers
|
|
if(current.con_below == 0){
|
|
; // EE is set already in initialization
|
|
//res.SetEE(true);
|
|
} else if (current.con_below == 1) {
|
|
if(current.getInsideAbove()){
|
|
if(current.getOwner()==avlseg::first){
|
|
SetEI(res,useCluster,cluster,done);
|
|
} else {
|
|
SetIE(res,useCluster,cluster,done);
|
|
}
|
|
} else {
|
|
if(current.getOwner()==avlseg::first){
|
|
SetIE(res,useCluster,cluster,done);
|
|
} else {
|
|
SetEI(res,useCluster,cluster,done);
|
|
}
|
|
}
|
|
} else {
|
|
assert(current.con_below==2);
|
|
SetII(res,useCluster,cluster,done);
|
|
}
|
|
// check for possible common endpoints with the neighbours
|
|
if(leftN && leftN->getOwner()!=current.getOwner()){
|
|
if(leftN->intersects(current)){
|
|
SetBB(res,useCluster,cluster,done);
|
|
}
|
|
}
|
|
if(rightN && rightN->getOwner()!=current.getOwner()){
|
|
if(rightN->intersects(current)){
|
|
SetBB(res,useCluster,cluster,done);
|
|
}
|
|
}
|
|
sss.insert(current);
|
|
|
|
} else { // inner intersection with at least one neighbour
|
|
// check for possible common points with the neighbours
|
|
if(leftN && leftN->getOwner()!=current.getOwner()){
|
|
if(leftN->intersects(current)){
|
|
SetBB(res,useCluster,cluster,done);
|
|
}
|
|
}
|
|
if(rightN && rightN->getOwner()!=current.getOwner()){
|
|
if(rightN->intersects(current)){
|
|
SetBB(res,useCluster,cluster,done);
|
|
}
|
|
}
|
|
splitByNeighbour(sss,current,leftN,q1,q2, forceThrow);
|
|
splitByNeighbour(sss,current,rightN,q1,q2, forceThrow);
|
|
// insert current (may be shortened) into sss
|
|
// update coverage numbers
|
|
bool iac = current.getOwner()==avlseg::first?
|
|
current.getInsideAbove_first()
|
|
:current.getInsideAbove_second();
|
|
|
|
|
|
if(leftN && current.extends(*leftN)){
|
|
current.con_below = leftN->con_below;
|
|
current.con_above = leftN->con_above;
|
|
}else{
|
|
if(leftN && leftN->isVertical()){
|
|
current.con_below = leftN->con_below;
|
|
} else if(leftN){
|
|
current.con_below = leftN->con_above;
|
|
} else {
|
|
current.con_below = 0;
|
|
}
|
|
if(iac){
|
|
current.con_above = current.con_below+1;
|
|
} else {
|
|
current.con_above = current.con_below-1;
|
|
}
|
|
}
|
|
|
|
// derive intersections from the coverage numbers
|
|
if(current.con_below == 0){
|
|
; // res.SetEE(true); // already done in initialization
|
|
} else if (current.con_below == 1) {
|
|
if(current.getInsideAbove()){
|
|
if(current.getOwner()==avlseg::first){
|
|
SetEI(res,useCluster,cluster,done);
|
|
} else {
|
|
SetIE(res,useCluster,cluster,done);
|
|
}
|
|
} else {
|
|
if(current.getOwner()==avlseg::first){
|
|
SetIE(res,useCluster,cluster,done);
|
|
} else {
|
|
SetEI(res,useCluster,cluster,done);
|
|
}
|
|
}
|
|
} else if(current.con_below==2){
|
|
SetII(res,useCluster,cluster,done);
|
|
} else {
|
|
cerr << "invalid value for con_below "
|
|
<< current.con_below << endl;
|
|
if(!leftN){
|
|
cerr << "no predecessor found" << endl;
|
|
} else {
|
|
cerr << "Left= " << *leftN << endl;
|
|
cerr << "extension : " << current.extends(*leftN) << endl;
|
|
cerr << "vertical : " << leftN->isVertical() << endl;
|
|
}
|
|
assert(false);
|
|
|
|
}
|
|
assert(current.con_above>=0 && current.con_above<=2);
|
|
assert(current.con_below + current.con_above > 0);
|
|
sss.insert(current);
|
|
}
|
|
}
|
|
} else { // right endpoint of an halfsegment
|
|
|
|
if(member){ // element found in sss, may be an old splitted element
|
|
// check if where member is located
|
|
if(member->getOwner()!=avlseg::both){
|
|
// member is located in the interior or in the exterior of the
|
|
// other region
|
|
if(member->con_below==0 || member->con_above==0){ // in exterior
|
|
switch(member->getOwner()){
|
|
case avlseg::first: SetBE(res,useCluster,cluster,done);
|
|
SetIE(res,useCluster,cluster,done);
|
|
//res.SetEE(true);
|
|
break;
|
|
case avlseg::second: SetEB(res,useCluster,cluster,done);
|
|
SetEI(res,useCluster,cluster,done);
|
|
//res.SetEE(true);
|
|
break;
|
|
default: assert(false);
|
|
}
|
|
} else if(member->con_below==2 || member->con_above==2){ //interior
|
|
switch(member->getOwner()){
|
|
case avlseg::first: SetBI(res,useCluster,cluster,done);
|
|
SetII(res,useCluster,cluster,done);
|
|
SetEI(res,useCluster,cluster,done);
|
|
break;
|
|
case avlseg::second: SetIB(res,useCluster,cluster,done);
|
|
SetII(res,useCluster,cluster,done);
|
|
SetIE(res,useCluster,cluster,done);
|
|
break;
|
|
default: assert(false);
|
|
}
|
|
}else {
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
sss.remove(*member);
|
|
|
|
|
|
if( leftN && rightN && !leftN->innerDisjoint(*rightN)){
|
|
if(leftN->crosses(*rightN)){
|
|
assert(leftN->getOwner() != rightN->getOwner());
|
|
res.Fill(); // we are done
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow);
|
|
}
|
|
}
|
|
}
|
|
if(res.IsFull()){
|
|
done = true;
|
|
}
|
|
if(useCluster){
|
|
done = done || cluster.IsEmpty();
|
|
}
|
|
|
|
if(empty1 || empty2){
|
|
done = true;
|
|
if(!empty1){
|
|
res.SetBE(true);
|
|
res.SetIE(true);
|
|
if(!res.GetBB()){
|
|
owner=selectNext(*reg1,pos1, *reg2,pos2, q1,q2,nextSeg,src);
|
|
if(owner==avlseg::second && lastDomPoint.owner!=avlseg::second){
|
|
Point dP = nextSeg.GetDomPoint();
|
|
if(AlmostEqual(dP,lastDomPoint.p)){
|
|
res.SetBB(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!empty2){
|
|
res.SetEB(true);
|
|
res.SetEI(true);
|
|
if(!res.GetBB()){
|
|
owner=selectNext(*reg1,pos1, *reg2,pos2, q1,q2,nextSeg,src);
|
|
if(owner==avlseg::first && lastDomPoint.owner!=avlseg::first){
|
|
Point dP = nextSeg.GetDomPoint();
|
|
if(AlmostEqual(dP,lastDomPoint.p)){
|
|
res.SetBB(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // sweep
|
|
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
|
|
} // GetInt9M (region x region)
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
~updateDomPoints~
|
|
|
|
This functions does all the things caused by the next dominating point.
|
|
This function only works correct with two line values.
|
|
|
|
*/
|
|
|
|
void updateDomPoints_Line_Line(
|
|
Point& lastDomPoint, const Point& newDomPoint,
|
|
int& lastDomPointCount1, int& lastDomPointCount2,
|
|
avlseg::ownertype owner,
|
|
Int9M& res,
|
|
bool useCluster,
|
|
Cluster& cluster,
|
|
bool& done){
|
|
|
|
|
|
// update dominating point information
|
|
int sum = lastDomPointCount1 + lastDomPointCount2;
|
|
|
|
if( sum == 0 || !AlmostEqual(lastDomPoint,newDomPoint)){
|
|
// dominating point changed
|
|
if(lastDomPointCount1 == 1){
|
|
if(lastDomPointCount2 == 1){
|
|
SetBB(res,useCluster,cluster,done);
|
|
} else if(lastDomPointCount2>1){
|
|
SetBI(res,useCluster,cluster,done);
|
|
} else {
|
|
SetBE(res,useCluster,cluster,done);
|
|
}
|
|
} else if(lastDomPointCount1 > 1){
|
|
if(lastDomPointCount2 == 1){
|
|
SetIB(res,useCluster,cluster,done);
|
|
} else if(lastDomPointCount2>1){
|
|
SetII(res,useCluster,cluster,done);
|
|
} else {
|
|
SetIE(res,useCluster,cluster,done);
|
|
}
|
|
} else { // lastDomPointCount1 == 0
|
|
if(lastDomPointCount2 == 1){
|
|
SetEB(res,useCluster,cluster,done);
|
|
} else if(lastDomPointCount2>1){
|
|
SetEI(res,useCluster,cluster,done);
|
|
} else {
|
|
;
|
|
// SetEE(res,useCluster,cluster,done);
|
|
}
|
|
}
|
|
|
|
// update dompoint and counter
|
|
lastDomPoint = newDomPoint;
|
|
switch(owner){
|
|
case avlseg::first: lastDomPointCount1 = 1;
|
|
lastDomPointCount2 = 0;
|
|
break;
|
|
case avlseg::second: lastDomPointCount1 = 0;
|
|
lastDomPointCount2 = 1;
|
|
break;
|
|
case avlseg::both: lastDomPointCount1 = 1;
|
|
lastDomPointCount2 = 1;
|
|
break;
|
|
default : assert(false);
|
|
}
|
|
} else { // dominating point not changed
|
|
switch(owner){
|
|
case avlseg::first : lastDomPointCount1++;
|
|
break;
|
|
case avlseg::second: lastDomPointCount2++;
|
|
break;
|
|
case avlseg::both : lastDomPointCount1++;
|
|
lastDomPointCount2++;
|
|
break;
|
|
default : assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool GetInt9M(Line const* const line1,
|
|
Line const* const line2,
|
|
Int9M& res,
|
|
const bool useCluster=false,
|
|
Cluster cluster = Cluster(),
|
|
const bool forceThrow = false ){
|
|
|
|
|
|
// we can only ommit the planesweep if both lines are empty
|
|
// disjointness of the lines is not sufficient to compute the
|
|
// result completely because it's not known if one of the line has
|
|
// any endpoints. For this reason, we ommit the bounding box check
|
|
|
|
res.SetValue(0);
|
|
res.SetEE(true);
|
|
|
|
if(line1->IsEmpty() && line2->IsEmpty()){
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
}else {
|
|
return true; // no further intersections can be found
|
|
}
|
|
}
|
|
|
|
bool done = false;
|
|
if(useCluster){
|
|
int cb = cluster.checkBoxes(line1->BoundingBox(),line1->IsEmpty(),
|
|
line2->BoundingBox(),line2->IsEmpty());
|
|
if(cb==1) {
|
|
return true;
|
|
}
|
|
if(cb==2){
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(line1->IsEmpty()){
|
|
// line2 is non empty
|
|
SetEI(res,useCluster,cluster,done);
|
|
if(useCluster){
|
|
Int9M m(false,false,false, false,false,false, true,true,true);
|
|
cluster.Restrict(m,false,false);
|
|
}
|
|
}
|
|
if(line2->IsEmpty()){
|
|
SetIE(res,useCluster,cluster,done);
|
|
if(useCluster){
|
|
Int9M m(false, false, true, false,false,true, false,false,true);
|
|
cluster.Restrict(m,false,false);
|
|
}
|
|
}
|
|
|
|
if(!line1->IsEmpty() && !line2->IsEmpty()){
|
|
if(!line1->BoundingBox().Intersects(line2->BoundingBox()) && useCluster){
|
|
Int9M m(false,false,true,false,false,true,true,true,true);
|
|
cluster.Restrict(m,false,false);
|
|
}
|
|
}
|
|
|
|
if(useCluster){
|
|
if(cluster.IsEmpty()){
|
|
return false;
|
|
}
|
|
if(cluster.isExtension(res)){
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// initialise the sweep state structure
|
|
avltree::AVLTree<avlseg::AVLSegment> sss;
|
|
// initialize priority queues for remaining parts of splitted
|
|
// segments
|
|
priority_queue<avlseg::ExtendedHalfSegment,
|
|
vector<avlseg::ExtendedHalfSegment>,
|
|
greater<avlseg::ExtendedHalfSegment> > q1;
|
|
priority_queue<avlseg::ExtendedHalfSegment,
|
|
vector<avlseg::ExtendedHalfSegment>,
|
|
greater<avlseg::ExtendedHalfSegment> > q2;
|
|
|
|
int pos1=0;
|
|
int pos2=0;
|
|
int size1 = line1->Size();
|
|
int size2 = line2->Size();
|
|
|
|
avlseg::ExtendedHalfSegment nextHs;
|
|
|
|
avlseg::ownertype owner;
|
|
|
|
avlseg::AVLSegment* leftN=0;
|
|
avlseg::AVLSegment* rightN=0;
|
|
avlseg::AVLSegment* member=0;
|
|
|
|
Point lastDomPoint;
|
|
int lastDomPointCount1 = 0;
|
|
int lastDomPointCount2 = 0;
|
|
avlseg::AVLSegment left1,right1,left2,right2,common;
|
|
int src=0;
|
|
avlseg::AVLSegment tmpL,tmpR;
|
|
|
|
bool empty1 = false; // flag indicating end of line 1
|
|
bool empty1eval = false; // empty1 already evaluated
|
|
bool empty2 = false;
|
|
bool empty2eval = false;
|
|
|
|
|
|
while(!done &&
|
|
((owner=selectNext(*line1,pos1,*line2,
|
|
pos2,q1,q2,nextHs,src))!=avlseg::none) ){
|
|
|
|
avlseg::AVLSegment current(nextHs,owner);
|
|
|
|
// debug::start
|
|
// cout << "process segment " << current << " "
|
|
// << (nextHs.IsLeftDomPoint()
|
|
// ?"avlseg::avlseg::LEFT"
|
|
// :"avlseg::RIGHT") << endl;
|
|
// debug::end
|
|
empty1 = pos1>=size1 && q1.empty() &&
|
|
lastDomPointCount1==0 &&
|
|
owner!=avlseg::first;
|
|
empty2 = pos2>=size2 && q2.empty() &&
|
|
lastDomPointCount2==0 &&
|
|
owner!=avlseg::second;
|
|
|
|
|
|
// try to find an overlapping segment in sss
|
|
member = sss.getMember(current,leftN,rightN);
|
|
if(leftN){
|
|
tmpL = *leftN;
|
|
leftN = &tmpL;
|
|
}
|
|
if(rightN){
|
|
tmpR = *rightN;
|
|
rightN = &tmpR;
|
|
}
|
|
if(nextHs.IsLeftDomPoint()){ // left event
|
|
if(member){ // overlapping segment found in sss
|
|
if(member->getOwner()==avlseg::both || member->getOwner()==owner){
|
|
// member and owner comes from the same line
|
|
// create events for the remaining part
|
|
if(!AlmostEqual(member->getX2(),current.getX2()) &&
|
|
current.getX2() > member->getX2()){
|
|
// current is an extension of member
|
|
current.splitAt(member->getX2(),member->getY2(),left1,right1);
|
|
insertEvents(right1,true,true,q1,q2);
|
|
} // otherwise we can ignore current
|
|
} else { // owner of member and current are different
|
|
// there is a common inner part
|
|
SetII(res,useCluster,cluster,done);
|
|
|
|
int parts = member->split(current, left1, common, right1);
|
|
|
|
// remove the old entry in sss
|
|
sss.remove(*member);
|
|
member = &common;
|
|
// insert left and common to sss
|
|
if(parts&avlseg::LEFT){
|
|
bool ok = sss.insert(left1);
|
|
assert(ok);
|
|
insertEvents(left1,false,true,q1,q2);
|
|
}
|
|
assert(parts & avlseg::COMMON);
|
|
|
|
bool ok = sss.insert(common);
|
|
assert(ok);
|
|
insertEvents(common,false,true,q1,q2);
|
|
// create events for right
|
|
if(parts & avlseg::RIGHT){
|
|
insertEvents(right1,true,true,q1,q2);
|
|
}
|
|
|
|
Point newDomPoint = nextHs.GetDomPoint();
|
|
avlseg::ownertype owner2 = owner;
|
|
if(parts & avlseg::LEFT){
|
|
owner2 = avlseg::both;
|
|
}
|
|
updateDomPoints_Line_Line(lastDomPoint,newDomPoint,
|
|
lastDomPointCount1,lastDomPointCount2,owner2, res,
|
|
useCluster, cluster, done);
|
|
|
|
}
|
|
} else { // no overlapping segment stored in sss
|
|
splitByNeighbour(sss,current,leftN,q1,q2, forceThrow);
|
|
splitByNeighbour(sss,current,rightN,q1,q2, forceThrow);
|
|
updateDomPoints_Line_Line(lastDomPoint,nextHs.GetDomPoint(),
|
|
lastDomPointCount1, lastDomPointCount2,
|
|
owner,res,useCluster,cluster,done);
|
|
bool ok = sss.insert(current);
|
|
assert(ok);
|
|
}
|
|
|
|
} else { // right Event
|
|
// check if current is stored in sss
|
|
if(member && member->exactEqualsTo(current)){
|
|
|
|
Point newDomPoint = nextHs.GetDomPoint();
|
|
|
|
updateDomPoints_Line_Line(lastDomPoint,newDomPoint,
|
|
lastDomPointCount1,lastDomPointCount2,
|
|
member->getOwner(), res,
|
|
useCluster, cluster, done);
|
|
|
|
avlseg::AVLSegment copy(*member);
|
|
sss.remove(current);
|
|
member = ©
|
|
|
|
switch(member->getOwner()){
|
|
case avlseg::first: SetIE(res,useCluster,cluster,done);
|
|
break;
|
|
case avlseg::second: SetEI(res,useCluster,cluster,done);
|
|
break;
|
|
case avlseg::both : SetII(res,useCluster,cluster,done);
|
|
break;
|
|
default: assert(false);
|
|
}
|
|
splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow);
|
|
} // otherwise current comes from a splitted segment and is ignored
|
|
}
|
|
done = done || res.IsFull();
|
|
|
|
if(empty1) { // end of line1 reached
|
|
if(res.GetEI() && res.GetEB()){
|
|
done = true;
|
|
} else if(useCluster && !empty1eval){
|
|
Int9M tmp = res;
|
|
tmp.SetEI(true);
|
|
tmp.SetEB(true);
|
|
cluster.Restrict(tmp,false,false);
|
|
if(cluster.IsEmpty()){
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(empty2) { // end of line1 reached
|
|
if(res.GetIE() && res.GetBE()){
|
|
done = true;
|
|
} else if(useCluster && !empty2eval){
|
|
Int9M tmp = res;
|
|
tmp.SetIE(true);
|
|
tmp.SetBE(true);
|
|
cluster.Restrict(tmp,false,false);
|
|
if(cluster.IsEmpty()){
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} // end of sweep
|
|
|
|
// create a point which is different to the last domPoint
|
|
Point newDP(lastDomPoint);
|
|
newDP.Translate(100,0);
|
|
updateDomPoints_Line_Line(lastDomPoint, newDP,
|
|
lastDomPointCount1, lastDomPointCount2,
|
|
avlseg::first,res,useCluster,cluster,done);
|
|
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
|
|
} // line x line
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
~updateDomPointInfo[_]Line[_]Region~
|
|
|
|
Does the things required by the switch from ~lastDomPoint~
|
|
to ~newDomPoint~.
|
|
|
|
*/
|
|
|
|
void updateDomPointInfo_Line_Region(Point& lastDomPoint,
|
|
const Point& newDomPoint,
|
|
int& count_line,
|
|
int& count_region,
|
|
int& lastCoverage_Num,
|
|
const int newCoverageNum,
|
|
const avlseg::ownertype owner,
|
|
Int9M& res,
|
|
const bool useCluster,
|
|
Cluster& cluster,
|
|
bool& done){
|
|
|
|
int sum = count_line + count_region;
|
|
if(sum == 0 || !AlmostEqual(newDomPoint,lastDomPoint)){
|
|
// dominating point changed
|
|
if(count_line==0) { // exterior of the line
|
|
if(count_region>0){ // boundary of the region
|
|
SetEB(res,useCluster,cluster,done);
|
|
} else {
|
|
; // SetEE(res,useCluster,cluster,done);
|
|
}
|
|
} else if(count_line == 1) { // boundary of the line
|
|
if(count_region>0){ // boundary of the region
|
|
SetBB(res,useCluster,cluster,done);
|
|
} else {
|
|
if(lastCoverage_Num==0){
|
|
SetBE(res,useCluster,cluster,done);
|
|
} else {
|
|
SetBI(res,useCluster,cluster,done);
|
|
}
|
|
}
|
|
} else { // interior of the line
|
|
if(count_region > 0){
|
|
SetIB(res,useCluster,cluster,done);
|
|
} else {
|
|
if(lastCoverage_Num==0){
|
|
SetIE(res,useCluster,cluster,done);
|
|
} else {
|
|
SetII(res,useCluster,cluster,done);
|
|
}
|
|
}
|
|
}
|
|
lastDomPoint = newDomPoint;
|
|
switch(owner){
|
|
case avlseg::first : count_line = 1;
|
|
count_region = 0;
|
|
break;
|
|
case avlseg::second: count_line = 0;
|
|
count_region = 1;
|
|
break;
|
|
case avlseg::both : count_line = 1;
|
|
count_region = 1;
|
|
break;
|
|
default : assert(false);
|
|
}
|
|
} else { // dompoint was not changed
|
|
switch(owner){
|
|
case avlseg::first : count_line++;
|
|
break;
|
|
case avlseg::second: count_region++;
|
|
break;
|
|
case avlseg::both : count_line++;
|
|
count_region++;
|
|
break;
|
|
default : assert(false);
|
|
}
|
|
}
|
|
lastCoverage_Num = newCoverageNum;
|
|
}
|
|
|
|
|
|
/*
|
|
~GetInt9M~ ~line~ [x] ~region~
|
|
|
|
*/
|
|
|
|
bool GetInt9M(Line const* const line,
|
|
Region const* const region,
|
|
Int9M& res,
|
|
const bool useCluster=false,
|
|
Cluster cluster = Cluster(),
|
|
const bool forceThrow = false){
|
|
|
|
res.SetValue(0);
|
|
res.SetEE(true);
|
|
if(line->IsEmpty()){
|
|
if(region->IsEmpty()){
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
} else {
|
|
res.SetEI(true);
|
|
res.SetEB(true);
|
|
res.SetEE(true);
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!region->IsEmpty()){
|
|
res.SetEI(true);
|
|
}
|
|
|
|
Rectangle<2> bbox1 = line->BoundingBox();
|
|
Rectangle<2> bbox2 = region->BoundingBox();
|
|
|
|
if(useCluster){
|
|
int cb = cluster.checkBoxes(bbox1, line->IsEmpty(),
|
|
bbox2, region->IsEmpty());
|
|
if(cb==1) {
|
|
return true;
|
|
}
|
|
if(cb==2){
|
|
return false;
|
|
}
|
|
cluster.Restrict(res,true,false);
|
|
if(cluster.IsEmpty() ){
|
|
return false;
|
|
}
|
|
if(cluster.isExtension(res)){
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(!bbox1.IntersectsUD(bbox2)){
|
|
res.SetIE(true); // line is not empty
|
|
if(!region->IsEmpty()){
|
|
res.SetEB(true);
|
|
}
|
|
if(useCluster){
|
|
Int9M m(false,false,true, false, false, true, true,true,true);
|
|
cluster.Restrict(m,false,false);
|
|
}
|
|
}
|
|
|
|
if(useCluster){
|
|
cluster.Restrict(res,true,false);
|
|
if(region->IsEmpty()){
|
|
Int9M m(false,false,true,false,false,true,false,false,true);
|
|
cluster.Restrict(m,false,false);
|
|
}
|
|
if(cluster.IsEmpty()){
|
|
return false;
|
|
}
|
|
if(cluster.isExtension(res)){
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// initialise the sweep state structure
|
|
avltree::AVLTree<avlseg::AVLSegment> sss;
|
|
// initialize priority queues for remaining parts of splitted
|
|
// segments
|
|
priority_queue<avlseg::ExtendedHalfSegment,
|
|
vector<avlseg::ExtendedHalfSegment>,
|
|
greater<avlseg::ExtendedHalfSegment> > q1;
|
|
priority_queue<avlseg::ExtendedHalfSegment,
|
|
vector<avlseg::ExtendedHalfSegment>,
|
|
greater<avlseg::ExtendedHalfSegment> > q2;
|
|
|
|
int pos1=0;
|
|
int pos2=0;
|
|
|
|
bool done = false;
|
|
|
|
avlseg::ExtendedHalfSegment nextHs;
|
|
|
|
avlseg::ownertype owner;
|
|
|
|
avlseg::AVLSegment* leftN=0;
|
|
avlseg::AVLSegment* rightN=0;
|
|
avlseg::AVLSegment* member=0;
|
|
|
|
Point lastDomPoint;
|
|
int lastDomPointCount1 = 0;
|
|
int lastDomPointCount2 = 0;
|
|
avlseg::AVLSegment left1,right1,left2,right2,common;
|
|
avlseg::AVLSegment tmpL,tmpR;
|
|
|
|
int src=0;
|
|
int lastCoverageNum = 0;
|
|
|
|
// plane sweep
|
|
while(!done &&
|
|
((owner=selectNext(*line,pos1,*region,
|
|
pos2,q1,q2,nextHs,src))!=avlseg::none)){
|
|
avlseg::AVLSegment current(nextHs,owner);
|
|
|
|
member = sss.getMember(current,leftN,rightN);
|
|
if(leftN){
|
|
tmpL = *leftN;
|
|
leftN = &tmpL;
|
|
}
|
|
if(rightN){
|
|
tmpR = *rightN;
|
|
rightN = &tmpR;
|
|
}
|
|
avlseg::ownertype owner2 = owner;
|
|
|
|
if(nextHs.IsLeftDomPoint()){ // left end point
|
|
if(member){ // overlapping segment found
|
|
if(member->getOwner()==avlseg::both || member->getOwner()==owner){
|
|
// current is part of member
|
|
if( (member->getX2() < current.getX2()) &&
|
|
!AlmostEqual(member->getX2(), current.getX2())){
|
|
current.splitAt(member->getX2(),member->getY2(),left1,right1);
|
|
insertEvents(right1,true,true,q1,q2);
|
|
} // otherwise there is nothing to do
|
|
} else { // stored segment come from the other spatial object
|
|
SetIB(res,useCluster,cluster,done);
|
|
int parts = member->split(current,left1,common,right1);
|
|
sss.remove(*member);
|
|
member = &common;
|
|
if(parts & avlseg::LEFT){
|
|
sss.insert(left1);
|
|
insertEvents(left1,false,true,q1,q2);
|
|
}
|
|
assert(parts & avlseg::COMMON);
|
|
// update coverage numbers
|
|
if(owner==avlseg::first){ // the line
|
|
common.con_below = member->con_below;
|
|
common.con_above = member->con_above;
|
|
} else { // a region
|
|
common.con_below = member->con_below;
|
|
if(current.isVertical()){
|
|
common.con_above = current.con_below;
|
|
} else { // non-vertical
|
|
if(nextHs.attr.insideAbove){
|
|
common.con_above = common.con_below +1;
|
|
} else {
|
|
common.con_above = common.con_below -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
assert(current.con_below + current.con_above >= 0);
|
|
assert(current.con_below + current.con_above <= 1);
|
|
|
|
sss.insert(common);
|
|
insertEvents(common,false,true,q1,q2);
|
|
if(parts & avlseg::RIGHT){
|
|
insertEvents(right1,true,true,q1,q2);
|
|
}
|
|
|
|
|
|
if(parts & avlseg::LEFT){ // this dominating point comes from both
|
|
// halfsegments
|
|
owner2 = avlseg::both;
|
|
}
|
|
// update counter for dominating points
|
|
Point domPoint = nextHs.GetDomPoint();
|
|
updateDomPointInfo_Line_Region(lastDomPoint, domPoint,
|
|
lastDomPointCount1,
|
|
lastDomPointCount2,
|
|
lastCoverageNum,current.con_below,
|
|
owner2,res,
|
|
useCluster, cluster, done);
|
|
}
|
|
} else { // no overlapping segment found
|
|
// check if current or an existing segment must be divided
|
|
splitByNeighbour(sss,current,leftN,q1,q2, forceThrow);
|
|
splitByNeighbour(sss,current,rightN,q1,q2, forceThrow);
|
|
// update coverage numbers
|
|
if(owner==avlseg::second){ // the region
|
|
bool iac = current.getInsideAbove();
|
|
if(leftN && current.extends(*leftN)){
|
|
current.con_below = leftN->con_below;
|
|
current.con_above = leftN->con_above;
|
|
}else{
|
|
if(leftN && leftN->isVertical()){
|
|
current.con_below = leftN->con_below;
|
|
} else if(leftN){
|
|
current.con_below = leftN->con_above;
|
|
} else {
|
|
current.con_below = 0;
|
|
}
|
|
if(iac){
|
|
current.con_above = current.con_below+1;
|
|
} else {
|
|
current.con_above = current.con_below-1;
|
|
}
|
|
}
|
|
} else { // the line
|
|
if(leftN){
|
|
if(current.extends(*leftN)){
|
|
current.con_below = leftN->con_below;
|
|
current.con_above = leftN->con_above;
|
|
} else if(leftN->isVertical()){
|
|
current.con_below = leftN->con_below;
|
|
} else {
|
|
current.con_below = leftN->con_above;
|
|
}
|
|
} else { // no left neighbour found
|
|
current.con_below = 0;
|
|
}
|
|
current.con_above = current.con_below;
|
|
}
|
|
|
|
|
|
// update dominating points
|
|
Point domPoint = nextHs.GetDomPoint();
|
|
updateDomPointInfo_Line_Region(lastDomPoint, domPoint,
|
|
lastDomPointCount1,
|
|
lastDomPointCount2,
|
|
lastCoverageNum,
|
|
current.con_below,
|
|
owner2,
|
|
res, useCluster, cluster, done);
|
|
sss.insert(current);
|
|
}
|
|
} else { // right event
|
|
if(member && member->exactEqualsTo(current)){
|
|
avlseg::AVLSegment tmp = *member;
|
|
sss.remove(*member);
|
|
member = &tmp;
|
|
splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow);
|
|
// update dominating point information
|
|
Point domPoint = nextHs.GetDomPoint();
|
|
updateDomPointInfo_Line_Region(lastDomPoint, domPoint,
|
|
lastDomPointCount1,
|
|
lastDomPointCount2,
|
|
lastCoverageNum,
|
|
member->con_below,
|
|
member->getOwner(), res,
|
|
useCluster,cluster, done);
|
|
// detect intersections
|
|
switch(member->getOwner()){
|
|
case avlseg::first : if(member->con_above==0){ // the line
|
|
SetIE(res,useCluster,cluster,done);
|
|
} else {
|
|
SetII(res,useCluster,cluster,done);
|
|
}
|
|
break;
|
|
case avlseg::second: SetEB(res,useCluster,cluster,done);
|
|
break;
|
|
case avlseg::both : SetIB(res,useCluster,cluster,done);
|
|
break;
|
|
default : assert(false);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// sweep done, check the last dominating point
|
|
Point domPoint(lastDomPoint);
|
|
domPoint.Translate(100,0);
|
|
updateDomPointInfo_Line_Region(lastDomPoint,domPoint,
|
|
lastDomPointCount1, lastDomPointCount2,
|
|
lastCoverageNum, 0, avlseg::both, res,
|
|
useCluster,
|
|
cluster,done);
|
|
|
|
if(useCluster){
|
|
return cluster.Contains(res);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
8 Integrating Operators in [secondo]
|
|
|
|
8.1 Type Mapping Functions
|
|
|
|
*/
|
|
|
|
|
|
/*
|
|
~TopPredTypeMap~
|
|
|
|
This function is the type mapping for the ~toppred~ operator.
|
|
The signature of this operator is:
|
|
t1 [x] t2 [x] cluster [->] bool
|
|
|
|
where t1, t2 in [{]point, points, line, region[}].
|
|
|
|
|
|
*/
|
|
ListExpr TopPredTypeMap(ListExpr args){
|
|
|
|
if(nl->ListLength(args)!=3){
|
|
ErrorReporter::ReportError("three arguments required");
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr cl = nl->Third(args);
|
|
if(!nl->IsEqual(cl,"cluster")){
|
|
ErrorReporter::ReportError("the third argument must"
|
|
" be of type cluster\n");
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr o1 = nl->First(args);
|
|
ListExpr o2 = nl->Second(args);
|
|
if(!IsSpatialType(o1)){
|
|
ErrorReporter::ReportError("The first argument must be a spatial type");
|
|
return nl->TypeError();
|
|
}
|
|
if(!IsSpatialType(o2)){
|
|
ErrorReporter::ReportError("The second argument"
|
|
" must be a spatial type");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
|
|
|
|
/*
|
|
~TopRelTypeMap~
|
|
|
|
This function is the type mapping for the ~toprel~ operator.
|
|
The signature is t1 [x] t2 [->] int9m
|
|
where t1, t2 in [{]point, points, line, region[}]
|
|
|
|
*/
|
|
|
|
ListExpr TopRelTypeMap(ListExpr args){
|
|
if(nl->ListLength(args)!=2){
|
|
ErrorReporter::ReportError("two arguments expected");
|
|
return nl->TypeError();
|
|
}
|
|
if(!IsSpatialType(nl->First(args))
|
|
|| !IsSpatialType(nl->Second(args))){
|
|
ErrorReporter::ReportError("Spatial types expected");
|
|
return (nl->TypeError());
|
|
}
|
|
return nl->SymbolAtom("int9m");
|
|
}
|
|
|
|
/*
|
|
~StdPredTypeMap~
|
|
|
|
t1 [x] t2 [->] bool,
|
|
|
|
where t1,t2 in [{]point, points, line, region[}].
|
|
|
|
*/
|
|
ListExpr StdPredTypeMap(ListExpr args){
|
|
if(nl->ListLength(args)!=2){
|
|
ErrorReporter::ReportError("two arguments expected");
|
|
return nl->SymbolAtom(Symbol::TYPEERROR());
|
|
}
|
|
if(!IsSpatialType(nl->First(args))
|
|
|| !IsSpatialType(nl->Second(args))){
|
|
ErrorReporter::ReportError("Spatial types expected");
|
|
return (nl->TypeError());
|
|
}
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
8.2 Value Mappings
|
|
|
|
~TopRel~
|
|
|
|
This value mapping can be instantiated with all combinations
|
|
where a function ~GetInt9M~ exists for the type combination
|
|
~type1~ [x] ~type2~.
|
|
|
|
*/
|
|
template<class type1, class type2>
|
|
int TopRel(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
type1* p1 = static_cast<type1*>(args[0].addr);
|
|
type2* p2 = static_cast<type2*>(args[1].addr);
|
|
Int9M matrix;
|
|
GetInt9M(p1,p2,matrix);
|
|
*(static_cast<Int9M*>(result.addr)) = matrix;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
This function is symmetrical to the ~TopRel~ function. This function avoids
|
|
the implementation of symmetric ~GetInt9M~ functions.
|
|
|
|
*/
|
|
template<class type1, class type2>
|
|
int TopRelSym(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
type1* p1 = static_cast<type1*>(args[0].addr);
|
|
type2* p2 = static_cast<type2*>(args[1].addr);
|
|
Int9M matrix(0);
|
|
GetInt9M(p2,p1,matrix);
|
|
matrix.Transpose();
|
|
*(static_cast<Int9M*>(result.addr))=matrix; // correct the swapping
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Value Mapping for the ~toppred~ operator
|
|
|
|
|
|
*/
|
|
template<class t1, class t2>
|
|
int TopPred(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
t1* v1 = static_cast<t1*>(args[0].addr);
|
|
t2* v2 = static_cast<t2*>(args[1].addr);
|
|
Cluster* cluster = static_cast<Cluster*>(args[2].addr);
|
|
if(!cluster->IsDefined() || !v1->IsDefined() || !v2->IsDefined()){
|
|
(static_cast<CcBool*>(result.addr))->Set(false,false);
|
|
return 0;
|
|
}
|
|
|
|
Int9M matrix;
|
|
try{
|
|
bool res = GetInt9M(v1,v2,matrix,true,*cluster);
|
|
(static_cast<CcBool*>(result.addr))->Set(true,res);
|
|
} catch(...){
|
|
cerr << "Toppred failed, set to undefined, TODO: robust toppred" << endl;
|
|
(static_cast<CcBool*>(result.addr))->Set(false,false);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
As for the ~toprel~ opererator there is a symmetric value mapping.
|
|
|
|
*/
|
|
template<class t1, class t2>
|
|
int TopPredSym(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
t1* v1 = static_cast<t1*>(args[0].addr);
|
|
t2* v2 = static_cast<t2*>(args[1].addr);
|
|
Cluster* cluster = static_cast<Cluster*>(args[2].addr);
|
|
if(!cluster->IsDefined()){// name not found within group
|
|
(static_cast<CcBool*>(result.addr))->Set(false,false);
|
|
return 0;
|
|
}
|
|
Int9M matrix;
|
|
Cluster tmp(cluster);
|
|
tmp.Transpose();
|
|
bool res = GetInt9M(v2,v1,matrix,true,tmp);
|
|
(static_cast<CcBool*>(result.addr))->Set(true,res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
template<class t1, class t2>
|
|
int TopPredSym2(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
t1* v1 = static_cast<t1*>(args[0].addr);
|
|
t2* v2 = static_cast<t2*>(args[1].addr);
|
|
Cluster* cluster = static_cast<Cluster*>(args[2].addr);
|
|
if(!cluster->IsDefined()){// name not found within group
|
|
(static_cast<CcBool*>(result.addr))->Set(false,false);
|
|
return 0;
|
|
}
|
|
Int9M matrix;
|
|
bool res = GetInt9M(v2,v1,matrix,true,*cluster,true);
|
|
(static_cast<CcBool*>(result.addr))->Set(true,res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
~Standard Topological Predicates~
|
|
|
|
|
|
In the following section, we define predicates
|
|
implementing our imagination of topological predicates.
|
|
They can be used without an explicit definition of a cluster.
|
|
|
|
|
|
*/
|
|
|
|
/*
|
|
~Standard Cluster~
|
|
|
|
For each standard predicate, we hold a variable of
|
|
type cluster. The variables are initialized once
|
|
at the algebra initialization.
|
|
|
|
*/
|
|
|
|
static Cluster cl_disjoint;
|
|
static Cluster cl_adjacent;
|
|
static Cluster cl_overlap;
|
|
static Cluster cl_covers;
|
|
static Cluster cl_coveredBy;
|
|
static Cluster cl_inside;
|
|
static Cluster cl_contains;
|
|
static Cluster cl_equal;
|
|
|
|
static Cluster cl_wcontains; // contains || covers
|
|
|
|
|
|
/*
|
|
Value Mappings for standard predicates
|
|
|
|
*/
|
|
template<class t1, class t2>
|
|
int StdPred(Word* args, Word& result, int message,
|
|
Word& local, Supplier s, Cluster& cl){
|
|
|
|
result = qp->ResultStorage(s);
|
|
t1* v1 = static_cast<t1*>(args[0].addr);
|
|
t2* v2 = static_cast<t2*>(args[1].addr);
|
|
Int9M matrix;
|
|
bool res = GetInt9M(v1,v2,matrix,true,cl);
|
|
(static_cast<CcBool*>(result.addr))->Set(true,res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool overlaps(Region* r1, Region* r2){
|
|
Int9M matrix;
|
|
return GetInt9M(r1, r2, matrix, true, cl_overlap);
|
|
}
|
|
|
|
|
|
template<class t1, class t2>
|
|
int StdPredSym(Word* args, Word& result, int message,
|
|
Word& local, Supplier s, Cluster& cl){
|
|
|
|
result = qp->ResultStorage(s);
|
|
t1* v1 = static_cast<t1*>(args[0].addr);
|
|
t2* v2 = static_cast<t2*>(args[1].addr);
|
|
Int9M matrix;
|
|
Cluster tmp(cl);
|
|
tmp.Transpose();
|
|
bool res = GetInt9M(v2,v1,matrix,true,tmp);
|
|
(static_cast<CcBool*>(result.addr))->Set(true,res);
|
|
return 0;
|
|
}
|
|
|
|
template<class t1, class t2>
|
|
int TrAdjacentVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPred<t1,t2>(args,result,message,local,s,cl_adjacent);
|
|
}
|
|
|
|
template<class t1, class t2>
|
|
int TrAdjacentVMSymm(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPredSym<t1,t2>(args,result,message,local,s,cl_adjacent);
|
|
}
|
|
|
|
|
|
template<class t1, class t2>
|
|
int TrInsideVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPred<t1,t2>(args,result,message,local,s,cl_inside);
|
|
}
|
|
|
|
template<class t1, class t2>
|
|
int TrInsideVMSymm(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPredSym<t1,t2>(args,result,message,local,s,cl_inside);
|
|
}
|
|
|
|
|
|
template<class t1, class t2>
|
|
int TrCoversVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPred<t1,t2>(args,result,message,local,s,cl_covers);
|
|
}
|
|
|
|
template<class t1, class t2>
|
|
int TrCoversVMSymm(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPredSym<t1,t2>(args,result,message,local,s,cl_covers);
|
|
}
|
|
|
|
|
|
template<class t1, class t2>
|
|
int TrCoveredByVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPred<t1,t2>(args,result,message,local,s,cl_coveredBy);
|
|
}
|
|
|
|
template<class t1, class t2>
|
|
int TrCoveredByVMSymm(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPredSym<t1,t2>(args,result,message,local,s,cl_coveredBy);
|
|
}
|
|
|
|
|
|
template<class t1, class t2>
|
|
int TrEqualVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPred<t1,t2>(args,result,message,local,s,cl_equal);
|
|
}
|
|
|
|
template<class t1, class t2>
|
|
int TrEqualVMSymm(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPredSym<t1,t2>(args,result,message,local,s,cl_equal);
|
|
}
|
|
|
|
template<class t1, class t2>
|
|
int TrDisjointVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPred<t1,t2>(args,result,message,local,s,cl_disjoint);
|
|
}
|
|
|
|
template<class t1, class t2>
|
|
int TrDisjointVMSymm(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPredSym<t1,t2>(args,result,message,local,s,cl_disjoint);
|
|
}
|
|
|
|
|
|
template<class t1, class t2>
|
|
int TrOverlapVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPred<t1,t2>(args,result,message,local,s,cl_overlap);
|
|
}
|
|
|
|
template<class t1, class t2>
|
|
int TrOverlapVMSymm(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPredSym<t1,t2>(args,result,message,local,s,cl_overlap);
|
|
}
|
|
|
|
template<class t1, class t2>
|
|
int TrContainsVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPred<t1,t2>(args,result,message,local,s,cl_contains);
|
|
}
|
|
|
|
template<class t1, class t2>
|
|
int TrContainsVMSymm(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
return StdPredSym<t1,t2>(args,result,message,local,s,cl_contains);
|
|
}
|
|
|
|
|
|
/*
|
|
~initClusters~
|
|
|
|
This function initializes the standard clusters.
|
|
|
|
*/
|
|
|
|
static void initClusters(){
|
|
PredicateGroup pg(0);
|
|
pg.SetToDefault();
|
|
|
|
Cluster* cl = pg.GetClusterOf("disjoint");
|
|
assert(cl);
|
|
cl_disjoint = *cl;
|
|
delete cl;
|
|
|
|
cl = pg.GetClusterOf("meet");
|
|
assert(cl);
|
|
cl_adjacent = *cl;
|
|
delete cl;
|
|
|
|
|
|
cl = pg.GetClusterOf("overlap");
|
|
assert(cl);
|
|
cl_overlap = *cl;
|
|
delete cl;
|
|
|
|
cl = pg.GetClusterOf("covers");
|
|
assert(cl);
|
|
cl_covers = *cl;
|
|
delete cl;
|
|
|
|
cl = pg.GetClusterOf("coveredBy");
|
|
assert(cl);
|
|
cl_coveredBy = *cl;
|
|
delete cl;
|
|
|
|
cl = pg.GetClusterOf("inside");
|
|
assert(cl);
|
|
cl_inside = *cl;
|
|
delete cl;
|
|
|
|
cl = pg.GetClusterOf("contains");
|
|
assert(cl);
|
|
cl_contains = *cl;
|
|
delete cl;
|
|
|
|
cl = pg.GetClusterOf("equal");
|
|
assert(cl);
|
|
cl_equal = *cl;
|
|
delete cl;
|
|
|
|
cl_wcontains = cl_contains;
|
|
cl_wcontains.Union(&cl_covers);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
9.3 Operator Specifications
|
|
|
|
Here, the operator descriptions are defined.
|
|
|
|
*/
|
|
|
|
const string TopRelSpec =
|
|
"((\"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
" ( <text> {point, points, line, region} x "
|
|
" {points, points, line, region} -> int9m </text--->"
|
|
" \" toprel(_ _) \" "
|
|
" <text>computes the 9 intersection matrix describing the"
|
|
" topological relationship between the arguments</text--->"
|
|
" \" query toprel(reg1, reg2) \" ))";
|
|
|
|
const string TopPredSpec =
|
|
"((\"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
" ( <text> so1 x so2 x cluster -> bool "
|
|
" where o1, o2 in {point, points, line, region}</text--->"
|
|
" \" topred(_, _, _) \" "
|
|
" <text> checks whether the topological relationship between"
|
|
" the spatial objects is part of the cluster </text---> "
|
|
" \" query toppred(l1,r1,cl_equals) \" ))";
|
|
|
|
|
|
|
|
const string TrAdjacentSpec =
|
|
"((\"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
" ( <text> t1 x t2 -> bool, t1,t2 in {point, points, line, region}</text--->"
|
|
" \" _ tradjacent _ \" "
|
|
" <text> checks whether the arguments have exacly a common border </text--->"
|
|
" \" query r1 tradjacent r2 \" ))";
|
|
|
|
|
|
const string TrInsideSpec =
|
|
"((\"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
" ( <text> t1 x t2 -> bool, t1,t2 in {point, points, line, region}</text--->"
|
|
" \" _ trinside _ \" "
|
|
" <text> checks whether the first argument is"
|
|
" part of the second one </text---> "
|
|
" \" query r1 trinside r2 \" ))";
|
|
|
|
const string TrContainsSpec =
|
|
"((\"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
" ( <text> t1 x t2 -> bool, t1,t2 in {point, points, line, region}</text--->"
|
|
" \" _ trcontains _ \" "
|
|
" <text> checks whether the second argument is part of"
|
|
" the first one </text---> "
|
|
" \" query r1 trcontains r2 \" ))";
|
|
|
|
const string TrCoversSpec =
|
|
"((\"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
" ( <text> t1 x t2 -> bool, t1,t2 in {point, points, line, region}</text--->"
|
|
" \" _ trcovers _ \" "
|
|
" <text> checks whether the first argument is part of the second one and"
|
|
" the boundaries touches each other</text---> "
|
|
" \" query r1 trcovers r2 \" ))";
|
|
|
|
const string TrCoveredBySpec =
|
|
"((\"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
" ( <text> t1 x t2 -> bool, t1,t2 in {point, points, line, region}</text--->"
|
|
" \" _ trcoveredby _ \" "
|
|
" <text> checks whether the second argument is part of the first one and"
|
|
" the boundaries touches each other</text---> "
|
|
" \" query r1 trcoveredby r2 \" ))";
|
|
|
|
const string TrEqualSpec =
|
|
"((\"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
" ( <text> t1 x t2 -> bool, t1,t2 in {point, points, line, region}</text--->"
|
|
" \" _ trequal _ \" "
|
|
" <text> checks whether the arguments have the same geometry</text---> "
|
|
" \" query r1 trequal r2 \" ))";
|
|
|
|
|
|
const string TrOverlapSpec =
|
|
"((\"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
" ( <text> t1 x t2 -> bool, t1,t2 in {point, points, line, region}</text--->"
|
|
" \" _ toverlap _ \" "
|
|
" <text> checks whether the arguments have a common part ans "
|
|
"also exclusive ones</text---> "
|
|
" \" query r1 troverlaps r2 \" ))";
|
|
|
|
const string TrDisjointSpec =
|
|
"((\"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
" ( <text> t1 x t2 -> bool, t1,t2 in {point, points, line, region}</text--->"
|
|
" \" _ trdisjoint _ \" "
|
|
" <text> checks if the arguments have no common part </text--->"
|
|
" \" query r1 trdisjoint r2 \" ))";
|
|
|
|
/*
|
|
8.4 Value Mapping Arrays
|
|
|
|
The following arrays collect the value mappings to enable overloaded
|
|
operations.
|
|
|
|
*/
|
|
|
|
ValueMapping TopRelMap[] = {
|
|
TopRel<Point,Point> , TopRel<Points,Point>,
|
|
TopRelSym<Point,Points>, TopRel<Points,Points>, TopRel<Line,Point>,
|
|
TopRelSym<Point,Line>,TopRel<Line,Points>,TopRelSym<Points,Line>,
|
|
TopRel<Region,Point>,TopRelSym<Point,Region>,
|
|
TopRel<Region,Points>, TopRelSym<Points,Region>,TopRel<Region,Region>,
|
|
TopRel<Line,Line>, TopRel<Line,Region>, TopRelSym<Region,Line>};
|
|
|
|
ValueMapping TopPredMap[] = {
|
|
TopPred<Point,Point> , TopPred<Points,Point>,
|
|
TopPredSym<Point,Points>, TopPred<Points,Points>,
|
|
TopPred<Line,Point>, TopPredSym<Point,Line>,
|
|
TopPred<Line,Points>, TopPredSym<Points,Line>,
|
|
TopPred<Region,Point>, TopPredSym<Point,Region>,
|
|
TopPred<Region,Points>, TopPredSym<Points,Region>,
|
|
TopPred<Region,Region>, TopPred<Line,Line>,
|
|
TopPred<Line,Region>, TopPredSym<Region,Line> };
|
|
|
|
ValueMapping AdjacentMap[] = {
|
|
TrAdjacentVM<Point,Point> , TrAdjacentVM<Points,Point>,
|
|
TrAdjacentVMSymm<Point,Points>, TrAdjacentVM<Points,Points>,
|
|
TrAdjacentVM<Line,Point>, TrAdjacentVMSymm<Point,Line>,
|
|
TrAdjacentVM<Line,Points>, TrAdjacentVMSymm<Points,Line>,
|
|
TrAdjacentVM<Region,Point>, TrAdjacentVMSymm<Point,Region>,
|
|
TrAdjacentVM<Region,Points>, TrAdjacentVMSymm<Points,Region>,
|
|
TrAdjacentVM<Region,Region>, TrAdjacentVM<Line,Line>,
|
|
TrAdjacentVM<Line,Region>, TrAdjacentVMSymm<Region,Line>};
|
|
|
|
ValueMapping InsideMap[] = {
|
|
TrInsideVM<Point,Point> , TrInsideVM<Points,Point>,
|
|
TrInsideVMSymm<Point,Points>, TrInsideVM<Points,Points>,
|
|
TrInsideVM<Line,Point>, TrInsideVMSymm<Point,Line>,
|
|
TrInsideVM<Line,Points>, TrInsideVMSymm<Points,Line>,
|
|
TrInsideVM<Region,Point>, TrInsideVMSymm<Point,Region>,
|
|
TrInsideVM<Region,Points>, TrInsideVMSymm<Points,Region>,
|
|
TrInsideVM<Region,Region>, TrInsideVM<Line,Line>,
|
|
TrInsideVM<Line,Region>, TrInsideVMSymm<Region,Line>};
|
|
|
|
ValueMapping CoversMap[] = {
|
|
TrCoversVM<Point,Point> , TrCoversVM<Points,Point>,
|
|
TrCoversVMSymm<Point,Points>, TrCoversVM<Points,Points>,
|
|
TrCoversVM<Line,Point>, TrCoversVMSymm<Point,Line>,
|
|
TrCoversVM<Line,Points>, TrCoversVMSymm<Points,Line>,
|
|
TrCoversVM<Region,Point>, TrCoversVMSymm<Point,Region>,
|
|
TrCoversVM<Region,Points>, TrCoversVMSymm<Points,Region>,
|
|
TrCoversVM<Region,Region>, TrCoversVM<Line,Line>,
|
|
TrCoversVM<Line,Region>, TrCoversVMSymm<Region,Line>};
|
|
|
|
ValueMapping CoveredByMap[] = {
|
|
TrCoveredByVM<Point,Point> , TrCoveredByVM<Points,Point>,
|
|
TrCoveredByVMSymm<Point,Points>, TrCoveredByVM<Points,Points>,
|
|
TrCoveredByVM<Line,Point>, TrCoveredByVMSymm<Point,Line>,
|
|
TrCoveredByVM<Line,Points>, TrCoveredByVMSymm<Points,Line>,
|
|
TrCoveredByVM<Region,Point>, TrCoveredByVMSymm<Point,Region>,
|
|
TrCoveredByVM<Region,Points>, TrCoveredByVMSymm<Points,Region>,
|
|
TrCoveredByVM<Region,Region>, TrCoveredByVM<Line,Line>,
|
|
TrCoveredByVM<Line,Region>, TrCoveredByVMSymm<Region,Line>};
|
|
|
|
ValueMapping EqualMap[] = {
|
|
TrEqualVM<Point,Point> , TrEqualVM<Points,Point>,
|
|
TrEqualVMSymm<Point,Points>, TrEqualVM<Points,Points>,
|
|
TrEqualVM<Line,Point>, TrEqualVMSymm<Point,Line>,
|
|
TrEqualVM<Line,Points>, TrEqualVMSymm<Points,Line>,
|
|
TrEqualVM<Region,Point>, TrEqualVMSymm<Point,Region>,
|
|
TrEqualVM<Region,Points>, TrEqualVMSymm<Points,Region>,
|
|
TrEqualVM<Region,Region>, TrEqualVM<Line,Line>,
|
|
TrEqualVM<Line,Region>, TrEqualVMSymm<Region,Line>};
|
|
|
|
ValueMapping DisjointMap[] = {
|
|
TrDisjointVM<Point,Point> , TrDisjointVM<Points,Point>,
|
|
TrDisjointVMSymm<Point,Points>, TrDisjointVM<Points,Points>,
|
|
TrDisjointVM<Line,Point>, TrDisjointVMSymm<Point,Line>,
|
|
TrDisjointVM<Line,Points>, TrDisjointVMSymm<Points,Line>,
|
|
TrDisjointVM<Region,Point>, TrDisjointVMSymm<Point,Region>,
|
|
TrDisjointVM<Region,Points>, TrDisjointVMSymm<Points,Region>,
|
|
TrDisjointVM<Region,Region>, TrDisjointVM<Line,Line>,
|
|
TrDisjointVM<Line,Region>, TrDisjointVMSymm<Region,Line>};
|
|
|
|
ValueMapping OverlapMap[] = {
|
|
TrOverlapVM<Point,Point> , TrOverlapVM<Points,Point>,
|
|
TrOverlapVMSymm<Point,Points>, TrOverlapVM<Points,Points>,
|
|
TrOverlapVM<Line,Point>, TrOverlapVMSymm<Point,Line>,
|
|
TrOverlapVM<Line,Points>, TrOverlapVMSymm<Points,Line>,
|
|
TrOverlapVM<Region,Point>, TrOverlapVMSymm<Point,Region>,
|
|
TrOverlapVM<Region,Points>, TrOverlapVMSymm<Points,Region>,
|
|
TrOverlapVM<Region,Region>, TrOverlapVM<Line,Line>,
|
|
TrOverlapVM<Line,Region>, TrOverlapVMSymm<Region,Line>};
|
|
|
|
ValueMapping ContainsMap[] = {
|
|
TrContainsVM<Point,Point> , TrContainsVM<Points,Point>,
|
|
TrContainsVMSymm<Point,Points>, TrContainsVM<Points,Points>,
|
|
TrContainsVM<Line,Point>, TrContainsVMSymm<Point,Line>,
|
|
TrContainsVM<Line,Points>, TrContainsVMSymm<Points,Line>,
|
|
TrContainsVM<Region,Point>, TrContainsVMSymm<Point,Region>,
|
|
TrContainsVM<Region,Points>, TrContainsVMSymm<Points,Region>,
|
|
TrContainsVM<Region,Region>, TrContainsVM<Line,Line>,
|
|
TrContainsVM<Line,Region>, TrContainsVMSymm<Region,Line>};
|
|
|
|
|
|
|
|
|
|
/*
|
|
8.5 Selection Functions
|
|
|
|
*/
|
|
|
|
static int TopOpsSelect(ListExpr args){
|
|
string type1 = nl->SymbolValue(nl->First(args));
|
|
string type2 = nl->SymbolValue(nl->Second(args));
|
|
|
|
if( (type1==Point::BasicType()) && (type2==Point::BasicType())){
|
|
return 0;
|
|
}
|
|
if( (type1==Points::BasicType()) && (type2==Point::BasicType())){
|
|
return 1;
|
|
}
|
|
if((type1==Point::BasicType()) && (type2==Points::BasicType())){
|
|
return 2;
|
|
}
|
|
if((type1==Points::BasicType()) && (type2==Points::BasicType())){
|
|
return 3;
|
|
}
|
|
if((type1==Line::BasicType()) && (type2==Point::BasicType())){
|
|
return 4;
|
|
}
|
|
if((type1==Point::BasicType()) && (type2==Line::BasicType())){
|
|
return 5;
|
|
}
|
|
if((type1==Line::BasicType()) && (type2==Points::BasicType())){
|
|
return 6;
|
|
}
|
|
if((type1==Points::BasicType()) && (type2==Line::BasicType())){
|
|
return 7;
|
|
}
|
|
if((type1==Region::BasicType()) && (type2==Point::BasicType())){
|
|
return 8;
|
|
}
|
|
if((type1==Point::BasicType()) && (type2==Region::BasicType())){
|
|
return 9;
|
|
}
|
|
if( type1==Region::BasicType() && (type2==Points::BasicType())){
|
|
return 10;
|
|
}
|
|
if(type1==Points::BasicType() && (type2==Region::BasicType())){
|
|
return 11;
|
|
}
|
|
if(type1==Region::BasicType() && (type2==Region::BasicType())){
|
|
return 12;
|
|
}
|
|
if( (type1==Line::BasicType()) && (type2==Line::BasicType())){
|
|
return 13;
|
|
}
|
|
if( (type1==Line::BasicType()) && (type2==Region::BasicType())){
|
|
return 14;
|
|
}
|
|
if( (type1==Region::BasicType()) && (type2==Line::BasicType())){
|
|
return 15;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
8.6 Creating Instances of Operators
|
|
|
|
In this section instances of the algebra operators are built.
|
|
|
|
*/
|
|
Operator optoprel(
|
|
"toprel", // name
|
|
TopRelSpec, // specification
|
|
sizeof(TopRelMap)/sizeof(ValueMapping), // number of functions
|
|
TopRelMap, // array of value mappings
|
|
TopOpsSelect,
|
|
TopRelTypeMap
|
|
);
|
|
|
|
|
|
Operator toppred(
|
|
"toppred", // name
|
|
TopPredSpec, // specification
|
|
sizeof(TopPredMap)/sizeof(ValueMapping), // number of functions
|
|
TopPredMap, // array of value mappings
|
|
TopOpsSelect,
|
|
TopPredTypeMap
|
|
);
|
|
|
|
Operator trAdjacent(
|
|
"tradjacent", // name
|
|
TrAdjacentSpec, // specification
|
|
sizeof(AdjacentMap)/sizeof(ValueMapping), // number of functions
|
|
AdjacentMap, // array of value mappings
|
|
TopOpsSelect,
|
|
StdPredTypeMap
|
|
);
|
|
|
|
Operator trContains(
|
|
"trcontains", // name
|
|
TrContainsSpec, // specification
|
|
sizeof(ContainsMap)/sizeof(ValueMapping), // number of functions
|
|
ContainsMap, // array of value mappings
|
|
TopOpsSelect,
|
|
StdPredTypeMap
|
|
);
|
|
|
|
Operator trOverlap(
|
|
"troverlaps", // name
|
|
TrOverlapSpec, // specification
|
|
sizeof(OverlapMap)/sizeof(ValueMapping), // number of functions
|
|
OverlapMap, // array of value mappings
|
|
TopOpsSelect,
|
|
StdPredTypeMap
|
|
);
|
|
|
|
Operator trDisjoint(
|
|
"trdisjoint", // name
|
|
TrDisjointSpec, // specification
|
|
sizeof(DisjointMap)/sizeof(ValueMapping), // number of functions
|
|
DisjointMap, // array of value mappings
|
|
TopOpsSelect,
|
|
StdPredTypeMap
|
|
);
|
|
|
|
Operator trEqual(
|
|
"trequal", // name
|
|
TrEqualSpec, // specification
|
|
sizeof(EqualMap)/sizeof(ValueMapping), // number of functions
|
|
EqualMap, // array of value mappings
|
|
TopOpsSelect,
|
|
StdPredTypeMap
|
|
);
|
|
|
|
Operator trCoveredBy(
|
|
"trcoveredby", // name
|
|
TrCoveredBySpec, // specification
|
|
sizeof(CoveredByMap)/sizeof(ValueMapping), // number of functions
|
|
CoveredByMap, // array of value mappings
|
|
TopOpsSelect,
|
|
StdPredTypeMap
|
|
);
|
|
|
|
Operator trCovers(
|
|
"trcovers", // name
|
|
TrCoversSpec, // specification
|
|
sizeof(CoversMap)/sizeof(ValueMapping), // number of functions
|
|
CoversMap, // array of value mappings
|
|
TopOpsSelect,
|
|
StdPredTypeMap
|
|
);
|
|
|
|
Operator trInside(
|
|
"trinside", // name
|
|
TrInsideSpec, // specification
|
|
sizeof(InsideMap)/sizeof(ValueMapping), // number of functions
|
|
InsideMap, // array of value mappings
|
|
TopOpsSelect,
|
|
StdPredTypeMap
|
|
);
|
|
|
|
|
|
/*
|
|
8.7 Creating the Algebra
|
|
|
|
*/
|
|
class TopOpsAlgebra : public Algebra {
|
|
public:
|
|
TopOpsAlgebra() : Algebra() {
|
|
AddOperator(&optoprel);
|
|
AddOperator(&toppred);
|
|
AddOperator(&trAdjacent);
|
|
AddOperator(&trInside);
|
|
AddOperator(&trCovers);
|
|
AddOperator(&trCoveredBy);
|
|
AddOperator(&trEqual);
|
|
AddOperator(&trDisjoint);
|
|
AddOperator(&trOverlap);
|
|
AddOperator(&trContains);
|
|
}
|
|
~TopOpsAlgebra(){}
|
|
};
|
|
|
|
/*
|
|
Functions exported in the header file.
|
|
|
|
*/
|
|
bool wcontains(const Region* reg1, const Region* reg2){
|
|
Int9M dummy;
|
|
return topops::GetInt9M(reg1, reg2, dummy, true, topops::cl_wcontains);
|
|
}
|
|
|
|
} // end of namespace topops
|
|
|
|
|
|
|
|
|
|
/*
|
|
8.8 Initialization of the Algebra
|
|
|
|
*/
|
|
extern "C"
|
|
Algebra* InitializeTopOpsAlgebra( NestedList* nlRef, QueryProcessor* qpRef ) {
|
|
nl = nlRef;
|
|
qp = qpRef;
|
|
topops::initClusters();
|
|
return (new topops::TopOpsAlgebra());
|
|
}
|
|
|