Files
secondo/Algebras/Spatial/RegionTools.cpp
2026-01-23 17:03:45 +08:00

579 lines
14 KiB
C++

/*
----
This file is part of SECONDO.
Copyright (C) 2010, 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
----
1 RegionTools
This file contains functions handling with regions.
*/
#include "AVLSegment.h"
#include "RegionT.h"
#include "RegionTools.h"
#include "Algebras/TopOps/TopOpsAlgebra.h"
#include <vector>
using namespace std;
template<template<typename T> class Array>
Array<HalfSegment>* Split(const Array<HalfSegment>& segments,
const bool forceThrow);
/*
~reverseCycle~
Changes teh direction of a cycle.
*/
void reverseCycle(vector<Point>& cycle){
for(unsigned int i=0;i<cycle.size()/2; i++){
Point tmp = cycle[i];
cycle[i] = cycle[cycle.size()-(i+1)];
cycle[cycle.size()-(i+1)] = tmp;
}
}
/*
1.2 ~getDir~
Determines the direction of a cycle. If the cycle is in clockwise order, the
return value is true. If the cycle is directed counter clockwise, the result
will
be false.
*/
bool getDir(const vector<Point>& vp){
// determine the direction of cycle
int min = 0;
for(unsigned int i=1;i<vp.size();i++){
if(vp[i] < vp[min]){
min = i;
}
}
bool cw;
int s = vp.size();
if(AlmostEqual(vp[0],vp[vp.size()-1])){
s--;
}
Point a = vp[ (min - 1 + s ) % s ];
Point p = vp[min];
Point b = vp[ (min+1) % s];
if(AlmostEqual(a.GetX(),p.GetX())){ // a -> p vertical
if(a.GetY()>p.GetY()){
cw = false;
} else {
cw = true;
}
} else if(AlmostEqual(p.GetX(), b.GetX())){ //p -> b vertical
if(p.GetY()>b.GetY()){
cw = false;
} else {
cw = true;
}
} else { // both segments are non-vertical
double m_p_a = (a.GetY() - p.GetY()) / (a.GetX() - p.GetX());
double m_p_b = (b.GetY() - p.GetY()) / (b.GetX() - p.GetX());
if(m_p_a > m_p_b){
cw = false;
} else {
cw = true;
}
}
return cw;
}
/*
~separateCycles~
Finds simple subcycles within ~path~ and inserts each of them into
~cycles~.
*/
void separateCycles(const vector<Point>& path, vector <vector<Point> >& cycles){
if(path.size()<4){ // path too short for a polyon
return;
}
if(!AlmostEqual(path[0], path[path.size()-1])){
cout << "Ignore Dead end" << endl;
return;
}
set<Point> visitedPoints;
vector<Point> cycle;
for(unsigned int i=0;i<path.size(); i++){
Point p = path[i];
if(visitedPoints.find(p)!=visitedPoints.end()){ // subpath found
vector<Point> subpath;
subpath.clear();
subpath.push_back(p);
int pos = cycle.size()-1;
while(pos>=0 && !AlmostEqual(cycle[pos],p)){
subpath.push_back(cycle[pos]);
visitedPoints.erase(cycle[pos]);
pos--;
}
if(pos<0){
cerr << "internal error during searching a subpath" << endl;
return;
} else {
subpath.push_back(p); // close path;
if(subpath.size()>3){
cycles.push_back(subpath);
}
cycle.erase(cycle.begin()+(pos+1), cycle.end());
}
} else {
cycle.push_back(p);
visitedPoints.insert(p);
}
}
if(cycle.size()>3){
cycles.push_back(cycle);
}
}
/*
~getUnusedExtension~
Returns the position of an unused extension of the segemnt at position pos.
If no such segment exist, -1 is returned.
*/
int getUnusedExtension(const DbArray<HalfSegment>& segs,
const int pos,
const bool* used){
HalfSegment hs;
segs.Get(pos,hs);
Point dp = hs.GetDomPoint();
double dpx = dp.GetX();
bool done = false;
int pos2=pos-1;
while(pos2>=0 && !done){
if(used[pos2]){
pos2--;
} else {
segs.Get(pos2,hs);
if(AlmostEqual(dp,hs.GetDomPoint())){
return pos2;
} else {
double dpx2 = hs.GetDomPoint().GetX();
if(AlmostEqual(dpx,dpx2)){
pos2--;
} else { // outside the X-Range
done = true;
}
}
}
}
pos2 = pos+1;
while(pos2<segs.Size() ){
if(used[pos2]){
pos2++;
}else {
segs.Get(pos2,hs);
if(AlmostEqual(dp,hs.GetDomPoint())){
return pos2;
} else {
double dpx2 = hs.GetDomPoint().GetX();
if(AlmostEqual(dpx,dpx2)){
pos2++;
}else{
return -1;
}
}
}
}
return -1;
}
/*
~SetPartnerno~
Sets the partner for the halfsegments if the edegno is set correct.
*/
static void SetPartnerNo(DbArray<HalfSegment>& segs){
if(segs.Size()==0){
return;
}
int TMP[(segs.Size()+1)/2];
HalfSegment hs1;
HalfSegment hs2;
for(int i=0; i<segs.Size(); i++){
segs.Get(i,&hs1);
if(hs1.IsLeftDomPoint()){
TMP[hs1.attr.edgeno] = i;
} else {
int leftpos = TMP[hs1.attr.edgeno];
HalfSegment right = hs1;
right.attr.partnerno = leftpos;
segs.Get(leftpos,hs2);
HalfSegment left = hs2;
left.attr.partnerno = i;
segs.Put(i,right);
segs.Put(leftpos,left);
}
}
}
void addRegion(vector<pair<Region*, bool> > & regs, vector<Point>& cycle){
bool isFace = getDir(cycle);
if(cycle.size()<2){
return;
}
if(!AlmostEqual(cycle[0],cycle[cycle.size()-1])){ // cycle not closed
cycle.push_back(cycle[0]);
}
if(cycle.size() < 4){
return;
}
Line* hss = new Line(cycle.size()*2);
hss->StartBulkLoad();
size_t np = 0;
for(size_t i = 0; i<cycle.size()-1; i++){
if(AlmostEqual(cycle[i],cycle[i+1])){
cerr << "Found almost equal points in cycle " << endl;
cerr << "p1 : " << cycle[i] << endl;
cerr << "p2 : " << cycle[i+1] << endl;
cerr << "the complete cycle is " << endl;
for(unsigned int j=0;j<cycle.size();j++){
cerr << "p["<<j<<"] = " << cycle[j] << endl;
}
cerr << "-------------------------------" << endl;
} else {
np++;
HalfSegment hs(true, cycle[i],cycle[i+1]);
hs.attr.edgeno = i;
(*hss) += hs;
hs.SetLeftDomPoint(!hs.IsLeftDomPoint());
(*hss) += hs;
}
}
hss->EndBulkLoad();
Region* res= new Region(cycle.size()*2);
if(np>2) {
hss->Transform(*res);
}
hss->DeleteIfAllowed();
if(np > 2){
regs.push_back(pair<Region*,bool>(res, isFace));
}
}
Region* buildRegion2( vector< vector<Point> >& cycles){
//cout << "buildRegion2 called wth " << cycles.size() << " cycles " << endl;
Line* hss = new Line(80);
hss->StartBulkLoad();
int edgeno = 0;
for(size_t c = 0; c<cycles.size(); c++){
for(size_t i=0;i<cycles[c].size()-1; i++){
HalfSegment hs(true, cycles[c][i],cycles[c][i+1]);
hs.attr.edgeno = edgeno;
(*hss) += hs;
hs.SetLeftDomPoint(!hs.IsLeftDomPoint());
(*hss) += hs;
edgeno++;
}
}
hss->EndBulkLoad();
Region* res= new Region(hss->Size());
hss->Transform(*res);
hss->DeleteIfAllowed();
return res;
}
/*
Adds a single cycle region to regs if cycle is valid.
*/
void addRegion2(vector<pair<Region*, bool> >& regs, vector<Point>& cycle){
if(cycle.size()<4){ // at least 3 points
cerr << "Cycle with less than 3 different points detected" << endl;
return;
}
bool isFace = getDir(cycle);
// create a DBArray of halfsegments representing this cycle
DbArray<HalfSegment> segments1(0);
for(unsigned int i=0;i<cycle.size()-1;i++){
Point p1 = cycle[i];
Point p2 = cycle[i+1];
Point lp(0.0,0.0);
Point rp(0.0,0.0);
if(p1<p2){
lp = p1;
rp = p2;
} else {
lp = p2;
rp = p1;
}
HalfSegment hs1(true,lp,rp);
hs1.attr.edgeno = i;
hs1.attr.faceno = 0;
hs1.attr.cycleno =0;
hs1.attr.coverageno = 0;
hs1.attr.insideAbove = false;
hs1.attr.partnerno = -1;
HalfSegment hs2 = hs1;
hs2.SetLeftDomPoint(false);
segments1.Append(hs1);
segments1.Append(hs2);
}
segments1.Sort(HalfSegmentCompare);
// split the segments at crossings and overlappings
DbArray<HalfSegment>* segments = Split(segments1);
SetPartnerNo(*segments);
bool used[segments->Size()];
for(int i=0;i<segments->Size();i++){
used[i] = false;
}
// try to find cycles
vector< vector<Point> > cycles; // corrected (simple) cycles
vector<Point> path; // current path
set<Point> points; // index for points in path
bool subcycle; // a multiple point within the path?
for(int i=0; i< segments->Size(); i++){
if(!used[i]){ // start of a new path found
int pos = i;
path.clear();
points.clear();
bool done = false;
subcycle = false;
while(!done){
HalfSegment hs1;
segments->Get(pos,hs1);
Point dp = hs1.GetDomPoint();
Point ndp = hs1.GetSecPoint();
int partner = hs1.attr.partnerno;
path.push_back(dp);
points.insert(dp);
used[pos] = true;
used[partner] = true;
if(points.find(ndp)!=points.end()){ // (sub) cycle found
if(AlmostEqual(path[0],ndp)){ // cycle closed
path.push_back(ndp);
done = true;
} else { // subcycle found
subcycle = true;
}
}
if(!done){
// no cycle, try to extend
int nb = getUnusedExtension(*segments,partner,used);
if(nb>=0){ // extension found, continue
pos = nb;
} else { // dead end found, track back
cout << " ----> DEAD END FOUND <--- " << endl;
done = true; // should never occur
}
}
}
if(subcycle){
separateCycles(path,cycles);
} else if( (path.size()>3 ) && AlmostEqual(path[0],path[path.size()-1])){
vector<Point> cycle = path;
cycles.push_back(cycle);
} else {
cout << "remove invalid path of lengthh " << path.size() << endl;
}
}// new path found
} // for
segments->Destroy();
delete segments;
// build the region from the corrected cycles
Region* result = 0;
for(unsigned int i = 0; i< cycles.size();i++){
vector<Point> cycle = cycles[i];
bool cw = getDir(cycle);
Region* reg = new Region(0);
reg->StartBulkLoad();
for(unsigned int j=0;j<cycle.size()-1;j++){
Point lp(true),rp(true);
bool smallb;
smallb = (cycle[j] < cycle[j+1]);
if(smallb){
lp = cycle[j];
rp = cycle[j+1];
} else {
lp = cycle[j+1];
rp = cycle[j];
}
HalfSegment hs(true,lp,rp);
hs.attr.edgeno = j;
hs.attr.insideAbove = (cw && !smallb) || (!cw && smallb);
hs.attr.faceno=0;
hs.attr.cycleno = 0;
hs.attr.coverageno = 0;
HalfSegment hs2(hs);
hs2.SetLeftDomPoint(false);
*reg += hs;
*reg += hs2;
}
reg->EndBulkLoad();
if(!result){
result = reg;
} else {
Region* tmp = SetOp(*result,*reg,avlseg::union_op);
result->DeleteIfAllowed();
result = tmp;
reg->DeleteIfAllowed();
}
}
if(result){
regs.push_back(make_pair(result,isFace));
}
}
/*
~buildRegion~
Builds a region from a set of cycles.
*/
Region* buildRegion(vector< vector<Point> >& cycles){
// first step create a single region from each cycle
vector<pair<Region*, bool> > sc_regions; // single cycle regions
for(unsigned int i=0;i<cycles.size(); i++){
vector<Point> cycle = cycles[i];
addRegion(sc_regions,cycle);
}
// split the vector into faces and holes
vector<Region*> faces;
vector<Region*> holes;
for(unsigned int i=0;i<sc_regions.size();i++){
if(sc_regions[i].second){
faces.push_back(sc_regions[i].first);
} else {
holes.push_back(sc_regions[i].first);
}
}
// subtract all holes from each face if nessecary
vector<Region*> faces2;
for(unsigned int i=0;i<faces.size();i++){
Region* face = faces[i];
for(unsigned int j=0; j< holes.size(); j++){
Region* hole = holes[j];
if(face->BoundingBox().Intersects(hole->BoundingBox())){
if(!topops::wcontains(hole,face)){ // may be an island
Region* tmp = SetOp(*face,*hole,avlseg::difference_op);
face->DeleteIfAllowed();
face = tmp;
}
}
}
if((face->Size())!=0){
faces2.push_back(face);
} else { // face was removed completely
face->DeleteIfAllowed();
}
}
// the hole regions are not longer needed, delete them
for(unsigned int i=0;i<holes.size();i++){
holes[i]->DeleteIfAllowed();
}
if(faces2.size()<1){
cerr << "no face found within the cycles" << endl;
return new Region(0);
}
// build the union of all faces
Region* reg = faces2[0];
for(unsigned int i=1;i<faces2.size();i++){
Region* face2 = faces2[i];
Region* tmp = SetOp(*reg, *face2, avlseg::union_op);
reg->DeleteIfAllowed();
face2->DeleteIfAllowed();
reg = tmp;
}
return reg;
}