DYT/Tool/OpenSceneGraph-3.6.5/include/geos/operation/overlayng/Edge.h

346 lines
9.7 KiB
C
Raw Permalink Normal View History

2024-12-24 23:49:36 +00:00
/**********************************************************************
*
* GEOS - Geometry Engine Open Source
* http://geos.osgeo.org
*
* Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca>
*
* This is free software; you can redistribute and/or modify it under
* the terms of the GNU Lesser General Public Licence as published
* by the Free Software Foundation.
* See the COPYING file for more information.
*
**********************************************************************/
#pragma once
#include <geos/geom/Coordinate.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/Dimension.h>
#include <geos/operation/overlayng/OverlayEdge.h>
#include <geos/operation/overlayng/OverlayLabel.h>
#include <geos/operation/overlayng/EdgeSourceInfo.h>
#include <geos/util/GEOSException.h>
#include <geos/export.h>
#include <memory>
namespace geos { // geos.
namespace operation { // geos.operation
namespace overlayng { // geos.operation.overlayng
/**
* Represents the underlying linework for edges in a topology graph,
* and carries the topology information
* derived from the two parent geometries.
* The edge may be the result of the merging of
* two or more edges which have the same underlying linework
* (although possibly different orientations).
* In this case the topology information is
* derived from the merging of the information in the
* source edges.
* Merged edges can occur in the following situations
*
* - Due to topology collapse caused by snapping or rounding
* of polygonal geometries.
* - Due to coincident linework in a linear input
*
* The source edges may have the same parent geometry,
* or different ones, or a mix of the two.
*
* @author mdavis
*/
class GEOS_DLL Edge {
private:
// Members
int aDim = OverlayLabel::DIM_UNKNOWN;
int aDepthDelta = 0;
bool aIsHole = false;
int bDim = OverlayLabel::DIM_UNKNOWN;
int bDepthDelta = 0;
bool bIsHole = false;
std::unique_ptr<geom::CoordinateSequence> pts;
// Methods
/**
* Populates the label for an edge resulting from an input geometry.
* - If the edge is not part of the input, the label is left as NOT_PART
* - If input is an Area and the edge is on the boundary
* (which may include some collapses),
* edge is marked as an AREA edge and side locations are assigned
* - If input is an Area and the edge is collapsed
* (depth delta = 0),
* the label is set to COLLAPSE.
* The location will be determined later
* by evaluating the final graph topology.
* - If input is a Line edge is set to a LINE edge.
* For line edges the line location is not significant
* (since there is no parent area for which to determine location).
*/
static void initLabel(OverlayLabel& lbl, uint8_t geomIndex, int dim, int depthDelta, bool p_isHole);
static int labelDim(int dim, int depthDelta)
{
if (dim == geom::Dimension::False)
return OverlayLabel::DIM_NOT_PART;
if (dim == geom::Dimension::L)
return OverlayLabel::DIM_LINE;
// assert: dim is A
bool isCollapse = (depthDelta == 0);
if (isCollapse)
return OverlayLabel::DIM_COLLAPSE;
return OverlayLabel::DIM_BOUNDARY;
};
bool isHole(int index) const
{
if (index == 0)
return aIsHole;
return bIsHole;
};
bool isBoundary(int geomIndex) const
{
if (geomIndex == 0)
return aDim == OverlayLabel::DIM_BOUNDARY;
return bDim == OverlayLabel::DIM_BOUNDARY;
};
/**
* Tests whether the edge is part of a shell in the given geometry.
* This is only the case if the edge is a boundary.
*/
bool isShell(int geomIndex) const
{
if (geomIndex == 0) {
return (aDim == OverlayLabel::DIM_BOUNDARY && ! aIsHole);
}
return (bDim == OverlayLabel::DIM_BOUNDARY && ! bIsHole);
};
static Location locationRight(int depthDelta)
{
int sgn = delSign(depthDelta);
switch (sgn) {
case 0: return Location::NONE;
case 1: return Location::INTERIOR;
case -1: return Location::EXTERIOR;
}
return Location::NONE;
};
static Location locationLeft(int depthDelta)
{
// TODO: is it always safe to ignore larger depth deltas?
int sgn = delSign(depthDelta);
switch (sgn) {
case 0: return Location::NONE;
case 1: return Location::EXTERIOR;
case -1: return Location::INTERIOR;
}
return Location::NONE;
};
static int delSign(int depthDel)
{
if (depthDel > 0) return 1;
if (depthDel < 0) return -1;
return 0;
};
void copyInfo(const EdgeSourceInfo* info)
{
if (info->getIndex() == 0) {
aDim = info->getDimension();
aIsHole = info->isHole();
aDepthDelta = info->getDepthDelta();
}
else {
bDim = info->getDimension();
bIsHole = info->isHole();
bDepthDelta = info->getDepthDelta();
}
};
static bool isHoleMerged(int geomIndex, const Edge* edge1, const Edge* edge2)
{
// TOD: this might be clearer with tri-state logic for isHole?
bool isShell1 = edge1->isShell(geomIndex);
bool isShell2 = edge2->isShell(geomIndex);
bool isShellMerged = isShell1 || isShell2;
// flip since isHole is stored
return !isShellMerged;
};
public:
Edge()
: aDim(OverlayLabel::DIM_UNKNOWN)
, aDepthDelta(0)
, aIsHole(false)
, bDim(OverlayLabel::DIM_UNKNOWN)
, bDepthDelta(0)
, bIsHole(false)
, pts(nullptr)
{};
friend std::ostream& operator<<(std::ostream& os, const Edge& e);
static bool isCollapsed(const geom::CoordinateSequence* pts);
// takes ownership of pts from caller
Edge(std::unique_ptr<geom::CoordinateSequence>&& p_pts, const EdgeSourceInfo* info);
// return a clone of the underlying points
std::unique_ptr<geom::CoordinateSequence> getCoordinates()
{
return pts->clone();
};
// return a read-only pointer to the underlying points
const geom::CoordinateSequence* getCoordinatesRO() const
{
return pts.get();
};
// release the underlying points to the caller
geom::CoordinateSequence* releaseCoordinates()
{
geom::CoordinateSequence* cs = pts.release();
pts.reset(nullptr);
return cs;
};
const geom::Coordinate& getCoordinate(std::size_t index) const
{
return pts->getAt(index);
};
std::size_t size() const
{
return pts->size();
};
bool direction() const
{
if (pts->size() < 2) {
throw util::GEOSException("Edge must have >= 2 points");
}
const geom::Coordinate& p0 = pts->getAt(0);
const geom::Coordinate& p1 = pts->getAt(1);
const geom::Coordinate& pn0 = pts->getAt(pts->size() - 1);
const geom::Coordinate& pn1 = pts->getAt(pts->size() - 2);
int cmp = 0;
int cmp0 = p0.compareTo(pn0);
if (cmp0 != 0) cmp = cmp0;
if (cmp == 0) {
int cmp1 = p1.compareTo(pn1);
if (cmp1 != 0) cmp = cmp1;
}
if (cmp == 0) {
throw util::GEOSException("Edge direction cannot be determined because endpoints are equal");
}
return cmp == -1;
};
/**
* Compares two coincident edges to determine
* whether they have the same or opposite direction.
*/
bool relativeDirection(const Edge* edge2) const
{
// assert: the edges match (have the same coordinates up to direction)
if (!getCoordinate(0).equals2D(edge2->getCoordinate(0))) {
return false;
}
if (!getCoordinate(1).equals2D(edge2->getCoordinate(1))) {
return false;
}
return true;
};
int dimension(int geomIndex) const
{
if (geomIndex == 0) return aDim;
return bDim;
};
/**
* Merges an edge into this edge,
* updating the topology info accordingly.
*/
void merge(const Edge* edge)
{
/**
* Marks this
* as a shell edge if any contributing edge is a shell.
* Update hole status first, since it depends on edge dim
*/
aIsHole = isHoleMerged(0, this, edge);
bIsHole = isHoleMerged(1, this, edge);
if (edge->aDim > aDim) aDim = edge->aDim;
if (edge->bDim > bDim) bDim = edge->bDim;
bool relDir = relativeDirection(edge);
int flipFactor = relDir ? 1 : -1;
aDepthDelta += flipFactor * edge->aDepthDelta;
bDepthDelta += flipFactor * edge->bDepthDelta;
};
void populateLabel(OverlayLabel &lbl) const
{
initLabel(lbl, 0, aDim, aDepthDelta, aIsHole);
initLabel(lbl, 1, bDim, bDepthDelta, bIsHole);
};
bool compareTo(const Edge& e) const
{
const geom::Coordinate& ca = getCoordinate(0);
const geom::Coordinate& cb = e.getCoordinate(0);
if(ca.compareTo(cb) < 0) {
return true;
}
else if (ca.compareTo(cb) > 0) {
return false;
}
else {
const geom::Coordinate& cca = getCoordinate(1);
const geom::Coordinate& ccb = e.getCoordinate(1);
if(cca.compareTo(ccb) < 0) {
return true;
}
else if (cca.compareTo(ccb) > 0) {
return false;
}
else {
return false;
}
}
}
};
bool EdgeComparator(const Edge* a, const Edge* b);
} // namespace geos.operation.overlayng
} // namespace geos.operation
} // namespace geos