kicad/common/geometry/hetriang.cpp

711 lines
20 KiB
C++

/*
* Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
* Applied Mathematics, Norway.
* Copyright (C) 2013 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* Contact information: E-mail: tor.dokken@sintef.no
* SINTEF ICT, Department of Applied Mathematics,
* P.O. Box 124 Blindern,
* 0314 Oslo, Norway.
*
* This file is part of TTL.
*
* TTL is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* TTL 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with TTL. If not, see
* <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public
* License, a covered work must retain the producer line in every data
* file that is created or manipulated using TTL.
*
* Other Usage
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the TTL library without
* disclosing the source code of your own applications.
*
* This file may be used in accordance with the terms contained in a
* written agreement between you and SINTEF ICT.
*/
#include <ttl/halfedge/hetriang.h>
#include <ttl/halfedge/hetraits.h>
#include <ttl/ttl.h>
#include <algorithm>
#include <fstream>
#include <limits>
using namespace hed;
using namespace std;
Triangulation* TTLtraits::triang_ = NULL;
#ifdef TTL_USE_NODE_ID
int Node::id_count = 0;
#endif
//#define DEBUG_HE
#ifdef DEBUG_HE
#include <iostream>
static void errorAndExit(char* message) {
cout << "\n!!! ERROR: "<< message << " !!!\n" << endl; exit(-1);
}
#endif
//--------------------------------------------------------------------------------------------------
static EdgePtr getLeadingEdgeInTriangle(const EdgePtr& e) {
EdgePtr edge = e;
// Code: 3EF (assumes triangle)
if (!edge->isLeadingEdge()) {
edge = edge->getNextEdgeInFace();
if (!edge->isLeadingEdge())
edge = edge->getNextEdgeInFace();
}
if (!edge->isLeadingEdge()) {
return EdgePtr();
}
return edge;
}
//--------------------------------------------------------------------------------------------------
static void getLimits(NodesContainer::iterator first,
NodesContainer::iterator last,
int& xmin, int& ymin,
int& xmax, int& ymax) {
xmin = ymin = std::numeric_limits<int>::min();
xmax = ymax = std::numeric_limits<int>::max();
NodesContainer::iterator it;
for (it = first; it != last; ++it) {
xmin = min(xmin, (*it)->GetX());
ymin = min(ymin, (*it)->GetY());
xmax = max(xmax, (*it)->GetX());
ymax = max(ymax, (*it)->GetY());
}
}
//--------------------------------------------------------------------------------------------------
EdgePtr Triangulation::initTwoEnclosingTriangles(NodesContainer::iterator first,
NodesContainer::iterator last) {
int xmin, ymin, xmax, ymax;
getLimits(first, last, xmin, ymin, xmax, ymax);
// Add 10% of range:
double fac = 10.0;
double dx = (xmax-xmin)/fac;
double dy = (ymax-ymin)/fac;
NodePtr n1(new Node(xmin-dx,ymin-dy));
NodePtr n2(new Node(xmax+dx,ymin-dy));
NodePtr n3(new Node(xmax+dx,ymax+dy));
NodePtr n4(new Node(xmin-dx,ymax+dy));
// diagonal
EdgePtr e1d(new Edge); // lower
EdgePtr e2d(new Edge); // upper, the twin edge
// lower triangle
EdgePtr e11(new Edge);
EdgePtr e12(new Edge);
// upper triangle
EdgePtr e21(new Edge); // upper upper
EdgePtr e22(new Edge);
// lower triangle
e1d->setSourceNode(n3);
e1d->setNextEdgeInFace(e11);
e1d->setTwinEdge(e2d);
e1d->setAsLeadingEdge();
addLeadingEdge(e1d);
e11->setSourceNode(n1);
e11->setNextEdgeInFace(e12);
e12->setSourceNode(n2);
e12->setNextEdgeInFace(e1d);
// upper triangle
e2d->setSourceNode(n1);
e2d->setNextEdgeInFace(e21);
e2d->setTwinEdge(e1d);
e2d->setAsLeadingEdge();
addLeadingEdge(e2d);
e21->setSourceNode(n3);
e21->setNextEdgeInFace(e22);
e22->setSourceNode(n4);
e22->setNextEdgeInFace(e2d);
return e11;
}
//--------------------------------------------------------------------------------------------------
void Triangulation::createDelaunay(NodesContainer::iterator first,
NodesContainer::iterator last) {
TTLtraits::triang_ = this;
cleanAll();
EdgePtr bedge = initTwoEnclosingTriangles(first, last);
Dart dc(bedge);
Dart d_iter = dc;
NodesContainer::iterator it;
for (it = first; it != last; ++it) {
ttl::insertNode<TTLtraits>(d_iter, *it);
}
// In general (e.g. for the triangle based data structure), the initial dart
// may have been changed.
// It is the users responsibility to get a valid boundary dart here.
// The half-edge data structure preserves the initial dart.
// (A dart at the boundary can also be found by trying to locate a
// triangle "outside" the triangulation.)
// Assumes rectangular domain
ttl::removeRectangularBoundary<TTLtraits>(dc);
}
//--------------------------------------------------------------------------------------------------
void Triangulation::removeTriangle(EdgePtr& edge) {
EdgePtr e1 = getLeadingEdgeInTriangle(edge);
#ifdef DEBUG_HE
if (!e1)
errorAndExit("Triangulation::removeTriangle: could not find leading edge");
#endif
removeLeadingEdgeFromList(e1);
// cout << "No leading edges = " << leadingEdges_.size() << endl;
// Remove the triangle
EdgePtr e2 = e1->getNextEdgeInFace();
EdgePtr e3 = e2->getNextEdgeInFace();
if (e1->getTwinEdge())
e1->getTwinEdge()->setTwinEdge(EdgePtr());
if (e2->getTwinEdge())
e2->getTwinEdge()->setTwinEdge(EdgePtr());
if (e3->getTwinEdge())
e3->getTwinEdge()->setTwinEdge(EdgePtr());
}
//--------------------------------------------------------------------------------------------------
void Triangulation::reverse_splitTriangle(EdgePtr& edge) {
// Reverse operation of splitTriangle
EdgePtr e1 = edge->getNextEdgeInFace();
EdgePtr le = getLeadingEdgeInTriangle(e1);
#ifdef DEBUG_HE
if (!le)
errorAndExit("Triangulation::removeTriangle: could not find leading edge");
#endif
removeLeadingEdgeFromList(le);
EdgePtr e2 = e1->getNextEdgeInFace()->getTwinEdge()->getNextEdgeInFace();
le = getLeadingEdgeInTriangle(e2);
#ifdef DEBUG_HE
if (!le)
errorAndExit("Triangulation::removeTriangle: could not find leading edge");
#endif
removeLeadingEdgeFromList(le);
EdgePtr e3 = edge->getTwinEdge()->getNextEdgeInFace()->getNextEdgeInFace();
le = getLeadingEdgeInTriangle(e3);
#ifdef DEBUG_HE
if (!le)
errorAndExit("Triangulation::removeTriangle: could not find leading edge");
#endif
removeLeadingEdgeFromList(le);
// The three triangles at the node have now been removed
// from the triangulation, but the arcs have not been deleted.
// Next delete the 6 half edges radiating from the node
// The node is maintained by handle and need not be deleted explicitly
// Create the new triangle
e1->setNextEdgeInFace(e2);
e2->setNextEdgeInFace(e3);
e3->setNextEdgeInFace(e1);
addLeadingEdge(e1);
}
//--------------------------------------------------------------------------------------------------
// This is a "template" for iterating the boundary
/*
static void iterateBoundary(const Dart& dart) {
cout << "Iterate boundary 2" << endl;
// input is a dart at the boundary
Dart dart_iter = dart;
do {
if (ttl::isBoundaryEdge(dart_iter))
dart_iter.alpha0().alpha1();
else
dart_iter.alpha2().alpha1();
} while(dart_iter != dart);
}
*/
//--------------------------------------------------------------------------------------------------
Dart Triangulation::createDart() {
// Return an arbitrary CCW dart
return Dart(*leadingEdges_.begin());
}
//--------------------------------------------------------------------------------------------------
bool Triangulation::removeLeadingEdgeFromList(EdgePtr& leadingEdge) {
// Remove the edge from the list of leading edges,
// but don't delete it.
// Also set flag for leading edge to false.
// Must search from start of list. Since edges are added to the
// start of the list during triangulation, this operation will
// normally be fast (when used in the triangulation algorithm)
list<EdgePtr>::iterator it;
for (it = leadingEdges_.begin(); it != leadingEdges_.end(); ++it) {
EdgePtr edge = *it;
if (edge == leadingEdge) {
edge->setAsLeadingEdge(false);
it = leadingEdges_.erase(it);
break;
}
}
if (it == leadingEdges_.end())
return false;
return true;
}
//--------------------------------------------------------------------------------------------------
void Triangulation::cleanAll() {
leadingEdges_.clear();
}
#ifdef TTL_USE_NODE_FLAG
//--------------------------------------------------------------------------------------------------
// This is a "template" for accessing all nodes (but multiple tests)
void Triangulation::flagNodes(bool flag) const {
list<EdgePtr>::const_iterator it;
for (it = leadingEdges_.begin(); it != leadingEdges_.end(); ++it) {
EdgePtr edge = *it;
for (int i = 0; i < 3; ++i) {
edge->getSourceNode()->SetFlag(flag);
edge = edge->getNextEdgeInFace();
}
}
}
//--------------------------------------------------------------------------------------------------
list<NodePtr>* Triangulation::getNodes() const {
flagNodes(false);
list<NodePtr>* nodeList = new list<NodePtr>;
list<EdgePtr>::const_iterator it;
for (it = leadingEdges_.begin(); it != leadingEdges_.end(); ++it) {
EdgePtr edge = *it;
for (int i = 0; i < 3; ++i) {
const NodePtr& node = edge->getSourceNode();
if (node->GetFlag() == false) {
nodeList->push_back(node);
node->SetFlag(true);
}
edge = edge->getNextEdgeInFace();
}
}
return nodeList;
}
#endif
//--------------------------------------------------------------------------------------------------
list<EdgePtr>* Triangulation::getEdges(bool skip_boundary_edges) const {
// collect all arcs (one half edge for each arc)
// (boundary edges are also collected).
list<EdgePtr>::const_iterator it;
list<EdgePtr>* elist = new list<EdgePtr>;
for (it = leadingEdges_.begin(); it != leadingEdges_.end(); ++it) {
EdgePtr edge = *it;
for (int i = 0; i < 3; ++i) {
EdgePtr twinedge = edge->getTwinEdge();
// only one of the half-edges
if ( (!twinedge && !skip_boundary_edges) ||
(twinedge && ((size_t)edge.get() > (size_t)twinedge.get())) )
elist->push_front(edge);
edge = edge->getNextEdgeInFace();
}
}
return elist;
}
//--------------------------------------------------------------------------------------------------
EdgePtr Triangulation::splitTriangle(EdgePtr& edge, NodePtr& point) {
// Add a node by just splitting a triangle into three triangles
// Assumes the half edge is located in the triangle
// Returns a half edge with source node as the new node
// double x, y, z;
// x = point.x();
// y = point.y();
// z = point.z();
// e#_n are new edges
// e# are existing edges
// e#_n and e##_n are new twin edges
// e##_n are edges incident to the new node
// Add the node to the structure
//NodePtr new_node(new Node(x,y,z));
NodePtr n1 = edge->getSourceNode();
EdgePtr e1 = edge;
EdgePtr e2 = edge->getNextEdgeInFace();
NodePtr n2 = e2->getSourceNode();
EdgePtr e3 = e2->getNextEdgeInFace();
NodePtr n3 = e3->getSourceNode();
EdgePtr e1_n(new Edge);
EdgePtr e11_n(new Edge);
EdgePtr e2_n(new Edge);
EdgePtr e22_n(new Edge);
EdgePtr e3_n(new Edge);
EdgePtr e33_n(new Edge);
e1_n->setSourceNode(n1);
e11_n->setSourceNode(point);
e2_n->setSourceNode(n2);
e22_n->setSourceNode(point);
e3_n->setSourceNode(n3);
e33_n->setSourceNode(point);
e1_n->setTwinEdge(e11_n);
e11_n->setTwinEdge(e1_n);
e2_n->setTwinEdge(e22_n);
e22_n->setTwinEdge(e2_n);
e3_n->setTwinEdge(e33_n);
e33_n->setTwinEdge(e3_n);
e1_n->setNextEdgeInFace(e33_n);
e2_n->setNextEdgeInFace(e11_n);
e3_n->setNextEdgeInFace(e22_n);
e11_n->setNextEdgeInFace(e1);
e22_n->setNextEdgeInFace(e2);
e33_n->setNextEdgeInFace(e3);
// and update old's next edge
e1->setNextEdgeInFace(e2_n);
e2->setNextEdgeInFace(e3_n);
e3->setNextEdgeInFace(e1_n);
// add the three new leading edges,
// Must remove the old leading edge from the list.
// Use the field telling if an edge is a leading edge
// NOTE: Must search in the list!!!
EdgePtr leadingEdge;
if (e1->isLeadingEdge())
leadingEdge = e1;
else if (e2->isLeadingEdge())
leadingEdge = e2;
else if(e3->isLeadingEdge())
leadingEdge = e3;
else
return EdgePtr();
removeLeadingEdgeFromList(leadingEdge);
addLeadingEdge(e1_n);
addLeadingEdge(e2_n);
addLeadingEdge(e3_n);
// Return a half edge incident to the new node (with the new node as source node)
return e11_n;
}
//--------------------------------------------------------------------------------------------------
void Triangulation::swapEdge(EdgePtr& diagonal) {
// Note that diagonal is both input and output and it is always
// kept in counterclockwise direction (this is not required by all
// finctions in ttl:: now)
// Swap by rotating counterclockwise
// Use the same objects - no deletion or new objects
EdgePtr eL = diagonal;
EdgePtr eR = eL->getTwinEdge();
EdgePtr eL_1 = eL->getNextEdgeInFace();
EdgePtr eL_2 = eL_1->getNextEdgeInFace();
EdgePtr eR_1 = eR->getNextEdgeInFace();
EdgePtr eR_2 = eR_1->getNextEdgeInFace();
// avoid node to be dereferenced to zero and deleted
NodePtr nR = eR_2->getSourceNode();
NodePtr nL = eL_2->getSourceNode();
eL->setSourceNode(nR);
eR->setSourceNode(nL);
// and now 6 1-sewings
eL->setNextEdgeInFace(eL_2);
eL_2->setNextEdgeInFace(eR_1);
eR_1->setNextEdgeInFace(eL);
eR->setNextEdgeInFace(eR_2);
eR_2->setNextEdgeInFace(eL_1);
eL_1->setNextEdgeInFace(eR);
EdgePtr leL;
if (eL->isLeadingEdge())
leL = eL;
else if (eL_1->isLeadingEdge())
leL = eL_1;
else if (eL_2->isLeadingEdge())
leL = eL_2;
EdgePtr leR;
if (eR->isLeadingEdge())
leR = eR;
else if (eR_1->isLeadingEdge())
leR = eR_1;
else if (eR_2->isLeadingEdge())
leR = eR_2;
removeLeadingEdgeFromList(leL);
removeLeadingEdgeFromList(leR);
addLeadingEdge(eL);
addLeadingEdge(eR);
}
////--------------------------------------------------------------------------
//static void printEdge(const Dart& dart, ostream& ofile) {
//
// Dart d0 = dart;
// d0.alpha0();
//
// ofile << dart.x() << " " << dart.y() << endl;
// ofile << d0.x() << " " << d0.y() << endl;
//}
//--------------------------------------------------------------------------
bool Triangulation::checkDelaunay() const {
// ???? outputs !!!!
// ofstream os("qweND.dat");
const list<EdgePtr>& leadingEdges = getLeadingEdges();
list<EdgePtr>::const_iterator it;
bool ok = true;
int noNotDelaunay = 0;
for (it = leadingEdges.begin(); it != leadingEdges.end(); ++it) {
EdgePtr edge = *it;
for (int i = 0; i < 3; ++i) {
EdgePtr twinedge = edge->getTwinEdge();
// only one of the half-edges
if (!twinedge || (size_t)edge.get() > (size_t)twinedge.get()) {
Dart dart(edge);
if (ttl::swapTestDelaunay<TTLtraits>(dart)) {
noNotDelaunay++;
//printEdge(dart,os); os << "\n";
ok = false;
//cout << "............. not Delaunay .... " << endl;
}
}
edge = edge->getNextEdgeInFace();
}
}
#ifdef DEBUG_HE
cout << "!!! Triangulation is NOT Delaunay: " << noNotDelaunay << " edges\n" << endl;
#endif
return ok;
}
//--------------------------------------------------------------------------------------------------
void Triangulation::optimizeDelaunay() {
// This function is also present in ttl where it is implemented
// generically.
// The implementation below is tailored for the half-edge data structure,
// and is thus more efficient
// Collect all interior edges (one half edge for each arc)
bool skip_boundary_edges = true;
list<EdgePtr>* elist = getEdges(skip_boundary_edges);
// Assumes that elist has only one half-edge for each arc.
bool cycling_check = true;
bool optimal = false;
list<EdgePtr>::const_iterator it;
while(!optimal) {
optimal = true;
for (it = elist->begin(); it != elist->end(); ++it) {
EdgePtr edge = *it;
Dart dart(edge);
// Constrained edges should not be swapped
if (!edge->isConstrained() && ttl::swapTestDelaunay<TTLtraits>(dart, cycling_check)) {
optimal = false;
swapEdge(edge);
}
}
}
delete elist;
}
//--------------------------------------------------------------------------------------------------
EdgePtr Triangulation::getInteriorNode() const {
const list<EdgePtr>& leadingEdges = getLeadingEdges();
list<EdgePtr>::const_iterator it;
for (it = leadingEdges.begin(); it != leadingEdges.end(); ++it) {
EdgePtr edge = *it;
// multiple checks, but only until found
for (int i = 0; i < 3; ++i) {
if (edge->getTwinEdge()) {
if (!ttl::isBoundaryNode(Dart(edge)))
return edge;
}
edge = edge->getNextEdgeInFace();
}
}
return EdgePtr(); // no boundary nodes
}
//--------------------------------------------------------------------------------------------------
static EdgePtr getBoundaryEdgeInTriangle(const EdgePtr& e) {
EdgePtr edge = e;
if (ttl::isBoundaryEdge(Dart(edge)))
return edge;
edge = edge->getNextEdgeInFace();
if (ttl::isBoundaryEdge(Dart(edge)))
return edge;
edge = edge->getNextEdgeInFace();
if (ttl::isBoundaryEdge(Dart(edge)))
return edge;
return EdgePtr();
}
//--------------------------------------------------------------------------------------------------
EdgePtr Triangulation::getBoundaryEdge() const {
// Get an arbitrary (CCW) boundary edge
// If the triangulation is closed, NULL is returned
const list<EdgePtr>& leadingEdges = getLeadingEdges();
list<EdgePtr>::const_iterator it;
EdgePtr edge;
for (it = leadingEdges.begin(); it != leadingEdges.end(); ++it) {
edge = getBoundaryEdgeInTriangle(*it);
if (edge)
return edge;
}
return EdgePtr();
}
//--------------------------------------------------------------------------------------------------
void Triangulation::printEdges(ofstream& os) const {
// Print source node and target node for each edge face by face,
// but only one of the half-edges.
const list<EdgePtr>& leadingEdges = getLeadingEdges();
list<EdgePtr>::const_iterator it;
for (it = leadingEdges.begin(); it != leadingEdges.end(); ++it) {
EdgePtr edge = *it;
for (int i = 0; i < 3; ++i) {
EdgePtr twinedge = edge->getTwinEdge();
// Print only one edge (the highest value of the pointer)
if (!twinedge || (size_t)edge.get() > (size_t)twinedge.get()) {
// Print source node and target node
NodePtr node = edge->getSourceNode();
os << node->GetX() << " " << node->GetY() << endl;
node = edge->getTargetNode();
os << node->GetX() << " " << node->GetY() << endl;
os << '\n'; // blank line
}
edge = edge->getNextEdgeInFace();
}
}
}