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

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;
}
}