631 lines
16 KiB
C++
631 lines
16 KiB
C++
/*
|
|
----
|
|
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
|
|
----
|
|
|
|
2.3 Implementation of ~Line2~ methods
|
|
|
|
Defines and includes.
|
|
|
|
*/
|
|
|
|
#include "Line.h"
|
|
#include "Region.h"
|
|
#include "Curve.h"
|
|
#include "NestedList.h"
|
|
#include "ListUtils.h"
|
|
#include "RectangleBB.h"
|
|
#include "Algebras/Spatial/SpatialAlgebra.h"
|
|
#include "AlmostEqual.h"
|
|
|
|
#include <vector>
|
|
|
|
namespace salr {
|
|
|
|
/*
|
|
Implementation of copy constructor.
|
|
|
|
*/
|
|
Line2::Line2(const Line2 &other) :
|
|
Attribute(other.IsDefined()),
|
|
coords(other.coords.Size()),
|
|
pointTypes(other.pointTypes.Size()),
|
|
hasQuads(other.hasQuads){
|
|
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 int constructor.
|
|
|
|
*/
|
|
Line2::Line2(int initialCapacity) :
|
|
Attribute(true),
|
|
coords(initialCapacity * 2),
|
|
pointTypes(initialCapacity),
|
|
hasQuads(false)
|
|
{}
|
|
|
|
/*
|
|
Implementation of constructor using ~Line~.
|
|
|
|
*/
|
|
Line2::Line2(const Line& l) :
|
|
Attribute(l.IsDefined()),
|
|
coords(l.Size()),
|
|
pointTypes(l.Size()),
|
|
hasQuads(false)
|
|
{
|
|
if (IsDefined()) {
|
|
HalfSegment hs;
|
|
l.Get(0, hs);
|
|
double x0 = hs.GetLeftPoint().GetX();
|
|
double y0 = hs.GetLeftPoint().GetY();
|
|
double lastx = x0;
|
|
double lasty = y0;
|
|
moveTo(x0, y0);
|
|
|
|
for(int i = 0; i < l.Size(); i++) {
|
|
l.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();
|
|
if (x0 == lastx && y0 == lasty) {
|
|
lineTo(x1, y1);
|
|
lastx = x1;
|
|
lasty = y1;
|
|
} else if (x1 == lastx && y1 == lasty) {
|
|
lineTo(x0, y0);
|
|
lastx = x0;
|
|
lasty = y0;
|
|
} else {
|
|
moveTo(x0, y0);
|
|
lineTo(x1, y1);
|
|
lastx = x1;
|
|
lasty = y1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Implementation of constructor using ~Region2~.
|
|
|
|
*/
|
|
Line2::Line2(const Region2 &r) :
|
|
Attribute(r.IsDefined()),
|
|
coords(r.coords.Size()),
|
|
pointTypes(r.pointTypes.Size()),
|
|
hasQuads(false){
|
|
for (int i = 0; i < r.pointTypes.Size(); i++) {
|
|
appendType(r.getPointType(i));
|
|
}
|
|
for (int i = 0; i < r.coords.Size(); i++) {
|
|
appendCoord(r.getCoord(i));
|
|
}
|
|
}
|
|
|
|
/*
|
|
Implementation of destructor.
|
|
|
|
*/
|
|
Line2::~Line2() {
|
|
}
|
|
|
|
/*
|
|
Implementation of system methods.
|
|
|
|
*/
|
|
const std::string Line2::BasicType() {
|
|
return "line2";
|
|
}
|
|
|
|
const bool Line2::checkType(const ListExpr type) {
|
|
return listutils::isSymbol(type, BasicType());
|
|
}
|
|
|
|
int Line2::NumOfFLOBs() const {
|
|
return 2;
|
|
}
|
|
|
|
Flob *Line2::GetFLOB(const int i) {
|
|
assert(i >= 0 && i < NumOfFLOBs());
|
|
if (i == 1) {
|
|
return &coords;
|
|
} else {
|
|
return &pointTypes;
|
|
}
|
|
}
|
|
|
|
int Line2::Compare(const Attribute *arg) const {
|
|
const Line2 &l = *((const Line2 *) arg);
|
|
if (!IsDefined() && !l.IsDefined()) {
|
|
return 0;
|
|
}
|
|
if (!IsDefined()) {
|
|
return -1;
|
|
}
|
|
if (!l.IsDefined()) {
|
|
return 1;
|
|
}
|
|
|
|
if (coords.Size() > l.coords.Size())
|
|
return 1;
|
|
if (coords.Size() < l.coords.Size())
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
ListExpr Line2::Property() {
|
|
return gentc::GenProperty("-> DATA",
|
|
BasicType(),
|
|
"((<pointTypes>)(<coords>))",
|
|
"((0 1 2) (1.5 2.5 2 3 3 4 4 3))",
|
|
"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=2: 4 Elements in coords\n"
|
|
"Type=4: 0 Elements in coords\n");
|
|
}
|
|
|
|
bool Line2::Adjacent(const Attribute *arg) const {
|
|
return false;
|
|
}
|
|
|
|
size_t Line2::Sizeof() const {
|
|
return sizeof(*this);
|
|
}
|
|
|
|
size_t Line2::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 Line2::CopyFrom(const Attribute *arg) {
|
|
*this = *((Line2 *) arg);
|
|
}
|
|
|
|
Attribute *Line2::Clone() const {
|
|
return new Line2(*this);
|
|
}
|
|
|
|
bool Line2::CheckKind(ListExpr type, ListExpr &errorInfo) {
|
|
return checkType(type);
|
|
}
|
|
|
|
/*
|
|
Implementation of readFrom method. Creates an instance from a list
|
|
representation.
|
|
|
|
*/
|
|
bool Line2::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) ||
|
|
(numTypes != 0 && numCoords == 0)) {
|
|
cmsg.inFunError("Either both list length must be 0 or none");
|
|
return false;
|
|
}
|
|
if (numTypes == 0) {
|
|
this->hasInitialMove();
|
|
}
|
|
|
|
if (this->pointTypes.Size() > 0
|
|
&& this->getPointType(0) != Curve::SEG_MOVETO) {
|
|
cmsg.inFunError("First pointType must be a moveTo");
|
|
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_QUADTO && pointType != Curve::SEG_CLOSE) {
|
|
cmsg.inFunError("Allowed pointType values: 0, 1, 2 or 4");
|
|
return false;
|
|
}
|
|
this->appendType(pointType);
|
|
if(pointType == Curve::SEG_QUADTO) {
|
|
hasQuads = true;
|
|
}
|
|
}
|
|
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 Line2::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;
|
|
}
|
|
|
|
|
|
/*
|
|
Adds a move segment to this line2.
|
|
|
|
*/
|
|
void Line2::moveTo(double x, double y) {
|
|
if (pointTypes.Size() > 0 &&
|
|
getPointType(pointTypes.Size() - 1) == Curve::SEG_MOVETO) {
|
|
coords.Put(coords.Size() - 2, x);
|
|
coords.Put(coords.Size() - 1, y);
|
|
} else {
|
|
pointTypes.Append((int) Curve::SEG_MOVETO);
|
|
coords.Append(x);
|
|
coords.Append(y);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Adds a line segment to this line2.
|
|
|
|
*/
|
|
void Line2::lineTo(double x, double y) {
|
|
hasInitialMove();
|
|
pointTypes.Append((int) Curve::SEG_LINETO);
|
|
coords.Append(x);
|
|
coords.Append(y);
|
|
}
|
|
|
|
/*
|
|
Adds a quad segment to this line2.
|
|
|
|
*/
|
|
void Line2::quadTo(double x1, double y1, double x2, double y2) {
|
|
hasInitialMove();
|
|
pointTypes.Append((int) Curve::SEG_QUADTO);
|
|
coords.Append(x1);
|
|
coords.Append(y1);
|
|
coords.Append(x2);
|
|
coords.Append(y2);
|
|
hasQuads = true;
|
|
}
|
|
|
|
/*
|
|
Adds a close segment to this line2.
|
|
|
|
*/
|
|
void Line2::closeLine() {
|
|
if (pointTypes.Size() == 0 ||
|
|
getPointType(pointTypes.Size() - 1) != Curve::SEG_CLOSE) {
|
|
hasInitialMove();
|
|
pointTypes.Append((int) Curve::SEG_CLOSE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Returns a ~RectangleBB~ representing the bounding box of this line2.
|
|
|
|
*/
|
|
RectangleBB Line2::getBounds() {
|
|
double x1, y1, x2, y2;
|
|
int i = coords.Size();
|
|
if (i > 0) {
|
|
y1 = y2 = getCoord(--i);
|
|
x1 = x2 = getCoord(--i);
|
|
while (i > 0) {
|
|
double y = getCoord(--i);
|
|
double x = getCoord(--i);
|
|
if (x < x1) x1 = x;
|
|
if (y < y1) y1 = y;
|
|
if (x > x2) x2 = x;
|
|
if (y > y2) y2 = y;
|
|
}
|
|
} else {
|
|
x1 = y1 = x2 = y2 = 0.0;
|
|
}
|
|
return RectangleBB(x1, y1, x2 - x1, y2 - y1);
|
|
}
|
|
|
|
/*
|
|
Checks if this line2 intersects with the ~RectangleBB~ in the argument.
|
|
|
|
*/
|
|
bool Line2::intersects(RectangleBB *bbox) {
|
|
if (bbox->width <= 0 || bbox->height <= 0) {
|
|
return false;
|
|
}
|
|
int crossings = rectCrossings(bbox->x, bbox->y,
|
|
bbox->x+bbox->width, bbox->y+bbox->height);
|
|
return (crossings == Curve::RECT_INTERSECTS ||
|
|
(crossings & -1) != 0);
|
|
}
|
|
|
|
/*
|
|
Helper method: Returns the coordinates for the next segment from ~coords~
|
|
depending on its pointType and an offset passed as arguments.
|
|
|
|
*/
|
|
void Line2::nextSegment(int offset, int pointType, double *result) const {
|
|
if(pointType == Curve::SEG_MOVETO
|
|
|| pointType == Curve::SEG_LINETO
|
|
|| pointType == Curve::SEG_QUADTO) {
|
|
result[0] = getCoord(offset);
|
|
result[1] = getCoord(offset + 1);
|
|
}
|
|
if(pointType == Curve::SEG_QUADTO) {
|
|
result[2] = getCoord(offset + 2);
|
|
result[3] = getCoord(offset + 3);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Transforms this ~Line2~ to a ~Line~.
|
|
|
|
*/
|
|
Line Line2::toLine() {
|
|
if(hasQuads) {
|
|
std::cerr << "Line2 contains quad-segment. Invalid transformation."
|
|
<< endl;
|
|
Line line = Line(0);
|
|
line.SetDefined(false);
|
|
return line;
|
|
}
|
|
Line l = Line(pointTypes.Size()*2);
|
|
l.StartBulkLoad();
|
|
int edgeno = 0;
|
|
double coords[2];
|
|
double x0=0, x1=0, y0=0, y1=0, movx=0, movy=0;
|
|
int offset = 0;
|
|
HalfSegment hs;
|
|
Point lp, rp;
|
|
for(int i = 0; i < pointTypes.Size(); i++) {
|
|
switch (getPointType(i)) {
|
|
case Curve::SEG_MOVETO:
|
|
nextSegment(offset, Curve::SEG_MOVETO, coords);
|
|
offset += 2;
|
|
movx = x0 = coords[0];
|
|
movy = y0 = coords[1];
|
|
break;
|
|
case Curve::SEG_LINETO:
|
|
nextSegment(offset, Curve::SEG_LINETO, coords);
|
|
offset += 2;
|
|
x1 = coords[0];
|
|
y1 = coords[1];
|
|
if(AlmostEqual(x0, x1) && AlmostEqual(y0, y1)) {
|
|
continue;
|
|
}
|
|
lp = Point(true, x0, y0);
|
|
rp = Point(true, x1, y1);
|
|
hs = HalfSegment(true, lp, rp);
|
|
hs.attr.edgeno = edgeno++;
|
|
l += hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
l += hs;
|
|
x0 = x1;
|
|
y0 = y1;
|
|
break;
|
|
case Curve::SEG_CLOSE:
|
|
x1 = movx;
|
|
y1 = movy;
|
|
if(AlmostEqual(x0, x1) && AlmostEqual(y0, y1)) {
|
|
continue;
|
|
}
|
|
lp = Point(true, x0, y0);
|
|
rp = Point(true, x1, y1);
|
|
hs = HalfSegment(true, lp, rp);
|
|
hs.attr.edgeno = edgeno++;
|
|
l += hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
l += hs;
|
|
x0 = x1;
|
|
y0 = y1;
|
|
break;
|
|
}
|
|
}
|
|
l.EndBulkLoad();
|
|
return l;
|
|
}
|
|
|
|
/*
|
|
Helper method: Returns index *i* from ~coords~.
|
|
|
|
*/
|
|
double Line2::getCoord(int i) const {
|
|
double coord;
|
|
coords.Get(i, &coord);
|
|
return coord;
|
|
}
|
|
|
|
/*
|
|
Helper method: Returns index *i* from ~pointTypes~.
|
|
|
|
*/
|
|
int Line2::getPointType(int i) const {
|
|
int pointType;
|
|
pointTypes.Get(i, &pointType);
|
|
return pointType;
|
|
}
|
|
|
|
/*
|
|
Adds a move segment to this line2 if it is empty.
|
|
|
|
*/
|
|
void Line2::hasInitialMove() {
|
|
if (pointTypes.Size() == 0) {
|
|
moveTo(0, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Helper method: Appends a coordinate to ~coords~.
|
|
|
|
*/
|
|
void Line2::appendCoord(double x) {
|
|
coords.Append(x);
|
|
}
|
|
|
|
/*
|
|
Helper method: Appends a type to ~pointTypes~.
|
|
|
|
*/
|
|
void Line2::appendType(int x) {
|
|
pointTypes.Append(x);
|
|
}
|
|
|
|
|
|
/*
|
|
Checks if this line2 intersects with the rectangle defined by ~rxmin~,
|
|
~rymin~, ~rxmax~ and ~rymax~. Returns the number of intersections.
|
|
|
|
*/
|
|
int
|
|
Line2::rectCrossings(double rxmin, double rymin, double rxmax, double rymax) {
|
|
if (pointTypes.Size() == 0) {
|
|
return 0;
|
|
}
|
|
double curx, cury, movx, movy, endx, endy;
|
|
std::vector<double> loc_coords;
|
|
for(int i = 0; i < coords.Size(); i++) {
|
|
loc_coords.push_back(getCoord(i));
|
|
}
|
|
curx = movx = loc_coords.at(0);
|
|
cury = movy = loc_coords.at(1);
|
|
int crossings = 0;
|
|
int ci = 2;
|
|
for (int i = 1;
|
|
crossings != Curve::RECT_INTERSECTS && i < pointTypes.Size(); i++) {
|
|
int pointTyp = getPointType(i);
|
|
switch (pointTyp) {
|
|
case Curve::SEG_MOVETO:
|
|
if (curx != movx || cury != movy) {
|
|
crossings = Curve::rectCrossingsForLine(crossings,
|
|
rxmin, rymin, rxmax, rymax,
|
|
curx, cury, movx, movy);
|
|
}
|
|
movx = curx = loc_coords.at(ci++);
|
|
movy = cury = loc_coords.at(ci++);
|
|
break;
|
|
case Curve::SEG_LINETO:
|
|
endx = loc_coords.at(ci++);
|
|
endy = loc_coords.at(ci++);
|
|
crossings = Curve::rectCrossingsForLine(crossings,
|
|
rxmin, rymin, rxmax, rymax,
|
|
curx, cury, endx, endy);
|
|
curx = endx;
|
|
cury = endy;
|
|
break;
|
|
case Curve::SEG_QUADTO:
|
|
double tmpx, tmpy;
|
|
tmpx = loc_coords.at(ci++);
|
|
tmpy = loc_coords.at(ci++);
|
|
endx = loc_coords.at(ci++);
|
|
endy = loc_coords.at(ci++);
|
|
crossings = Curve::rectCrossingsForQuad(crossings,
|
|
rxmin, rymin, rxmax, rymax,
|
|
curx, cury, tmpx, tmpy,
|
|
endx, endy, 0);
|
|
curx = endx;
|
|
cury = endy;
|
|
break;
|
|
case Curve::SEG_CLOSE:
|
|
if (curx != movx || cury != movy) {
|
|
crossings = Curve::rectCrossingsForLine(crossings,
|
|
rxmin, rymin, rxmax, rymax,
|
|
curx, cury, movx, movy);
|
|
}
|
|
curx = movx;
|
|
cury = movy;
|
|
break;
|
|
}
|
|
}
|
|
if (crossings != Curve::RECT_INTERSECTS &&
|
|
(curx != movx || cury != movy)) {
|
|
crossings = Curve::rectCrossingsForLine(crossings,
|
|
rxmin, rymin, rxmax, rymax,
|
|
curx, cury, movx, movy);
|
|
}
|
|
return crossings;
|
|
}
|
|
|
|
}
|
|
|