Files
secondo/Algebras/SpatialLR/Region.cpp

565 lines
14 KiB
C++
Raw Normal View History

2026-01-23 17:03:45 +08:00
/*
----
This file is part of SECONDO.
Copyright (C) 2016, 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
----
3.3 Implementation of ~Region2~ methods
Defines and includes.
*/
#include "Region.h"
#include "Curve.h"
#include "Crossings.h"
#include "NestedList.h"
#include "ListUtils.h"
#include "AreaOp.h"
#include "RectangleBB.h"
using namespace std;
namespace salr {
/*
Implementation of copy constructor.
*/
Region2::Region2(const Region2 &other) :
Attribute(other.IsDefined()),
curves(0),
coords(other.coords.Size()),
pointTypes(other.pointTypes.Size())
{
if (other.IsDefined()) {
for (int i = 0; i < other.pointTypes.Size(); i++) {
appendType(other.getPointType(i));
}
for (int i = 0; i < other.coords.Size(); i++) {
appendCoord(other.getCoord(i));
}
}
}
/*
Implementation of constructor using ~Line2~.
*/
Region2::Region2(const Line2 &line) :
Attribute(line.IsDefined()),
curves(0),
coords(line.coords.Size()),
pointTypes(line.pointTypes.Size())
{
if(line.hasQuads) {
cerr << "Can't transform line2 with quad-segment to region2" << endl;
SetDefined(false);
} else {
if (line.IsDefined() && line.pointTypes.Size() > 0) {
for (int i = 0; i < line.pointTypes.Size(); i++) {
appendType(line.getPointType(i));
}
for (int i = 0; i < line.coords.Size(); i++) {
appendCoord(line.getCoord(i));
}
}
}
}
/*
Implementation of constructor using ~Region~.
*/
Region2::Region2(const Region &r) :
Attribute(r.IsDefined()),
curves(0),
coords(r.Size()),
pointTypes(r.Size())
{
if (IsDefined()) {
HalfSegment hs;
for(int i = 0; i < r.Size(); i++) {
r.Get(i, hs);
if(hs.IsLeftDomPoint()) {
double x0 = hs.GetLeftPoint().GetX();
double y0 = hs.GetLeftPoint().GetY();
double x1 = hs.GetRightPoint().GetX();
double y1 = hs.GetRightPoint().GetY();
Curve::insertLine(&curves, x0, y0, x1, y1);
}
}
EOWindOp op;
op.calculate(&curves, NULL);
updateFLOBs();
clearCurves();
}
}
/*
Implementation of destructor.
*/
Region2::~Region2() {
}
/*
Implementation of system methods.
*/
const string Region2::BasicType() {
return "region2";
}
const bool Region2::checkType(const ListExpr type) {
return listutils::isSymbol(type, BasicType());
}
int Region2::NumOfFLOBs() const {
return 2;
}
Flob *Region2::GetFLOB(const int i) {
assert(i >= 0 && i < NumOfFLOBs());
if (i == 1) {
return &coords;
} else {
return &pointTypes;
}
}
int Region2::Compare(const Attribute *arg) const {
const Region2 &l = *((const Region2 *) arg);
if (!IsDefined() && !l.IsDefined()) {
return 0;
}
if (!IsDefined()) {
return -1;
}
if (!l.IsDefined()) {
return 1;
}
if (pointTypes.Size() > l.pointTypes.Size())
return 1;
if (pointTypes.Size() < l.pointTypes.Size())
return -1;
return 0;
}
bool Region2::Adjacent(const Attribute *arg) const {
return false;
}
size_t Region2::Sizeof() const {
return sizeof(*this);
}
ListExpr Region2::Property() {
return gentc::GenProperty("-> DATA",
BasicType(),
"((<pointTypes>)(<coords>))",
"((0 1 1 1 4) (0 0 0 300 300.0 300 300 0))",
"The lists pointTypes and coords need to be konsistent.\n"
"For each int in pointTypes the correct number of int\n"
"or real must be in coords: \n"
"Type=0: 2 Elements in coords\n"
"Type=1: 2 Elements in coords\n"
"Type=4: 0 Elements in coords\n");
}
size_t Region2::HashValue() const {
if (pointTypes.Size() == 0) return 0;
size_t h = 17 * (coords.Size() + pointTypes.Size());
for (int i = 0; i < coords.Size(); i++) {
h += (size_t)((5 * getCoord(i)));
}
for (int i = 0; i < pointTypes.Size(); i++) {
h += (size_t)((7 * getPointType(i)));
}
return h;
}
void Region2::CopyFrom(const Attribute *arg) {
*this = *((Region2 *) arg);
}
Attribute *Region2::Clone() const {
return new Region2(*this);
}
bool Region2::CheckKind(ListExpr type, ListExpr &errorInfo) {
return checkType(type);
}
/*
Implementation of readFrom method. Creates an instance from a list
representation.
*/
bool Region2::ReadFrom(ListExpr LE, const ListExpr typeInfo) {
if (listutils::isSymbolUndefined(LE)) {
SetDefined(false);
return true;
}
if (!nl->HasLength(LE, 2)) {
cmsg.inFunError("List in ReadFrom-Function wrong size");
return false;
}
int numTypes = nl->ListLength(nl->First(LE));
int numCoords = nl->ListLength(nl->Second(LE));
if (numTypes == 0 || numCoords == 0) {
cmsg.inFunError("None of the num variables can be 0");
return false;
}
ListExpr pointTypesList = nl->First(LE);
for (int i = 1; i <= numTypes; i++) {
ListExpr current = nl->Nth(i, pointTypesList);
if (!(nl->IsAtom(current) && nl->AtomType(current) == IntType)) {
cmsg.inFunError("All pointType values must be of IntType");
return false;
}
int pointType = nl->IntValue(current);
if(pointType != Curve::SEG_MOVETO && pointType != Curve::SEG_LINETO
&& pointType != Curve::SEG_CLOSE) {
cmsg.inFunError("Allowed pointType values: 0, 1 or 4");
return false;
}
this->appendType(pointType);
}
if (this->pointTypes.Size() > 0
&& this->getPointType(0) != Curve::SEG_MOVETO) {
cmsg.inFunError("First pointType must be a moveTo");
return false;
}
ListExpr coordsList = nl->Second(LE);
for (int i = 1; i <= numCoords; i++) {
ListExpr current = nl->Nth(i, coordsList);
if (!listutils::isNumeric(current)) {
cmsg.inFunError("All coord values must be of numeric");
return false;
} else if (nl->AtomType(current) == IntType) {
this->appendCoord((double)nl->IntValue(current));
} else if (nl->AtomType(current) == RealType) {
this->appendCoord(nl->RealValue(current));
}
}
SetDefined(true);
return true;
}
/*
Implementation of toListExpr method. Returns a list representation of this
object.
*/
ListExpr Region2::ToListExpr(ListExpr typeInfo) const {
if (!IsDefined()) {
return listutils::getUndefined();
}
ListExpr last= nl->TheEmptyList();
ListExpr typesList = nl->TheEmptyList();
for (int i = 0; i < pointTypes.Size(); i++) {
if (i == 0) {
typesList = last = nl->OneElemList(nl->IntAtom(this->getPointType(i)));
} else {
last = nl->Append(last, nl->IntAtom(this->getPointType(i)));
}
}
ListExpr coordsList = nl->TheEmptyList();
for (int i = 0; i < coords.Size(); i++) {
if (i == 0) {
coordsList = last = nl->OneElemList(nl->RealAtom(this->getCoord(i)));
} else {
last = nl->Append(last, nl->RealAtom(this->getCoord(i)));
}
}
ListExpr result = nl->TwoElemList(typesList, coordsList);
return result;
}
/*
Returns a ~RectangleBB~ representing the bounding box of this region2.
*/
RectangleBB Region2::getBounds() {
updateCurves();
RectangleBB r = RectangleBB(0, 0 ,0 ,0);
if (curves.size() > 0) {
Curve* c = curves.at(0);
// First point is always an order 0 curve (moveto)
r.x = c->getX0();
r.y = c->getY0();
for (unsigned int i = 1; i < curves.size(); i++) {
curves.at(i)->enlarge(&r);
}
}
clearCurves();
return r;
}
/*
Checks if this line2 intersects with the ~RectangleBB~ in the argument.
*/
bool Region2::intersects(RectangleBB *bbox) {
double x, y, w, h;
x = bbox->x;
y = bbox->y;
w = bbox->width;
h = bbox->height;
if (w < 0 || h < 0) {
return false;
}
if (!getBounds().intersects(x, y, w, h)) {
return false;
}
updateCurves();
bool result = Crossings::findCrossings(&curves, x, y, x+w, y+h);
clearCurves();
return result;
}
/*
Returns a ~Region2~ containing the area of this region2 and the argument.
*/
Region2* Region2::union1(Region2 *rhs) {
updateCurves();
UnionOp op;
op.calculate(&curves, &rhs->curves);
this->updateFLOBs();
clearCurves();
rhs->curves.clear();
return this;
}
/*
Returns a ~Region2~ containing the area from this region2 which is not
contained in the argument.
*/
Region2* Region2::minus1(Region2 *rhs) {
updateCurves();
MinusOp op;
op.calculate(&curves, &rhs->curves);
this->updateFLOBs();
clearCurves();
rhs->curves.clear();
return this;
}
/*
Returns a ~Region2~ containing the area contained in both, this region2 and
the argument.
*/
Region2* Region2::intersection1(Region2 *rhs) {
updateCurves();
IntersectsOp op;
op.calculate(&curves, &rhs->curves);
this->updateFLOBs();
clearCurves();
rhs->curves.clear();
return this;
}
/*
Returns a ~Region2~ containing the area contained either in this region2 or in
the argument.
*/
Region2* Region2::xor1(Region2 *rhs) {
updateCurves();
XorOp op;
op.calculate(&curves, &rhs->curves);
this->updateFLOBs();
clearCurves();
rhs->curves.clear();
return this;
}
/*
Adds a ~MoveCurve~ or a ~LineCurve~ to ~curves~ for each segment contained in
~pointTypes~.
*/
void Region2::createCurves() {
if(pointTypes.Size() <= 0) {
return;
}
double coords[10];
double movx = 0, movy = 0, curx = 0, cury = 0;
double newx, newy;
int offset = 0;
for (int i = 0; i < pointTypes.Size(); i++) {
switch (getPointType(i)) {
case Curve::SEG_MOVETO:
nextSegment(offset, Curve::SEG_MOVETO, coords);
offset += 2;
Curve::insertLine(&curves, curx, cury, movx, movy);
curx = movx = coords[0];
cury = movy = coords[1];
Curve::insertMove(&curves, movx, movy);
break;
case Curve::SEG_LINETO:
nextSegment(offset, Curve::SEG_LINETO, coords);
offset += 2;
newx = coords[0];
newy = coords[1];
Curve::insertLine(&curves, curx, cury, newx, newy);
curx = newx;
cury = newy;
break;
case Curve::SEG_CLOSE:
Curve::insertLine(&curves, curx, cury, movx, movy);
curx = movx;
cury = movy;
break;
}
}
Curve::insertLine(&curves, curx, cury, movx, movy);
EOWindOp op;
op.calculate(&curves, NULL);
}
/*
Calls the ~createCurves()~ method if ~curves~ is empty and ~pointTypes~ is not.
*/
void Region2::updateCurves() {
if(curves.size() == 0 && pointTypes.Size() > 0) {
createCurves();
}
}
/*
Deletes all Curves in ~curves~.
*/
void Region2::clearCurves() {
for (unsigned int i = 0; i < curves.size(); i++) {
Curve *c = curves.at(i);
delete c;
c = 0;
}
curves.clear();
}
/*
Clears all segments from ~coords~ and ~pointTypes~ and then adds them again
using the current state of ~curves~.
*/
void Region2::updateFLOBs() {
coords.clean();
pointTypes.clean();
Curve *lastcurve=0;
for (unsigned int i = 0; i < curves.size(); i++) {
Curve *curve = curves.at(i);
if (curve->getOrder() == Curve::SEG_MOVETO) {
if(i > 0 && lastcurve->getOrder() != Curve::SEG_MOVETO) {
pointTypes.Append((int) Curve::SEG_CLOSE);
}
coords.Append(curve->getX0());
coords.Append(curve->getY0());
} else if (curve->getOrder() == Curve::SEG_LINETO) {
if(getPointType(pointTypes.Size() - 1) == Curve::SEG_LINETO
&& (getCoord(coords.Size()-2) != curve->getX0()
|| getCoord(coords.Size()-1) != curve->getY0())) {
// horizontal lines are added again
pointTypes.Append((int) Curve::SEG_LINETO);
coords.Append(curve->getX0());
coords.Append(curve->getY0());
}
coords.Append(curve->getX1());
coords.Append(curve->getY1());
}
pointTypes.Append(curves.at(i)->getOrder());
lastcurve = curve;
}
if(lastcurve->getOrder() != Curve::SEG_CLOSE) {
pointTypes.Append((int) Curve::SEG_CLOSE);
}
}
/*
Helper method: Returns the coordinates for the next segment from ~coords~
depending on its pointType and an offset passed as arguments.
*/
void Region2::nextSegment(int offset, int pointType, double *result) const {
if (pointType == Curve::SEG_MOVETO
|| pointType == Curve::SEG_LINETO){
result[0] = getCoord(offset);
result[1] = getCoord(offset + 1);
}
}
/*
Helper method: Appends a coordinate to ~coords~.
*/
void Region2::appendCoord(double x) {
coords.Append(x);
}
/*
Helper method: Appends a type to ~pointTypes~.
*/
void Region2::appendType(int x) {
pointTypes.Append(x);
}
/*
Helper method: Returns index *i* from ~pointTypes~.
*/
int Region2::getPointType(int i) const {
int pointType;
pointTypes.Get(i, &pointType);
return pointType;
}
/*
Helper method: Returns index *i* from ~coords~.
*/
double Region2::getCoord(int i) const {
double coord;
coords.Get(i, &coord);
return coord;
}
}